1 // Copyright 2013 The Chromium Authors. 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 "ash/ash_constants.h"
6 #include "ash/frame/custom_frame_view_ash.h"
8 #include "ash/test/ash_test_base.h"
9 #include "ash/test/cursor_manager_test_api.h"
10 #include "ash/wm/resize_shadow.h"
11 #include "ash/wm/resize_shadow_controller.h"
12 #include "ash/wm/window_state.h"
13 #include "base/bind.h"
14 #include "ui/aura/window_event_dispatcher.h"
15 #include "ui/base/cursor/cursor.h"
16 #include "ui/base/hit_test.h"
17 #include "ui/events/test/event_generator.h"
18 #include "ui/views/widget/widget.h"
19 #include "ui/views/widget/widget_delegate.h"
26 // views::WidgetDelegate which uses ash::CustomFrameViewAsh.
27 class TestWidgetDelegate
: public views::WidgetDelegateView
{
29 TestWidgetDelegate() {}
30 ~TestWidgetDelegate() override
{}
32 // views::WidgetDelegateView overrides:
33 bool CanResize() const override
{ return true; }
34 bool CanMaximize() const override
{ return true; }
35 bool CanMinimize() const override
{ return true; }
36 views::NonClientFrameView
* CreateNonClientFrameView(
37 views::Widget
* widget
) override
{
38 return new ash::CustomFrameViewAsh(widget
);
42 DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate
);
47 // The test tests that the mouse cursor is changed and that the resize shadows
48 // are shown when the mouse is hovered over the window edge.
49 class ResizeShadowAndCursorTest
: public AshTestBase
{
51 ResizeShadowAndCursorTest() {}
52 ~ResizeShadowAndCursorTest() override
{}
54 // AshTestBase override:
55 void SetUp() override
{
58 views::Widget
* widget(views::Widget::CreateWindowWithContextAndBounds(
59 new TestWidgetDelegate(), CurrentContext(), gfx::Rect(0, 0, 200, 100)));
61 window_
= widget
->GetNativeView();
63 // Add a child window to |window_| in order to properly test that the resize
64 // handles and the resize shadows are shown when the mouse is
65 // ash::kResizeInsideBoundsSize inside of |window_|'s edges.
66 aura::Window
* child
= CreateTestWindowInShell(
67 SK_ColorWHITE
, 0, gfx::Rect(0, 10, 200, 90));
68 window_
->AddChild(child
);
71 // Returns the hit test code if there is a resize shadow. Returns HTNOWHERE if
72 // there is no resize shadow.
73 int ResizeShadowHitTest() const {
74 ash::ResizeShadow
* resize_shadow
= ash::Shell::GetInstance()
75 ->resize_shadow_controller()
76 ->GetShadowForWindowForTest(window_
);
77 return resize_shadow
? resize_shadow
->GetLastHitTestForTest() : HTNOWHERE
;
80 // Returns true if there is a resize shadow.
81 bool HasResizeShadow() const {
82 return ResizeShadowHitTest() != HTNOWHERE
;
85 // Returns the current cursor type.
86 int GetCurrentCursorType() const {
87 CursorManagerTestApi
test_api(ash::Shell::GetInstance()->cursor_manager());
88 return test_api
.GetCurrentCursor().native_type();
91 // Called for each step of a scroll sequence initiated at the bottom right
92 // corner of |window_|. Tests whether the resize shadow is shown.
93 void ProcessBottomRightResizeGesture(ui::EventType type
,
94 const gfx::Vector2dF
& delta
) {
95 if (type
== ui::ET_GESTURE_SCROLL_END
) {
96 // After gesture scroll ends, there should be no resize shadow.
97 EXPECT_FALSE(HasResizeShadow());
99 EXPECT_EQ(HTBOTTOMRIGHT
, ResizeShadowHitTest());
103 aura::Window
* window() {
108 aura::Window
* window_
;
110 DISALLOW_COPY_AND_ASSIGN(ResizeShadowAndCursorTest
);
113 // Test whether the resize shadows are visible and the cursor type based on the
115 TEST_F(ResizeShadowAndCursorTest
, MouseHover
) {
116 ui::test::EventGenerator
generator(Shell::GetPrimaryRootWindow());
117 ASSERT_TRUE(ash::wm::GetWindowState(window())->IsNormalStateType());
119 generator
.MoveMouseTo(50, 50);
120 EXPECT_FALSE(HasResizeShadow());
121 EXPECT_EQ(ui::kCursorNull
, GetCurrentCursorType());
123 generator
.MoveMouseTo(gfx::Point(50, 0));
124 EXPECT_EQ(HTTOP
, ResizeShadowHitTest());
125 EXPECT_EQ(ui::kCursorNorthResize
, GetCurrentCursorType());
127 generator
.MoveMouseTo(50, 50);
128 EXPECT_FALSE(HasResizeShadow());
129 EXPECT_EQ(ui::kCursorNull
, GetCurrentCursorType());
131 generator
.MoveMouseTo(200, 100);
132 EXPECT_EQ(HTBOTTOMRIGHT
, ResizeShadowHitTest());
133 EXPECT_EQ(ui::kCursorSouthEastResize
, GetCurrentCursorType());
135 generator
.MoveMouseTo(50, 100);
136 EXPECT_EQ(HTBOTTOM
, ResizeShadowHitTest());
137 EXPECT_EQ(ui::kCursorSouthResize
, GetCurrentCursorType());
139 generator
.MoveMouseTo(50, 100 + ash::kResizeOutsideBoundsSize
- 1);
140 EXPECT_EQ(HTBOTTOM
, ResizeShadowHitTest());
141 EXPECT_EQ(ui::kCursorSouthResize
, GetCurrentCursorType());
143 generator
.MoveMouseTo(50, 100 + ash::kResizeOutsideBoundsSize
+ 10);
144 EXPECT_FALSE(HasResizeShadow());
145 EXPECT_EQ(ui::kCursorNull
, GetCurrentCursorType());
147 generator
.MoveMouseTo(50, 100 - ash::kResizeInsideBoundsSize
);
148 EXPECT_EQ(HTBOTTOM
, ResizeShadowHitTest());
149 EXPECT_EQ(ui::kCursorSouthResize
, GetCurrentCursorType());
151 generator
.MoveMouseTo(50, 100 - ash::kResizeInsideBoundsSize
- 10);
152 EXPECT_FALSE(HasResizeShadow());
153 EXPECT_EQ(ui::kCursorNull
, GetCurrentCursorType());
156 // Test that the resize shadows stay visible and that the cursor stays the same
157 // as long as a user is resizing a window.
158 TEST_F(ResizeShadowAndCursorTest
, MouseDrag
) {
159 ui::test::EventGenerator
generator(Shell::GetPrimaryRootWindow());
160 ASSERT_TRUE(ash::wm::GetWindowState(window())->IsNormalStateType());
161 gfx::Size
initial_size(window()->bounds().size());
163 generator
.MoveMouseTo(200, 50);
164 generator
.PressLeftButton();
165 EXPECT_EQ(HTRIGHT
, ResizeShadowHitTest());
166 EXPECT_EQ(ui::kCursorEastResize
, GetCurrentCursorType());
168 generator
.MoveMouseTo(210, 50);
169 EXPECT_EQ(HTRIGHT
, ResizeShadowHitTest());
170 EXPECT_EQ(ui::kCursorEastResize
, GetCurrentCursorType());
172 generator
.ReleaseLeftButton();
173 EXPECT_EQ(HTRIGHT
, ResizeShadowHitTest());
174 EXPECT_EQ(ui::kCursorEastResize
, GetCurrentCursorType());
176 gfx::Size
new_size(window()->bounds().size());
177 EXPECT_NE(new_size
.ToString(), initial_size
.ToString());
180 // Test that the resize shadows stay visible while resizing a window via touch.
181 TEST_F(ResizeShadowAndCursorTest
, Touch
) {
182 ASSERT_TRUE(ash::wm::GetWindowState(window())->IsNormalStateType());
183 ui::test::EventGenerator
generator(Shell::GetPrimaryRootWindow());
185 int start_x
= 200 + ash::kResizeOutsideBoundsSize
- 1;
186 int start_y
= 100 + ash::kResizeOutsideBoundsSize
- 1;
187 generator
.GestureScrollSequenceWithCallback(
188 gfx::Point(start_x
, start_y
),
189 gfx::Point(start_x
+ 50, start_y
+ 50),
190 base::TimeDelta::FromMilliseconds(200),
192 base::Bind(&ResizeShadowAndCursorTest::ProcessBottomRightResizeGesture
,
193 base::Unretained(this)));
196 // Test that the resize shadows are not visible and that the default cursor is
197 // used when the window is maximized.
198 TEST_F(ResizeShadowAndCursorTest
, MaximizeRestore
) {
199 ui::test::EventGenerator
generator(Shell::GetPrimaryRootWindow());
200 ASSERT_TRUE(ash::wm::GetWindowState(window())->IsNormalStateType());
202 generator
.MoveMouseTo(200, 50);
203 EXPECT_EQ(HTRIGHT
, ResizeShadowHitTest());
204 EXPECT_EQ(ui::kCursorEastResize
, GetCurrentCursorType());
205 generator
.MoveMouseTo(200 - ash::kResizeInsideBoundsSize
, 50);
206 EXPECT_EQ(HTRIGHT
, ResizeShadowHitTest());
207 EXPECT_EQ(ui::kCursorEastResize
, GetCurrentCursorType());
209 ash::wm::GetWindowState(window())->Maximize();
210 gfx::Rect
bounds(window()->GetBoundsInRootWindow());
211 gfx::Point
right_center(bounds
.right() - 1,
212 (bounds
.y() + bounds
.bottom()) / 2);
213 generator
.MoveMouseTo(right_center
);
214 EXPECT_FALSE(HasResizeShadow());
215 EXPECT_EQ(ui::kCursorNull
, GetCurrentCursorType());
217 ash::wm::GetWindowState(window())->Restore();
218 generator
.MoveMouseTo(200, 50);
219 EXPECT_EQ(HTRIGHT
, ResizeShadowHitTest());
220 EXPECT_EQ(ui::kCursorEastResize
, GetCurrentCursorType());
221 generator
.MoveMouseTo(200 - ash::kResizeInsideBoundsSize
, 50);
222 EXPECT_EQ(HTRIGHT
, ResizeShadowHitTest());
223 EXPECT_EQ(ui::kCursorEastResize
, GetCurrentCursorType());