Commit 456d6e53 authored by Jennifer Apacible's avatar Jennifer Apacible Committed by Commit Bot

[Widget] Implement SetAspectRatio for Windows.

The WM_SIZING message is now handled by HWNDMessageHandler. When the
aspect ratio is set for a window on Windows OS, the handler will adjust
the final RECT bounds to adhere to the given aspect ratio.

Bug: 853276
Change-Id: I54099581012d3f59e3d0535ed1285a25bb512cd9
Reviewed-on: https://chromium-review.googlesource.com/1136226
Commit-Queue: apacible <apacible@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Cr-Commit-Position: refs/heads/master@{#580626}
parent 844900c4
......@@ -240,7 +240,6 @@ jumbo_component("views") {
"widget/root_view.h",
"widget/root_view_targeter.h",
"widget/tooltip_manager.h",
"widget/util_mac.h",
"widget/widget.h",
"widget/widget_aura_utils.h",
"widget/widget_delegate.h",
......@@ -260,6 +259,7 @@ jumbo_component("views") {
"window/window_button_order_provider.h",
"window/window_resources.h",
"window/window_shape.h",
"window/window_resize_utils.h",
"word_lookup_client.h",
]
......@@ -440,6 +440,7 @@ jumbo_component("views") {
"window/native_frame_view.cc",
"window/non_client_view.cc",
"window/window_button_order_provider.cc",
"window/window_resize_utils.cc",
"window/window_shape.cc",
]
......@@ -1007,6 +1008,7 @@ source_set("views_unittests_sources") {
"window/dialog_client_view_unittest.cc",
"window/dialog_delegate_unittest.cc",
"window/non_client_view_unittest.cc",
"window/window_resize_utils_unittest.cc",
]
configs += [ "//build/config:precompiled_headers" ]
......
......@@ -450,7 +450,9 @@ void DesktopWindowTreeHostWin::SetOpacity(float opacity) {
}
void DesktopWindowTreeHostWin::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
// TODO(apacible): Implement for Windows. https://crbug.com/853276.
DCHECK(!aspect_ratio.IsEmpty());
message_handler_->SetAspectRatio(aspect_ratio.width() /
aspect_ratio.height());
}
void DesktopWindowTreeHostWin::SetWindowIcons(
......
......@@ -21,4 +21,5 @@ include_rules = [
"+ui/views/views_export.h",
"+ui/views/widget/widget_hwnd_utils.h",
"+ui/views/win",
"+ui/views/window/window_resize_utils.h",
]
......@@ -245,6 +245,31 @@ bool IsHitTestOnResizeHandle(LRESULT hittest) {
hittest == HTBOTTOMLEFT || hittest == HTBOTTOMRIGHT;
}
// Convert |param| to the HitTest used in WindowResizeUtils.
HitTest GetWindowResizeHitTest(UINT param) {
switch (param) {
case WMSZ_BOTTOM:
return HitTest::kBottom;
case WMSZ_TOP:
return HitTest::kTop;
case WMSZ_LEFT:
return HitTest::kLeft;
case WMSZ_RIGHT:
return HitTest::kRight;
case WMSZ_TOPLEFT:
return HitTest::kTopLeft;
case WMSZ_TOPRIGHT:
return HitTest::kTopRight;
case WMSZ_BOTTOMLEFT:
return HitTest::kBottomLeft;
case WMSZ_BOTTOMRIGHT:
return HitTest::kBottomRight;
default:
NOTREACHED();
return HitTest::kBottomRight;
}
}
const int kTouchDownContextResetTimeout = 500;
// Windows does not flag synthesized mouse messages from touch or pen in all
......@@ -868,6 +893,23 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen) {
PerformDwmTransition();
}
void HWNDMessageHandler::SetAspectRatio(float aspect_ratio) {
// If the aspect ratio is not in the valid range, do nothing.
DCHECK_GT(aspect_ratio, 0.0f);
aspect_ratio_ = aspect_ratio;
// When the aspect ratio is set, size the window to adhere to it. This keeps
// the same origin point as the original window.
RECT window_rect;
if (GetWindowRect(hwnd(), &window_rect)) {
gfx::Rect rect(window_rect);
SizeRectToAspectRatio(WMSZ_BOTTOMRIGHT, &rect);
SetBoundsInternal(rect, false);
}
}
void HWNDMessageHandler::SizeConstraintsChanged() {
LONG style = GetWindowLong(hwnd(), GWL_STYLE);
// Ignore if this is not a standard window.
......@@ -2337,6 +2379,19 @@ void HWNDMessageHandler::OnSize(UINT param, const gfx::Size& size) {
ResetWindowRegion(false, true);
}
void HWNDMessageHandler::OnSizing(UINT param, RECT* rect) {
// If the aspect ratio was not specified for the window, do nothing.
if (!aspect_ratio_.has_value())
return;
gfx::Rect window_rect(*rect);
SizeRectToAspectRatio(param, &window_rect);
// TODO(apacible): Account for window borders as part of the aspect ratio.
// https://crbug/869487.
*rect = window_rect.ToRECT();
}
void HWNDMessageHandler::OnSysCommand(UINT notification_code,
const gfx::Point& point) {
// Windows uses the 4 lower order bits of |notification_code| for type-
......@@ -3189,4 +3244,18 @@ void HWNDMessageHandler::DestroyAXSystemCaret() {
ax_system_caret_ = nullptr;
}
void HWNDMessageHandler::SizeRectToAspectRatio(UINT param,
gfx::Rect* window_rect) {
gfx::Size min_window_size;
gfx::Size max_window_size;
delegate_->GetMinMaxSize(&min_window_size, &max_window_size);
WindowResizeUtils::SizeMinMaxToAspectRatio(
aspect_ratio_.value(), &min_window_size, &max_window_size);
min_window_size = delegate_->DIPToScreenSize(min_window_size);
max_window_size = delegate_->DIPToScreenSize(max_window_size);
WindowResizeUtils::SizeRectToAspectRatio(
GetWindowResizeHitTest(param), aspect_ratio_.value(), min_window_size,
max_window_size, window_rect);
}
} // namespace views
......@@ -31,6 +31,7 @@
#include "ui/gfx/win/window_impl.h"
#include "ui/views/views_export.h"
#include "ui/views/win/pen_event_processor.h"
#include "ui/views/window/window_resize_utils.h"
namespace gfx {
class ImageSkia;
......@@ -161,6 +162,9 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
void SetFullscreen(bool fullscreen);
// Updates the aspect ratio of the window.
void SetAspectRatio(float aspect_ratio);
// Updates the window style to reflect whether it can be resized or maximized.
void SizeConstraintsChanged();
......@@ -408,6 +412,7 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
CR_MSG_WM_SETTEXT(OnSetText)
CR_MSG_WM_SETTINGCHANGE(OnSettingChange)
CR_MSG_WM_SIZE(OnSize)
CR_MSG_WM_SIZING(OnSizing)
CR_MSG_WM_SYSCOMMAND(OnSysCommand)
CR_MSG_WM_THEMECHANGED(OnThemeChanged)
CR_MSG_WM_TIMECHANGE(OnTimeChange)
......@@ -467,6 +472,7 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
LRESULT OnSetText(const wchar_t* text);
void OnSettingChange(UINT flags, const wchar_t* section);
void OnSize(UINT param, const gfx::Size& size);
void OnSizing(UINT param, RECT* rect);
void OnSysCommand(UINT notification_code, const gfx::Point& point);
void OnThemeChanged();
void OnTimeChange();
......@@ -560,6 +566,10 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
// if they request its location.
void DestroyAXSystemCaret();
// Updates |rect| to adhere to the |aspect_ratio| of the window. |param|
// refers to the edge of the window being sized.
void SizeRectToAspectRatio(UINT param, gfx::Rect* rect);
HWNDMessageHandlerDelegate* delegate_;
std::unique_ptr<FullscreenHandler> fullscreen_handler_;
......@@ -586,6 +596,10 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl,
// The icon created from the bitmap image of the app icon.
base::win::ScopedHICON app_icon_;
// The aspect ratio for the window. This is only used for sizing operations
// for the non-client area.
base::Optional<float> aspect_ratio_;
// The current DPI.
int dpi_;
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/window/window_resize_utils.h"
#include <algorithm>
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace views {
// static
void WindowResizeUtils::SizeMinMaxToAspectRatio(float aspect_ratio,
gfx::Size* min_window_size,
gfx::Size* max_window_size) {
DCHECK_GT(aspect_ratio, 0.0f);
// Calculate the height using the min-width and aspect ratio.
int min_height = min_window_size->width() / aspect_ratio;
if (min_height < min_window_size->height()) {
// The supplied width is too small to honor the min size, so use the height
// to determine the minimum width.
min_window_size->set_width(min_window_size->height() * aspect_ratio);
} else {
min_window_size->set_height(min_height);
}
// Calculate the height using the max-width and aspect ratio.
int max_height = max_window_size->width() / aspect_ratio;
if (max_height > max_window_size->height()) {
// The supplied width is too large to honor the max size, so use the height
// to determine the maximum width.
max_window_size->set_width(max_window_size->height() * aspect_ratio);
} else {
max_window_size->set_height(max_height);
}
DCHECK_GE(max_window_size->width(), min_window_size->width());
DCHECK_GE(max_window_size->height(), min_window_size->height());
}
// static
void WindowResizeUtils::SizeRectToAspectRatio(HitTest param,
float aspect_ratio,
const gfx::Size& min_window_size,
const gfx::Size& max_window_size,
gfx::Rect* rect) {
DCHECK_GT(aspect_ratio, 0.0f);
DCHECK_GE(max_window_size.width(), min_window_size.width());
DCHECK_GE(max_window_size.height(), min_window_size.height());
float rect_width = 0.0;
float rect_height = 0.0;
if (param == HitTest::kLeft || param == HitTest::kRight ||
param == HitTest::kTopLeft ||
param == HitTest::kBottomLeft) { /* horizontal axis to pivot */
rect_width = std::min(max_window_size.width(),
std::max(rect->width(), min_window_size.width()));
rect_height = rect_width / aspect_ratio;
} else { /* vertical axis to pivot */
rect_height = std::min(max_window_size.height(),
std::max(rect->height(), min_window_size.height()));
rect_width = rect_height * aspect_ratio;
}
// |rect| bounds before sizing to aspect ratio.
int left = rect->x();
int top = rect->y();
int right = rect->right();
int bottom = rect->bottom();
switch (param) {
case HitTest::kRight:
case HitTest::kBottom:
right = rect_width + left;
bottom = top + rect_height;
break;
case HitTest::kTop:
right = rect_width + left;
top = bottom - rect_height;
break;
case HitTest::kLeft:
case HitTest::kTopLeft:
left = right - rect_width;
top = bottom - rect_height;
break;
case HitTest::kTopRight:
right = left + rect_width;
top = bottom - rect_height;
break;
case HitTest::kBottomLeft:
left = right - rect_width;
bottom = top + rect_height;
break;
case HitTest::kBottomRight:
right = left + rect_width;
bottom = top + rect_height;
break;
}
rect->SetByBounds(left, top, right, bottom);
}
} // namespace views
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_VIEWS_WINDOW_WINDOW_RESIZE_UTILS_H_
#define UI_VIEWS_WINDOW_WINDOW_RESIZE_UTILS_H_
#include "base/macros.h"
#include "ui/views/views_export.h"
namespace gfx {
class Size;
class Rect;
} // namespace gfx
namespace views {
enum class HitTest {
kBottom,
kBottomLeft,
kBottomRight,
kLeft,
kRight,
kTop,
kTopLeft,
kTopRight
};
class VIEWS_EXPORT WindowResizeUtils {
public:
// Force the min and max window sizes to adhere to the aspect ratio.
// |aspect_ratio| must be valid and is found using width / height.
static void SizeMinMaxToAspectRatio(float aspect_ratio,
gfx::Size* min_window_size,
gfx::Size* max_window_size);
// Updates |rect| to adhere to the |aspect_ratio| of the window, if it has
// been set. |param| refers to the edge of the window being sized.
// |min_window_size| and |max_window_size| are expected to adhere to the
// given aspect ratio.
// |aspect_ratio| must be valid and is found using width / height.
// TODO(apacible): |max_window_size| is expected to be non-empty. Handle
// unconstrained max sizes and sizing when windows are maximized.
static void SizeRectToAspectRatio(HitTest param,
float aspect_ratio,
const gfx::Size& min_window_size,
const gfx::Size& max_window_size,
gfx::Rect* rect);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(WindowResizeUtils);
};
} // namespace views
#endif // UI_VIEWS_WINDOW_WINDOW_RESIZE_UTILS_H_
\ No newline at end of file
// CopykRight 2018 The Chromium Authors. All kRights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/window/window_resize_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace views {
namespace {
// Aspect ratio is defined by width / height.
constexpr float kAspectRatioSquare = 1.0f;
constexpr float kAspectRatioHorizontal = 2.0f;
constexpr float kAspectRatioVertical = 0.5f;
const gfx::Size kMinSizeSquare = gfx::Size(10, 10);
const gfx::Size kMaxSizeSquare = gfx::Size(50, 50);
const gfx::Size kMinSizeHorizontal = gfx::Size(20, 10);
const gfx::Size kMaxSizeHorizontal = gfx::Size(50, 25);
const gfx::Size kMinSizeVertical = gfx::Size(10, 20);
const gfx::Size kMaxSizeVertical = gfx::Size(25, 50);
} // namespace
// Tests resizing of window with a 1:1 aspect ratio. This test also tests the
// 'pivot points' when resizing, i.e. the opposite side or corner of the
// window.
TEST(WindowResizeUtilsTest, SizeToSquareAspectRatio) {
// Size from the top of the window.
// |window_rect| within the bounds of kMinSizeSquare and kMaxSizeSquare.
gfx::Rect window_rect(100, 100, 15, 15);
WindowResizeUtils::SizeRectToAspectRatio(HitTest::kTop, kAspectRatioSquare,
kMinSizeSquare, kMaxSizeSquare,
&window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 100, 15, 15));
// Size from the bottom right corner of the window.
// |window_rect| smaller than kMinSizeSquare.
window_rect.SetRect(100, 100, 5, 5);
WindowResizeUtils::SizeRectToAspectRatio(HitTest::kBottomRight,
kAspectRatioSquare, kMinSizeSquare,
kMaxSizeSquare, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMinSizeSquare.width(),
kMinSizeSquare.height()));
// Size from the top of the window.
// |window_rect| larger than kMaxSizeSquare.
window_rect.SetRect(100, 100, 100, 100);
WindowResizeUtils::SizeRectToAspectRatio(HitTest::kTop, kAspectRatioSquare,
kMinSizeSquare, kMaxSizeSquare,
&window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 150, kMaxSizeSquare.width(),
kMaxSizeSquare.height()));
// Size from the bottom of the window.
window_rect.SetRect(100, 100, 100, 100);
WindowResizeUtils::SizeRectToAspectRatio(HitTest::kBottom, kAspectRatioSquare,
kMinSizeSquare, kMaxSizeSquare,
&window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMaxSizeSquare.width(),
kMaxSizeSquare.height()));
// Size from the left of the window.
window_rect.SetRect(100, 100, 100, 100);
WindowResizeUtils::SizeRectToAspectRatio(HitTest::kLeft, kAspectRatioSquare,
kMinSizeSquare, kMaxSizeSquare,
&window_rect);
EXPECT_EQ(window_rect, gfx::Rect(150, 150, kMaxSizeSquare.width(),
kMaxSizeSquare.height()));
// Size from the right of the window.
window_rect.SetRect(100, 100, 100, 100);
WindowResizeUtils::SizeRectToAspectRatio(HitTest::kRight, kAspectRatioSquare,
kMinSizeSquare, kMaxSizeSquare,
&window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMaxSizeSquare.width(),
kMaxSizeSquare.height()));
// Size from the top left corner of the window.
window_rect.SetRect(100, 100, 100, 100);
WindowResizeUtils::SizeRectToAspectRatio(HitTest::kTopLeft,
kAspectRatioSquare, kMinSizeSquare,
kMaxSizeSquare, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(150, 150, kMaxSizeSquare.width(),
kMaxSizeSquare.height()));
// Size from the top right corner of the window.
window_rect.SetRect(100, 100, 100, 100);
WindowResizeUtils::SizeRectToAspectRatio(HitTest::kTopRight,
kAspectRatioSquare, kMinSizeSquare,
kMaxSizeSquare, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 150, kMaxSizeSquare.width(),
kMaxSizeSquare.height()));
// Size from the bottom left corner of the window.
window_rect.SetRect(100, 100, 100, 100);
WindowResizeUtils::SizeRectToAspectRatio(HitTest::kBottomLeft,
kAspectRatioSquare, kMinSizeSquare,
kMaxSizeSquare, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(150, 100, kMaxSizeSquare.width(),
kMaxSizeSquare.height()));
}
// Tests the aspect ratio of the gfx::Rect adheres to the horizontal aspect
// ratio.
TEST(WindowResizeUtilsTest, SizeToHorizontalAspectRatio) {
// |window_rect| within bounds of kMinSizeHorizontal and kMaxSizeHorizontal.
gfx::Rect window_rect(100, 100, 20, 10);
WindowResizeUtils::SizeRectToAspectRatio(
HitTest::kTop, kAspectRatioHorizontal, kMinSizeHorizontal,
kMaxSizeHorizontal, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 100, 20, 10));
// |window_rect| smaller than kMinSizeHorizontal.
window_rect.SetRect(100, 100, 5, 5);
WindowResizeUtils::SizeRectToAspectRatio(
HitTest::kBottomRight, kAspectRatioHorizontal, kMinSizeHorizontal,
kMaxSizeHorizontal, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMinSizeHorizontal.width(),
kMinSizeHorizontal.height()));
// |window_rect| greater than kMaxSizeHorizontal.
window_rect.SetRect(100, 100, 100, 100);
WindowResizeUtils::SizeRectToAspectRatio(
HitTest::kTop, kAspectRatioHorizontal, kMinSizeHorizontal,
kMaxSizeHorizontal, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 175, kMaxSizeHorizontal.width(),
kMaxSizeHorizontal.height()));
}
// Tests the aspect ratio of the gfx::Rect adheres to the vertical aspect ratio.
TEST(WindowResizeUtilsTest, SizeToVerticalAspectRatio) {
// |window_rect| within bounds of kMinSizeVertical and kMaxSizeVertical.
gfx::Rect window_rect(100, 100, 10, 20);
WindowResizeUtils::SizeRectToAspectRatio(
HitTest::kBottomRight, kAspectRatioVertical, kMinSizeVertical,
kMaxSizeVertical, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 100, 10, 20));
// |window_rect| smaller than kMinSizeVertical.
window_rect.SetRect(100, 100, 5, 5);
WindowResizeUtils::SizeRectToAspectRatio(
HitTest::kBottomRight, kAspectRatioVertical, kMinSizeVertical,
kMaxSizeVertical, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMinSizeVertical.width(),
kMinSizeVertical.height()));
// |window_rect| greater than kMaxSizeVertical.
window_rect.SetRect(100, 100, 100, 100);
WindowResizeUtils::SizeRectToAspectRatio(
HitTest::kBottomRight, kAspectRatioVertical, kMinSizeVertical,
kMaxSizeVertical, &window_rect);
EXPECT_EQ(window_rect, gfx::Rect(100, 100, kMaxSizeVertical.width(),
kMaxSizeVertical.height()));
}
} // namespace views
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment