1 // Copyright (c) 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/wm/panels/panel_window_resizer.h"
7 #include "ash/root_window_controller.h"
8 #include "ash/shelf/shelf.h"
9 #include "ash/shelf/shelf_layout_manager.h"
10 #include "ash/shelf/shelf_model.h"
11 #include "ash/shelf/shelf_types.h"
12 #include "ash/shelf/shelf_util.h"
13 #include "ash/shelf/shelf_widget.h"
14 #include "ash/shell.h"
15 #include "ash/shell_window_ids.h"
16 #include "ash/test/ash_test_base.h"
17 #include "ash/test/cursor_manager_test_api.h"
18 #include "ash/test/shell_test_api.h"
19 #include "ash/test/test_shelf_delegate.h"
20 #include "ash/wm/drag_window_resizer.h"
21 #include "ash/wm/panels/panel_layout_manager.h"
22 #include "ash/wm/window_state.h"
23 #include "ash/wm/wm_event.h"
24 #include "base/win/windows_version.h"
25 #include "ui/aura/client/aura_constants.h"
26 #include "ui/aura/window_event_dispatcher.h"
27 #include "ui/base/hit_test.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/base/ui_base_types.h"
30 #include "ui/views/widget/widget.h"
31 #include "ui/wm/core/window_util.h"
35 class PanelWindowResizerTest
: public test::AshTestBase
{
37 PanelWindowResizerTest() {}
38 virtual ~PanelWindowResizerTest() {}
40 virtual void SetUp() OVERRIDE
{
42 UpdateDisplay("600x400");
43 test::ShellTestApi
test_api(Shell::GetInstance());
44 model_
= test_api
.shelf_model();
45 shelf_delegate_
= test::TestShelfDelegate::instance();
48 virtual void TearDown() OVERRIDE
{
49 AshTestBase::TearDown();
53 gfx::Point
CalculateDragPoint(const WindowResizer
& resizer
,
56 gfx::Point location
= resizer
.GetInitialLocation();
57 location
.set_x(location
.x() + delta_x
);
58 location
.set_y(location
.y() + delta_y
);
62 aura::Window
* CreatePanelWindow(const gfx::Point
& origin
) {
63 gfx::Rect
bounds(origin
, gfx::Size(101, 101));
64 aura::Window
* window
= CreateTestWindowInShellWithDelegateAndType(
65 NULL
, ui::wm::WINDOW_TYPE_PANEL
, 0, bounds
);
66 shelf_delegate_
->AddShelfItem(window
);
70 void DragStart(aura::Window
* window
) {
71 resizer_
.reset(CreateWindowResizer(
73 window
->bounds().origin(),
75 aura::client::WINDOW_MOVE_SOURCE_MOUSE
).release());
76 ASSERT_TRUE(resizer_
.get());
79 void DragMove(int dx
, int dy
) {
80 resizer_
->Drag(CalculateDragPoint(*resizer_
, dx
, dy
), 0);
84 resizer_
->CompleteDrag();
89 resizer_
->RevertDrag();
93 // Test dragging the panel slightly, then detaching, and then reattaching
94 // dragging out by the vector (dx, dy).
95 void DetachReattachTest(aura::Window
* window
, int dx
, int dy
) {
96 wm::WindowState
* window_state
= wm::GetWindowState(window
);
97 EXPECT_TRUE(window_state
->panel_attached());
98 aura::Window
* root_window
= window
->GetRootWindow();
99 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
101 gfx::Rect initial_bounds
= window
->GetBoundsInScreen();
103 // Drag the panel slightly. The window should still be snapped to the
105 DragMove(dx
* 5, dy
* 5);
106 EXPECT_EQ(initial_bounds
.x(), window
->GetBoundsInScreen().x());
107 EXPECT_EQ(initial_bounds
.y(), window
->GetBoundsInScreen().y());
109 // Drag further out and the window should now move to the cursor.
110 DragMove(dx
* 100, dy
* 100);
111 EXPECT_EQ(initial_bounds
.x() + dx
* 100, window
->GetBoundsInScreen().x());
112 EXPECT_EQ(initial_bounds
.y() + dy
* 100, window
->GetBoundsInScreen().y());
114 // The panel should be detached when the drag completes.
117 EXPECT_FALSE(window_state
->panel_attached());
118 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
119 EXPECT_EQ(root_window
, window
->GetRootWindow());
122 // Drag the panel down.
123 DragMove(dx
* -95, dy
* -95);
124 // Release the mouse and the panel should be reattached.
127 // The panel should be reattached and have snapped to the launcher.
128 EXPECT_TRUE(window_state
->panel_attached());
129 EXPECT_EQ(initial_bounds
.x(), window
->GetBoundsInScreen().x());
130 EXPECT_EQ(initial_bounds
.y(), window
->GetBoundsInScreen().y());
131 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
134 void TestWindowOrder(const std::vector
<aura::Window
*>& window_order
) {
135 int panel_index
= model_
->FirstPanelIndex();
136 EXPECT_EQ((int)(panel_index
+ window_order
.size()), model_
->item_count());
137 for (std::vector
<aura::Window
*>::const_iterator iter
=
138 window_order
.begin(); iter
!= window_order
.end();
139 ++iter
, ++panel_index
) {
140 ShelfID id
= GetShelfIDForWindow(*iter
);
141 EXPECT_EQ(id
, model_
->items()[panel_index
].id
);
145 // Test dragging panel window along the shelf and verify that panel icons
146 // are reordered appropriately.
147 void DragAlongShelfReorder(int dx
, int dy
) {
148 gfx::Point
origin(0, 0);
149 scoped_ptr
<aura::Window
> w1(CreatePanelWindow(origin
));
150 scoped_ptr
<aura::Window
> w2(CreatePanelWindow(origin
));
151 std::vector
<aura::Window
*> window_order_original
;
152 std::vector
<aura::Window
*> window_order_swapped
;
153 window_order_original
.push_back(w1
.get());
154 window_order_original
.push_back(w2
.get());
155 window_order_swapped
.push_back(w2
.get());
156 window_order_swapped
.push_back(w1
.get());
157 TestWindowOrder(window_order_original
);
159 // Drag window #2 to the beginning of the shelf.
161 DragMove(400 * dx
, 400 * dy
);
162 TestWindowOrder(window_order_swapped
);
165 // Expect swapped window order.
166 TestWindowOrder(window_order_swapped
);
168 // Drag window #2 back to the end.
170 DragMove(-400 * dx
, -400 * dy
);
171 TestWindowOrder(window_order_original
);
174 // Expect original order.
175 TestWindowOrder(window_order_original
);
179 scoped_ptr
<WindowResizer
> resizer_
;
180 PanelLayoutManager
* panel_layout_manager_
;
182 test::TestShelfDelegate
* shelf_delegate_
;
184 DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTest
);
187 class PanelWindowResizerTextDirectionTest
188 : public PanelWindowResizerTest
,
189 public testing::WithParamInterface
<bool> {
191 PanelWindowResizerTextDirectionTest() : is_rtl_(GetParam()) {}
192 virtual ~PanelWindowResizerTextDirectionTest() {}
194 virtual void SetUp() OVERRIDE
{
195 original_locale
= l10n_util::GetApplicationLocale(std::string());
197 base::i18n::SetICUDefaultLocale("he");
198 PanelWindowResizerTest::SetUp();
199 ASSERT_EQ(is_rtl_
, base::i18n::IsRTL());
202 virtual void TearDown() OVERRIDE
{
204 base::i18n::SetICUDefaultLocale(original_locale
);
205 PanelWindowResizerTest::TearDown();
210 std::string original_locale
;
212 DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTextDirectionTest
);
215 // PanelLayoutManager and PanelWindowResizer should work if panels have
216 // transient children of supported types.
217 class PanelWindowResizerTransientTest
218 : public PanelWindowResizerTest
,
219 public testing::WithParamInterface
<ui::wm::WindowType
> {
221 PanelWindowResizerTransientTest() : transient_window_type_(GetParam()) {}
222 virtual ~PanelWindowResizerTransientTest() {}
225 ui::wm::WindowType transient_window_type_
;
228 DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTransientTest
);
231 // Verifies a window can be dragged from the panel and detached and then
233 TEST_F(PanelWindowResizerTest
, PanelDetachReattachBottom
) {
234 if (!SupportsHostWindowResize())
237 scoped_ptr
<aura::Window
> window(
238 CreatePanelWindow(gfx::Point(0, 0)));
239 DetachReattachTest(window
.get(), 0, -1);
242 TEST_F(PanelWindowResizerTest
, PanelDetachReattachLeft
) {
243 if (!SupportsHostWindowResize())
246 ash::Shell
* shell
= ash::Shell::GetInstance();
247 shell
->SetShelfAlignment(SHELF_ALIGNMENT_LEFT
, shell
->GetPrimaryRootWindow());
248 scoped_ptr
<aura::Window
> window(
249 CreatePanelWindow(gfx::Point(0, 0)));
250 DetachReattachTest(window
.get(), 1, 0);
253 TEST_F(PanelWindowResizerTest
, PanelDetachReattachRight
) {
254 if (!SupportsHostWindowResize())
257 ash::Shell
* shell
= ash::Shell::GetInstance();
258 shell
->SetShelfAlignment(SHELF_ALIGNMENT_RIGHT
,
259 shell
->GetPrimaryRootWindow());
260 scoped_ptr
<aura::Window
> window(
261 CreatePanelWindow(gfx::Point(0, 0)));
262 DetachReattachTest(window
.get(), -1, 0);
265 TEST_F(PanelWindowResizerTest
, PanelDetachReattachTop
) {
266 if (!SupportsHostWindowResize())
269 ash::Shell
* shell
= ash::Shell::GetInstance();
270 shell
->SetShelfAlignment(SHELF_ALIGNMENT_TOP
, shell
->GetPrimaryRootWindow());
271 scoped_ptr
<aura::Window
> window(
272 CreatePanelWindow(gfx::Point(0, 0)));
273 DetachReattachTest(window
.get(), 0, 1);
276 TEST_F(PanelWindowResizerTest
, PanelDetachReattachMultipleDisplays
) {
277 if (!SupportsMultipleDisplays())
280 UpdateDisplay("600x400,600x400");
281 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
282 scoped_ptr
<aura::Window
> window(
283 CreatePanelWindow(gfx::Point(600, 0)));
284 EXPECT_EQ(root_windows
[1], window
->GetRootWindow());
285 DetachReattachTest(window
.get(), 0, -1);
288 TEST_F(PanelWindowResizerTest
, DetachThenDragAcrossDisplays
) {
289 if (!SupportsMultipleDisplays())
292 UpdateDisplay("600x400,600x400");
293 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
294 scoped_ptr
<aura::Window
> window(
295 CreatePanelWindow(gfx::Point(0, 0)));
296 gfx::Rect initial_bounds
= window
->GetBoundsInScreen();
297 EXPECT_EQ(root_windows
[0], window
->GetRootWindow());
298 DragStart(window
.get());
301 EXPECT_EQ(root_windows
[0], window
->GetRootWindow());
302 EXPECT_EQ(initial_bounds
.x(), window
->GetBoundsInScreen().x());
303 EXPECT_EQ(initial_bounds
.y() - 100, window
->GetBoundsInScreen().y());
304 EXPECT_FALSE(wm::GetWindowState(window
.get())->panel_attached());
305 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
307 DragStart(window
.get());
310 EXPECT_EQ(root_windows
[1], window
->GetRootWindow());
311 EXPECT_EQ(initial_bounds
.x() + 500, window
->GetBoundsInScreen().x());
312 EXPECT_EQ(initial_bounds
.y() - 100, window
->GetBoundsInScreen().y());
313 EXPECT_FALSE(wm::GetWindowState(window
.get())->panel_attached());
314 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
317 TEST_F(PanelWindowResizerTest
, DetachAcrossDisplays
) {
318 if (!SupportsMultipleDisplays())
321 UpdateDisplay("600x400,600x400");
322 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
323 scoped_ptr
<aura::Window
> window(
324 CreatePanelWindow(gfx::Point(0, 0)));
325 gfx::Rect initial_bounds
= window
->GetBoundsInScreen();
326 EXPECT_EQ(root_windows
[0], window
->GetRootWindow());
327 DragStart(window
.get());
330 EXPECT_EQ(root_windows
[1], window
->GetRootWindow());
331 EXPECT_EQ(initial_bounds
.x() + 500, window
->GetBoundsInScreen().x());
332 EXPECT_EQ(initial_bounds
.y() - 100, window
->GetBoundsInScreen().y());
333 EXPECT_FALSE(wm::GetWindowState(window
.get())->panel_attached());
334 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
337 TEST_F(PanelWindowResizerTest
, DetachThenAttachToSecondDisplay
) {
338 if (!SupportsMultipleDisplays())
341 UpdateDisplay("600x400,600x600");
342 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
343 scoped_ptr
<aura::Window
> window(
344 CreatePanelWindow(gfx::Point(0, 0)));
345 gfx::Rect initial_bounds
= window
->GetBoundsInScreen();
346 EXPECT_EQ(root_windows
[0], window
->GetRootWindow());
348 // Detach the window.
349 DragStart(window
.get());
352 EXPECT_EQ(root_windows
[0], window
->GetRootWindow());
353 EXPECT_FALSE(wm::GetWindowState(window
.get())->panel_attached());
355 // Drag the window just above the other display's launcher.
356 DragStart(window
.get());
358 EXPECT_EQ(initial_bounds
.x() + 500, window
->GetBoundsInScreen().x());
360 // Should stick to other launcher.
361 EXPECT_EQ(initial_bounds
.y() + 200, window
->GetBoundsInScreen().y());
364 // When dropped should move to second display's panel container.
365 EXPECT_EQ(root_windows
[1], window
->GetRootWindow());
366 EXPECT_TRUE(wm::GetWindowState(window
.get())->panel_attached());
367 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
370 TEST_F(PanelWindowResizerTest
, AttachToSecondDisplay
) {
371 if (!SupportsMultipleDisplays())
374 UpdateDisplay("600x400,600x600");
375 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
376 scoped_ptr
<aura::Window
> window(
377 CreatePanelWindow(gfx::Point(0, 0)));
378 gfx::Rect initial_bounds
= window
->GetBoundsInScreen();
379 EXPECT_EQ(root_windows
[0], window
->GetRootWindow());
381 // Drag the window just above the other display's launcher.
382 DragStart(window
.get());
384 EXPECT_EQ(initial_bounds
.x() + 500, window
->GetBoundsInScreen().x());
386 // Should stick to other launcher.
387 EXPECT_EQ(initial_bounds
.y() + 200, window
->GetBoundsInScreen().y());
390 // When dropped should move to second display's panel container.
391 EXPECT_EQ(root_windows
[1], window
->GetRootWindow());
392 EXPECT_TRUE(wm::GetWindowState(window
.get())->panel_attached());
393 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
396 TEST_F(PanelWindowResizerTest
, AttachToSecondFullscreenDisplay
) {
397 if (!SupportsMultipleDisplays())
400 UpdateDisplay("600x400,600x600");
401 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
402 scoped_ptr
<aura::Window
> window(
403 CreatePanelWindow(gfx::Point(0, 0)));
404 scoped_ptr
<aura::Window
> fullscreen(
405 CreateTestWindowInShellWithBounds(gfx::Rect(600, 0, 101, 101)));
406 wm::GetWindowState(fullscreen
.get())->Activate();
407 const wm::WMEvent
event(wm::WM_EVENT_TOGGLE_FULLSCREEN
);
408 wm::GetWindowState(fullscreen
.get())->OnWMEvent(&event
);
409 EXPECT_TRUE(wm::GetWindowState(fullscreen
.get())->IsFullscreen());
411 gfx::Rect initial_bounds
= window
->GetBoundsInScreen();
412 EXPECT_EQ(root_windows
[0], window
->GetRootWindow());
414 // Activate and drag the window to the other display's launcher.
415 wm::GetWindowState(window
.get())->Activate();
416 DragStart(window
.get());
418 EXPECT_EQ(initial_bounds
.x() + 500, window
->GetBoundsInScreen().x());
419 EXPECT_GT(window
->GetBoundsInScreen().y(),
420 initial_bounds
.y() + 200);
423 // When dropped should move to second display's panel container.
424 EXPECT_EQ(root_windows
[1], window
->GetRootWindow());
425 EXPECT_TRUE(wm::GetWindowState(window
.get())->panel_attached());
426 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
427 EXPECT_TRUE(window
->IsVisible());
428 EXPECT_TRUE(wm::GetWindowState(window
.get())->IsActive());
429 EXPECT_EQ(initial_bounds
.y() + 200, window
->GetBoundsInScreen().y());
432 TEST_F(PanelWindowResizerTest
, RevertDragRestoresAttachment
) {
433 scoped_ptr
<aura::Window
> window(
434 CreatePanelWindow(gfx::Point(0, 0)));
435 EXPECT_TRUE(wm::GetWindowState(window
.get())->panel_attached());
436 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
437 DragStart(window
.get());
440 EXPECT_TRUE(wm::GetWindowState(window
.get())->panel_attached());
441 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
444 DragStart(window
.get());
447 EXPECT_FALSE(wm::GetWindowState(window
.get())->panel_attached());
448 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
450 // Drag back to launcher.
451 DragStart(window
.get());
454 // When the drag is reverted it should remain detached.
456 EXPECT_FALSE(wm::GetWindowState(window
.get())->panel_attached());
457 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
460 TEST_F(PanelWindowResizerTest
, DragMovesToPanelLayer
) {
461 scoped_ptr
<aura::Window
> window(CreatePanelWindow(gfx::Point(0, 0)));
462 DragStart(window
.get());
465 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
467 // While moving the panel window should be moved to the panel container.
468 DragStart(window
.get());
470 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
473 // When dropped it should return to the default container.
474 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
477 TEST_P(PanelWindowResizerTextDirectionTest
, DragReordersPanelsHorizontal
) {
478 if (!SupportsHostWindowResize())
481 DragAlongShelfReorder(base::i18n::IsRTL() ? 1 : -1, 0);
484 TEST_F(PanelWindowResizerTest
, DragReordersPanelsVertical
) {
485 if (!SupportsHostWindowResize())
488 ash::Shell
* shell
= ash::Shell::GetInstance();
489 shell
->SetShelfAlignment(SHELF_ALIGNMENT_LEFT
, shell
->GetPrimaryRootWindow());
490 DragAlongShelfReorder(0, -1);
493 // Tests that panels can have transient children of different types.
494 // The transient children should be reparented in sync with the panel.
495 TEST_P(PanelWindowResizerTransientTest
, PanelWithTransientChild
) {
496 if (!SupportsHostWindowResize())
499 scoped_ptr
<aura::Window
> window(CreatePanelWindow(gfx::Point(0, 0)));
500 scoped_ptr
<aura::Window
> child(CreateTestWindowInShellWithDelegateAndType(
501 NULL
, transient_window_type_
, 0, gfx::Rect(20, 20, 150, 40)));
502 ::wm::AddTransientChild(window
.get(), child
.get());
503 if (window
->parent() != child
->parent())
504 window
->parent()->AddChild(child
.get());
505 EXPECT_EQ(window
.get(), ::wm::GetTransientParent(child
.get()));
507 // Drag the child to the shelf. Its new position should not be overridden.
508 const gfx::Rect
attached_bounds(window
->GetBoundsInScreen());
509 const int dy
= window
->GetBoundsInScreen().bottom() -
510 child
->GetBoundsInScreen().bottom();
511 DragStart(child
.get());
513 // While moving the transient child window should be in the panel container.
514 EXPECT_EQ(kShellWindowId_PanelContainer
, child
->parent()->id());
516 // Child should move, |window| should not.
517 EXPECT_EQ(gfx::Point(20 + 50, 20 + dy
).ToString(),
518 child
->GetBoundsInScreen().origin().ToString());
519 EXPECT_EQ(attached_bounds
.ToString(), window
->GetBoundsInScreen().ToString());
521 // Drag the child along the the shelf past the |window|.
522 // Its new position should not be overridden.
523 DragStart(child
.get());
525 // While moving the transient child window should be in the panel container.
526 EXPECT_EQ(kShellWindowId_PanelContainer
, child
->parent()->id());
528 // |child| should move, |window| should not.
529 EXPECT_EQ(gfx::Point(20 + 50 + 350, 20 + dy
).ToString(),
530 child
->GetBoundsInScreen().origin().ToString());
531 EXPECT_EQ(attached_bounds
.ToString(), window
->GetBoundsInScreen().ToString());
533 DragStart(window
.get());
535 // While moving the windows should be in the panel container.
536 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
537 EXPECT_EQ(kShellWindowId_PanelContainer
, child
->parent()->id());
539 // When dropped they should return to the default container.
540 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
541 EXPECT_EQ(kShellWindowId_DefaultContainer
, child
->parent()->id());
543 // While moving the window and child should be moved to the panel container.
544 DragStart(window
.get());
546 EXPECT_EQ(kShellWindowId_PanelContainer
, window
->parent()->id());
547 EXPECT_EQ(kShellWindowId_PanelContainer
, child
->parent()->id());
550 // When dropped they should return to the default container.
551 EXPECT_EQ(kShellWindowId_DefaultContainer
, window
->parent()->id());
552 EXPECT_EQ(kShellWindowId_DefaultContainer
, child
->parent()->id());
555 INSTANTIATE_TEST_CASE_P(LtrRtl
, PanelWindowResizerTextDirectionTest
,
557 INSTANTIATE_TEST_CASE_P(NormalPanelPopup
,
558 PanelWindowResizerTransientTest
,
559 testing::Values(ui::wm::WINDOW_TYPE_NORMAL
,
560 ui::wm::WINDOW_TYPE_PANEL
,
561 ui::wm::WINDOW_TYPE_POPUP
));