1 // Copyright (c) 2012 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/wm/workspace/multi_window_resize_controller.h"
7 #include "ash/ash_constants.h"
8 #include "ash/frame/custom_frame_view_ash.h"
10 #include "ash/test/ash_test_base.h"
11 #include "ash/test/shell_test_api.h"
12 #include "ash/wm/window_util.h"
13 #include "ash/wm/workspace/workspace_event_handler_test_helper.h"
14 #include "ash/wm/workspace_controller.h"
15 #include "ash/wm/workspace_controller_test_helper.h"
16 #include "ui/aura/test/test_window_delegate.h"
17 #include "ui/aura/window.h"
18 #include "ui/base/hit_test.h"
19 #include "ui/events/test/event_generator.h"
20 #include "ui/gfx/screen.h"
21 #include "ui/views/widget/widget.h"
22 #include "ui/views/widget/widget_delegate.h"
28 // WidgetDelegate for a resizable widget which creates a NonClientFrameView
29 // which is actually used in Ash.
30 class TestWidgetDelegate
: public views::WidgetDelegateView
{
32 TestWidgetDelegate() {}
33 ~TestWidgetDelegate() override
{}
35 // views::WidgetDelegateView:
36 bool CanResize() const override
{
40 views::NonClientFrameView
* CreateNonClientFrameView(
41 views::Widget
* widget
) override
{
42 return new CustomFrameViewAsh(widget
);
46 DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate
);
51 class MultiWindowResizeControllerTest
: public test::AshTestBase
{
53 MultiWindowResizeControllerTest() : resize_controller_(NULL
) {}
54 ~MultiWindowResizeControllerTest() override
{}
56 void SetUp() override
{
57 test::AshTestBase::SetUp();
58 WorkspaceController
* wc
=
59 test::ShellTestApi(Shell::GetInstance()).workspace_controller();
60 WorkspaceEventHandler
* event_handler
=
61 WorkspaceControllerTestHelper(wc
).GetEventHandler();
62 resize_controller_
= WorkspaceEventHandlerTestHelper(event_handler
).
67 aura::Window
* CreateTestWindow(aura::WindowDelegate
* delegate
,
68 const gfx::Rect
& bounds
) {
69 aura::Window
* window
= new aura::Window(delegate
);
70 window
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
71 window
->Init(aura::WINDOW_LAYER_TEXTURED
);
72 ParentWindowInPrimaryRootWindow(window
);
73 window
->SetBounds(bounds
);
79 resize_controller_
->ShowNow();
83 return resize_controller_
->IsShowing();
86 bool HasPendingShow() {
87 return resize_controller_
->show_timer_
.IsRunning();
91 resize_controller_
->Hide();
94 bool HasTarget(aura::Window
* window
) {
95 if (!resize_controller_
->windows_
.is_valid())
97 if ((resize_controller_
->windows_
.window1
== window
||
98 resize_controller_
->windows_
.window2
== window
))
101 i
< resize_controller_
->windows_
.other_windows
.size(); ++i
) {
102 if (resize_controller_
->windows_
.other_windows
[i
] == window
)
108 bool IsOverWindows(const gfx::Point
& loc
) {
109 return resize_controller_
->IsOverWindows(loc
);
112 views::Widget
* resize_widget() {
113 return resize_controller_
->resize_widget_
.get();
116 MultiWindowResizeController
* resize_controller_
;
119 DISALLOW_COPY_AND_ASSIGN(MultiWindowResizeControllerTest
);
122 // Assertions around moving mouse over 2 windows.
123 TEST_F(MultiWindowResizeControllerTest
, BasicTests
) {
124 aura::test::TestWindowDelegate delegate1
;
125 scoped_ptr
<aura::Window
> w1(
126 CreateTestWindow(&delegate1
, gfx::Rect(0, 0, 100, 100)));
127 delegate1
.set_window_component(HTRIGHT
);
128 aura::test::TestWindowDelegate delegate2
;
129 scoped_ptr
<aura::Window
> w2(
130 CreateTestWindow(&delegate2
, gfx::Rect(100, 0, 100, 100)));
131 delegate2
.set_window_component(HTRIGHT
);
132 ui::test::EventGenerator
generator(w1
->GetRootWindow());
133 generator
.MoveMouseTo(w1
->bounds().CenterPoint());
134 EXPECT_TRUE(HasPendingShow());
135 EXPECT_TRUE(IsShowing());
139 EXPECT_FALSE(HasPendingShow());
140 EXPECT_TRUE(IsShowing());
142 EXPECT_FALSE(IsOverWindows(gfx::Point(200, 200)));
144 // Have to explicitly invoke this as MouseWatcher listens for native events.
145 resize_controller_
->MouseMovedOutOfHost();
146 EXPECT_FALSE(HasPendingShow());
147 EXPECT_FALSE(IsShowing());
150 // Test the behavior of IsOverWindows().
151 TEST_F(MultiWindowResizeControllerTest
, IsOverWindows
) {
152 // Create the following layout:
153 // __________________
157 // |________|________|
158 scoped_ptr
<views::Widget
> w1(new views::Widget
);
159 views::Widget::InitParams params1
;
160 params1
.delegate
= new TestWidgetDelegate
;
161 params1
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
162 params1
.bounds
= gfx::Rect(100, 200);
163 params1
.context
= CurrentContext();
167 scoped_ptr
<views::Widget
> w2(new views::Widget
);
168 views::Widget::InitParams params2
;
169 params2
.delegate
= new TestWidgetDelegate
;
170 params2
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
171 params2
.bounds
= gfx::Rect(100, 0, 100, 100);
172 params2
.context
= CurrentContext();
176 scoped_ptr
<views::Widget
> w3(new views::Widget
);
177 views::Widget::InitParams params3
;
178 params3
.delegate
= new TestWidgetDelegate
;
179 params3
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
180 params3
.bounds
= gfx::Rect(100, 100, 100, 100);
181 params3
.context
= CurrentContext();
185 ui::test::EventGenerator
& generator
= GetEventGenerator();
186 generator
.MoveMouseTo(gfx::Point(100, 150));
187 EXPECT_TRUE(HasPendingShow());
188 EXPECT_TRUE(IsShowing());
190 EXPECT_TRUE(IsShowing());
192 // Check that the multi-window resize handle does not hide while the mouse is
193 // over a window's resize area. A window's resize area extends outside the
195 EXPECT_TRUE(w3
->IsActive());
196 ASSERT_LT(kResizeInsideBoundsSize
, kResizeOutsideBoundsSize
);
198 EXPECT_TRUE(IsOverWindows(gfx::Point(100, 150)));
199 EXPECT_TRUE(IsOverWindows(gfx::Point(100 - kResizeOutsideBoundsSize
, 150)));
201 IsOverWindows(gfx::Point(100 - kResizeOutsideBoundsSize
- 1, 150)));
203 IsOverWindows(gfx::Point(100 + kResizeInsideBoundsSize
- 1, 150)));
204 EXPECT_FALSE(IsOverWindows(gfx::Point(100 + kResizeInsideBoundsSize
, 150)));
206 IsOverWindows(gfx::Point(100 + kResizeOutsideBoundsSize
- 1, 150)));
209 EXPECT_TRUE(IsOverWindows(gfx::Point(100, 150)));
210 EXPECT_TRUE(IsOverWindows(gfx::Point(100 - kResizeInsideBoundsSize
, 150)));
212 IsOverWindows(gfx::Point(100 - kResizeInsideBoundsSize
- 1, 150)));
213 EXPECT_FALSE(IsOverWindows(gfx::Point(100 - kResizeOutsideBoundsSize
, 150)));
215 IsOverWindows(gfx::Point(100 + kResizeOutsideBoundsSize
- 1, 150)));
216 EXPECT_FALSE(IsOverWindows(gfx::Point(100 + kResizeOutsideBoundsSize
, 150)));
218 // Check that the multi-window resize handles eventually hide if the mouse
219 // moves between |w1| and |w2|.
220 EXPECT_FALSE(IsOverWindows(gfx::Point(100, 50)));
223 // Makes sure deleting a window hides.
224 TEST_F(MultiWindowResizeControllerTest
, DeleteWindow
) {
225 aura::test::TestWindowDelegate delegate1
;
226 scoped_ptr
<aura::Window
> w1(
227 CreateTestWindow(&delegate1
, gfx::Rect(0, 0, 100, 100)));
228 delegate1
.set_window_component(HTRIGHT
);
229 aura::test::TestWindowDelegate delegate2
;
230 scoped_ptr
<aura::Window
> w2(
231 CreateTestWindow(&delegate2
, gfx::Rect(100, 0, 100, 100)));
232 delegate2
.set_window_component(HTRIGHT
);
233 ui::test::EventGenerator
generator(w1
->GetRootWindow());
234 generator
.MoveMouseTo(w1
->bounds().CenterPoint());
235 EXPECT_TRUE(HasPendingShow());
236 EXPECT_TRUE(IsShowing());
240 EXPECT_FALSE(HasPendingShow());
241 EXPECT_TRUE(IsShowing());
243 // Move the mouse over the resize widget.
244 ASSERT_TRUE(resize_widget());
245 gfx::Rect
bounds(resize_widget()->GetWindowBoundsInScreen());
246 generator
.MoveMouseTo(bounds
.x() + 1, bounds
.y() + 1);
247 EXPECT_FALSE(HasPendingShow());
248 EXPECT_TRUE(IsShowing());
250 // Move the resize widget
251 generator
.PressLeftButton();
252 generator
.MoveMouseTo(bounds
.x() + 10, bounds
.y() + 10);
256 EXPECT_TRUE(resize_widget() == NULL
);
257 EXPECT_FALSE(HasPendingShow());
258 EXPECT_FALSE(IsShowing());
259 EXPECT_FALSE(HasTarget(w1
.get()));
263 TEST_F(MultiWindowResizeControllerTest
, Drag
) {
264 aura::test::TestWindowDelegate delegate1
;
265 scoped_ptr
<aura::Window
> w1(
266 CreateTestWindow(&delegate1
, gfx::Rect(0, 0, 100, 100)));
267 delegate1
.set_window_component(HTRIGHT
);
268 aura::test::TestWindowDelegate delegate2
;
269 scoped_ptr
<aura::Window
> w2(
270 CreateTestWindow(&delegate2
, gfx::Rect(100, 0, 100, 100)));
271 delegate2
.set_window_component(HTRIGHT
);
272 ui::test::EventGenerator
generator(w1
->GetRootWindow());
273 generator
.MoveMouseTo(w1
->bounds().CenterPoint());
274 EXPECT_TRUE(HasPendingShow());
275 EXPECT_TRUE(IsShowing());
279 EXPECT_FALSE(HasPendingShow());
280 EXPECT_TRUE(IsShowing());
282 // Move the mouse over the resize widget.
283 ASSERT_TRUE(resize_widget());
284 gfx::Rect
bounds(resize_widget()->GetWindowBoundsInScreen());
285 generator
.MoveMouseTo(bounds
.x() + 1, bounds
.y() + 1);
286 EXPECT_FALSE(HasPendingShow());
287 EXPECT_TRUE(IsShowing());
289 // Move the resize widget
290 generator
.PressLeftButton();
291 generator
.MoveMouseTo(bounds
.x() + 11, bounds
.y() + 10);
292 generator
.ReleaseLeftButton();
294 EXPECT_TRUE(resize_widget());
295 EXPECT_FALSE(HasPendingShow());
296 EXPECT_TRUE(IsShowing());
297 EXPECT_EQ("0,0 110x100", w1
->bounds().ToString());
298 EXPECT_EQ("110,0 100x100", w2
->bounds().ToString());
301 // Makes sure three windows are picked up.
302 TEST_F(MultiWindowResizeControllerTest
, Three
) {
303 aura::test::TestWindowDelegate delegate1
;
304 scoped_ptr
<aura::Window
> w1(
305 CreateTestWindow(&delegate1
, gfx::Rect(0, 0, 100, 100)));
306 delegate1
.set_window_component(HTRIGHT
);
307 aura::test::TestWindowDelegate delegate2
;
308 scoped_ptr
<aura::Window
> w2(
309 CreateTestWindow(&delegate2
, gfx::Rect(100, 0, 100, 100)));
310 delegate2
.set_window_component(HTRIGHT
);
311 aura::test::TestWindowDelegate delegate3
;
312 scoped_ptr
<aura::Window
> w3(
313 CreateTestWindow(&delegate3
, gfx::Rect(200, 0, 100, 100)));
314 delegate3
.set_window_component(HTRIGHT
);
316 ui::test::EventGenerator
generator(w1
->GetRootWindow());
317 generator
.MoveMouseTo(w1
->bounds().CenterPoint());
318 EXPECT_TRUE(HasPendingShow());
319 EXPECT_TRUE(IsShowing());
320 EXPECT_FALSE(HasTarget(w3
.get()));
323 EXPECT_FALSE(HasPendingShow());
324 EXPECT_TRUE(IsShowing());
326 // w3 should be picked up when resize is started.
327 gfx::Rect
bounds(resize_widget()->GetWindowBoundsInScreen());
328 generator
.MoveMouseTo(bounds
.x() + 1, bounds
.y() + 1);
329 generator
.PressLeftButton();
330 generator
.MoveMouseTo(bounds
.x() + 11, bounds
.y() + 10);
332 EXPECT_TRUE(HasTarget(w3
.get()));
334 // Release the mouse. The resizer should still be visible and a subsequent
335 // press should not trigger a DCHECK.
336 generator
.ReleaseLeftButton();
337 EXPECT_TRUE(IsShowing());
338 generator
.PressLeftButton();
341 // Tests that clicking outside of the resize handle dismisses it.
342 TEST_F(MultiWindowResizeControllerTest
, ClickOutside
) {
343 aura::test::TestWindowDelegate delegate1
;
344 scoped_ptr
<aura::Window
> w1(
345 CreateTestWindow(&delegate1
, gfx::Rect(0, 0, 100, 100)));
346 delegate1
.set_window_component(HTRIGHT
);
347 aura::test::TestWindowDelegate delegate2
;
348 scoped_ptr
<aura::Window
> w2(
349 CreateTestWindow(&delegate2
, gfx::Rect(100, 0, 100, 100)));
350 delegate2
.set_window_component(HTLEFT
);
352 ui::test::EventGenerator
& generator(GetEventGenerator());
353 gfx::Point w1_center_in_screen
= w1
->GetBoundsInScreen().CenterPoint();
354 generator
.MoveMouseTo(w1_center_in_screen
);
355 EXPECT_TRUE(HasPendingShow());
356 EXPECT_TRUE(IsShowing());
358 EXPECT_TRUE(IsShowing());
360 gfx::Rect resize_widget_bounds_in_screen
=
361 resize_widget()->GetWindowBoundsInScreen();
363 // Clicking on the resize handle should not do anything.
364 generator
.MoveMouseTo(resize_widget_bounds_in_screen
.CenterPoint());
365 generator
.ClickLeftButton();
366 EXPECT_TRUE(IsShowing());
368 // Clicking outside the resize handle should immediately hide the resize
370 EXPECT_FALSE(resize_widget_bounds_in_screen
.Contains(w1_center_in_screen
));
371 generator
.MoveMouseTo(w1_center_in_screen
);
372 generator
.ClickLeftButton();
373 EXPECT_FALSE(IsShowing());