1 // Copyright 2023 Samsung Electronics. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "wrt/src/browser/wrt_frame_view.h"
7 #include "base/files/file_util.h"
8 #include "base/task/task_traits.h"
9 #include "base/task/thread_pool.h"
10 #include "services/data_decoder/public/cpp/decode_image.h"
11 #include "ui/aura/window.h"
12 #include "ui/base/hit_test.h"
13 #include "ui/gfx/canvas.h"
14 #include "ui/gfx/image/image_skia_operations.h"
15 #include "ui/views/controls/image_view.h"
16 #include "ui/views/background.h"
17 #include "ui/views/border.h"
18 #include "ui/views/painter.h"
19 #include "ui/views/widget/widget.h"
20 #include "ui/views/widget/widget_delegate.h"
26 const int kResizeInsideBoundsSize = 5;
27 const int kResizeAreaCornerSize = 16;
29 std::string ReadFileToString(const base::FilePath& path) {
31 if (!base::ReadFileToString(path, &result)) {
32 LOG(WARNING) << "Failed reading file";
39 void DecodeImageData(data_decoder::DecodeImageCallback callback,
40 const std::string& data) {
42 std::move(callback).Run(SkBitmap());
45 data_decoder::DecodeImageIsolated(
46 base::as_bytes(base::make_span(data)),
47 data_decoder::mojom::ImageCodec::kDefault,
48 true, data_decoder::kDefaultMaxSizeInBytes, gfx::Size(),
52 void DecodeImageFile(const base::FilePath& file_path,
53 data_decoder::DecodeImageCallback callback) {
54 base::ThreadPool::PostTaskAndReplyWithResult(
56 {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
57 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
58 base::BindOnce(&ReadFileToString, file_path),
59 base::BindOnce(&DecodeImageData, std::move(callback)));
65 const char WRTFrameView::kViewClassName[] = "WRTFrameView";
67 WRTFrameView::WRTFrameView() = default;
69 WRTFrameView::~WRTFrameView() = default;
71 int WRTFrameView::ResizingBorderHitTest(const gfx::Point& point) {
72 gfx::Insets resize_border(kResizeInsideBoundsSize);
74 // to be used for resize handles.
75 bool can_ever_resize = GetWidget()->widget_delegate()
76 ? GetWidget()->widget_delegate()->CanResize()
79 // https://github.com/electron/electron/issues/611
80 // If window isn't resizable, we should always return HTNOWHERE, otherwise the
81 // hover state of DOM will not be cleared probably.
85 // Don't allow overlapping resize handles when the window is maximized or
86 // fullscreen, as it can't be resized in those states.
87 bool allow_overlapping_handles =
88 !GetWidget()->IsMaximized() && !GetWidget()->IsFullscreen();
89 return GetHTComponentForFrame(
90 point, allow_overlapping_handles ? resize_border : gfx::Insets(),
91 kResizeAreaCornerSize, kResizeAreaCornerSize, can_ever_resize);
94 gfx::Rect WRTFrameView::GetBoundsForClientView() const {
98 gfx::Rect WRTFrameView::GetWindowBoundsForClientBounds(
99 const gfx::Rect& client_bounds) const {
100 gfx::Rect window_bounds = client_bounds;
101 // Enforce minimum size (1, 1) in case that client_bounds is passed with
102 // empty size. This could occur when the frameless window is being
104 if (window_bounds.IsEmpty()) {
105 window_bounds.set_width(1);
106 window_bounds.set_height(1);
108 return window_bounds;
111 int WRTFrameView::NonClientHitTest(const gfx::Point& cursor) {
112 if (GetWidget()->IsFullscreen())
115 // Support resizing frameless window by dragging the border.
116 int frame_component = ResizingBorderHitTest(cursor);
117 if (frame_component != HTNOWHERE)
118 return frame_component;
123 void WRTFrameView::GetWindowMask(const gfx::Size& size, SkPath* window_mask) {}
125 void WRTFrameView::ResetWindowControls() {}
127 void WRTFrameView::UpdateWindowIcon() {}
129 void WRTFrameView::UpdateWindowTitle() {}
131 void WRTFrameView::SizeConstraintsChanged() {}
133 views::View* WRTFrameView::TargetForRect(views::View* root,
134 const gfx::Rect& rect) {
135 CHECK_EQ(root, this);
137 if (NonClientHitTest(rect.origin()) != HTCLIENT)
140 return NonClientFrameView::TargetForRect(root, rect);
143 gfx::Size WRTFrameView::CalculatePreferredSize() const {
144 return GetWidget()->non_client_view()
145 ->GetWindowBoundsForClientBounds(
146 gfx::Rect(GetWidget()->client_view()->GetPreferredSize()))
150 gfx::Size WRTFrameView::GetMinimumSize() const {
151 return gfx::Size(0, 0);
154 gfx::Size WRTFrameView::GetMaximumSize() const {
155 return gfx::Size(INT_MAX, INT_MAX);
158 const char* WRTFrameView::GetClassName() const {
159 return kViewClassName;
162 void WRTFrameView::SetBackgroundColor(SkColor color) {
163 SetBackground(views::CreateSolidBackground(color));
166 void WRTFrameView::SetImageBorder(const base::FilePath& file_path,
167 const gfx::Insets& insets) {
168 image_border_insets_ = insets;
169 ++image_loading_count_;
170 DecodeImageFile(file_path, base::BindOnce(&WRTFrameView::OnImageBorderDecoded,
171 base::Unretained(this)));
174 void WRTFrameView::OnImageBorderDecoded(const SkBitmap& bitmap) {
175 SetBorder(views::CreateBorderPainter(
176 views::Painter::CreateImagePainter(
177 gfx::ImageSkia::CreateFrom1xBitmap(bitmap), image_border_insets_),
182 void WRTFrameView::SetImage(const base::FilePath& file_path) {
183 image_view_ = AddChildView(std::make_unique<views::ImageView>());
184 image_view_->SetSize(size());
185 ++image_loading_count_;
186 DecodeImageFile(file_path, base::BindOnce(&WRTFrameView::OnImageDecoded,
187 base::Unretained(this)));
190 void WRTFrameView::OnImageDecoded(const SkBitmap& bitmap) {
191 gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
192 image_view_->SetImage(
193 ui::ImageModel::FromImageSkia(ResizeImage(image, image_view_->size())));
197 gfx::ImageSkia WRTFrameView::ResizeImage(const gfx::ImageSkia& image,
198 const gfx::Size& view_size) {
200 return gfx::ImageSkia();
202 const double image_width = image.width();
203 const double image_height = image.height();
204 const double view_width = view_size.width();
205 const double view_height = view_size.height();
206 const double horizontal_ratio = view_width / image_width;
207 const double vertical_ratio = view_height / image_height;
208 const double image_ratio = image_height / image_width;
209 const double view_ratio = view_height / view_width;
211 // If the image and the container view has the same orientation, e.g. both
212 // portrait, the |scale| will make the image filled the whole view with
213 // possible cropping on one direction. If they are in different orientation,
214 // the |scale| will display the image in the view without any cropping, but
215 // with empty background.
216 const double scale = (image_ratio - 1) * (view_ratio - 1) > 0
217 ? std::max(horizontal_ratio, vertical_ratio)
218 : std::min(horizontal_ratio, vertical_ratio);
219 const gfx::Size& resized = gfx::ScaleToCeiledSize(image.size(), scale);
220 return gfx::ImageSkiaOperations::CreateResizedImage(
221 image, skia::ImageOperations::RESIZE_BEST, resized);
224 void WRTFrameView::ShowWidgetWhenReady() {
225 if (!image_loading_count_) {
229 need_to_show_widget_ = true;
232 void WRTFrameView::OnImageLoaded() {
233 if (--image_loading_count_ == 0 && need_to_show_widget_) {
234 need_to_show_widget_ = false;