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/launcher/launcher.h"
8 #include "ash/launcher/launcher_model.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shelf/shelf_layout_manager.h"
11 #include "ash/shelf/shelf_types.h"
12 #include "ash/shelf/shelf_widget.h"
13 #include "ash/shell.h"
14 #include "ash/shell_window_ids.h"
15 #include "ash/test/ash_test_base.h"
16 #include "ash/test/cursor_manager_test_api.h"
17 #include "ash/test/shell_test_api.h"
18 #include "ash/test/test_launcher_delegate.h"
19 #include "ash/wm/drag_window_resizer.h"
20 #include "ash/wm/panels/panel_layout_manager.h"
21 #include "ash/wm/window_state.h"
22 #include "base/win/windows_version.h"
23 #include "ui/aura/client/aura_constants.h"
24 #include "ui/aura/root_window.h"
25 #include "ui/base/hit_test.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/ui_base_types.h"
28 #include "ui/views/widget/widget.h"
33 class PanelWindowResizerTest : public test::AshTestBase {
35 PanelWindowResizerTest() {}
36 virtual ~PanelWindowResizerTest() {}
38 virtual void SetUp() OVERRIDE {
40 UpdateDisplay("600x400");
41 test::ShellTestApi test_api(Shell::GetInstance());
42 model_ = test_api.launcher_model();
43 launcher_delegate_ = test::TestLauncherDelegate::instance();
46 virtual void TearDown() OVERRIDE {
47 AshTestBase::TearDown();
51 gfx::Point CalculateDragPoint(const WindowResizer& resizer,
54 gfx::Point location = resizer.GetInitialLocation();
55 location.set_x(location.x() + delta_x);
56 location.set_y(location.y() + delta_y);
60 aura::Window* CreatePanelWindow(const gfx::Point& origin) {
61 gfx::Rect bounds(origin, gfx::Size(101, 101));
62 aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
64 aura::client::WINDOW_TYPE_PANEL,
67 launcher_delegate_->AddLauncherItem(window);
68 PanelLayoutManager* manager =
69 static_cast<PanelLayoutManager*>(
70 Shell::GetContainer(window->GetRootWindow(),
71 internal::kShellWindowId_PanelContainer)->
77 void DragStart(aura::Window* window) {
78 resizer_.reset(CreateWindowResizer(
80 window->bounds().origin(),
82 aura::client::WINDOW_MOVE_SOURCE_MOUSE).release());
83 ASSERT_TRUE(resizer_.get());
86 void DragMove(int dx, int dy) {
87 resizer_->Drag(CalculateDragPoint(*resizer_, dx, dy), 0);
91 resizer_->CompleteDrag(0);
96 resizer_->RevertDrag();
100 // Test dragging the panel slightly, then detaching, and then reattaching
101 // dragging out by the vector (dx, dy).
102 void DetachReattachTest(aura::Window* window, int dx, int dy) {
103 wm::WindowState* window_state = wm::GetWindowState(window);
104 EXPECT_TRUE(window_state->panel_attached());
105 aura::Window* root_window = window->GetRootWindow();
106 EXPECT_EQ(internal::kShellWindowId_PanelContainer, window->parent()->id());
108 gfx::Rect initial_bounds = window->GetBoundsInScreen();
110 // Drag the panel slightly. The window should still be snapped to the
112 DragMove(dx * 5, dy * 5);
113 EXPECT_EQ(initial_bounds.x(), window->GetBoundsInScreen().x());
114 EXPECT_EQ(initial_bounds.y(), window->GetBoundsInScreen().y());
116 // Drag further out and the window should now move to the cursor.
117 DragMove(dx * 100, dy * 100);
118 EXPECT_EQ(initial_bounds.x() + dx * 100, window->GetBoundsInScreen().x());
119 EXPECT_EQ(initial_bounds.y() + dy * 100, window->GetBoundsInScreen().y());
121 // The panel should be detached when the drag completes.
124 EXPECT_FALSE(window_state->panel_attached());
125 EXPECT_EQ(internal::kShellWindowId_DefaultContainer,
126 window->parent()->id());
127 EXPECT_EQ(root_window, window->GetRootWindow());
130 // Drag the panel down.
131 DragMove(dx * -95, dy * -95);
132 // Release the mouse and the panel should be reattached.
135 // The panel should be reattached and have snapped to the launcher.
136 EXPECT_TRUE(window_state->panel_attached());
137 EXPECT_EQ(initial_bounds.x(), window->GetBoundsInScreen().x());
138 EXPECT_EQ(initial_bounds.y(), window->GetBoundsInScreen().y());
139 EXPECT_EQ(internal::kShellWindowId_PanelContainer, window->parent()->id());
142 void TestWindowOrder(const std::vector<aura::Window*>& window_order) {
143 int panel_index = model_->FirstPanelIndex();
144 EXPECT_EQ((int)(panel_index + window_order.size()), model_->item_count());
145 for (std::vector<aura::Window*>::const_iterator iter =
146 window_order.begin(); iter != window_order.end();
147 ++iter, ++panel_index) {
148 LauncherID id = launcher_delegate_->GetIDByWindow(*iter);
149 EXPECT_EQ(id, model_->items()[panel_index].id);
153 // Test dragging panel window along the shelf and verify that panel icons
154 // are reordered appropriately.
155 void DragAlongShelfReorder(int dx, int dy) {
156 gfx::Point origin(0, 0);
157 scoped_ptr<aura::Window> w1(CreatePanelWindow(origin));
158 scoped_ptr<aura::Window> w2(CreatePanelWindow(origin));
159 std::vector<aura::Window*> window_order_original;
160 std::vector<aura::Window*> window_order_swapped;
161 window_order_original.push_back(w1.get());
162 window_order_original.push_back(w2.get());
163 window_order_swapped.push_back(w2.get());
164 window_order_swapped.push_back(w1.get());
165 TestWindowOrder(window_order_original);
167 // Drag window #2 to the beginning of the shelf.
169 DragMove(400 * dx, 400 * dy);
170 TestWindowOrder(window_order_swapped);
173 // Expect swapped window order.
174 TestWindowOrder(window_order_swapped);
176 // Drag window #2 back to the end.
178 DragMove(-400 * dx, -400 * dy);
179 TestWindowOrder(window_order_original);
182 // Expect original order.
183 TestWindowOrder(window_order_original);
187 scoped_ptr<WindowResizer> resizer_;
188 internal::PanelLayoutManager* panel_layout_manager_;
189 LauncherModel* model_;
190 test::TestLauncherDelegate* launcher_delegate_;
192 DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTest);
195 class PanelWindowResizerTextDirectionTest
196 : public PanelWindowResizerTest,
197 public testing::WithParamInterface<bool> {
199 PanelWindowResizerTextDirectionTest() : is_rtl_(GetParam()) {}
200 virtual ~PanelWindowResizerTextDirectionTest() {}
202 virtual void SetUp() OVERRIDE {
203 original_locale = l10n_util::GetApplicationLocale(std::string());
205 base::i18n::SetICUDefaultLocale("he");
206 PanelWindowResizerTest::SetUp();
207 ASSERT_EQ(is_rtl_, base::i18n::IsRTL());
210 virtual void TearDown() OVERRIDE {
212 base::i18n::SetICUDefaultLocale(original_locale);
213 PanelWindowResizerTest::TearDown();
218 std::string original_locale;
220 DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTextDirectionTest);
223 // PanelLayoutManager and PanelWindowResizer should work if panels have
224 // transient children of supported types.
225 class PanelWindowResizerTransientTest
226 : public PanelWindowResizerTest,
227 public testing::WithParamInterface<aura::client::WindowType> {
229 PanelWindowResizerTransientTest() : transient_window_type_(GetParam()) {}
230 virtual ~PanelWindowResizerTransientTest() {}
233 aura::client::WindowType transient_window_type_;
236 DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTransientTest);
239 // Verifies a window can be dragged from the panel and detached and then
241 TEST_F(PanelWindowResizerTest, PanelDetachReattachBottom) {
242 if (!SupportsHostWindowResize())
245 scoped_ptr<aura::Window> window(
246 CreatePanelWindow(gfx::Point(0, 0)));
247 DetachReattachTest(window.get(), 0, -1);
250 TEST_F(PanelWindowResizerTest, PanelDetachReattachLeft) {
251 if (!SupportsHostWindowResize())
254 ash::Shell* shell = ash::Shell::GetInstance();
255 shell->SetShelfAlignment(SHELF_ALIGNMENT_LEFT, shell->GetPrimaryRootWindow());
256 scoped_ptr<aura::Window> window(
257 CreatePanelWindow(gfx::Point(0, 0)));
258 DetachReattachTest(window.get(), 1, 0);
261 TEST_F(PanelWindowResizerTest, PanelDetachReattachRight) {
262 if (!SupportsHostWindowResize())
265 ash::Shell* shell = ash::Shell::GetInstance();
266 shell->SetShelfAlignment(SHELF_ALIGNMENT_RIGHT,
267 shell->GetPrimaryRootWindow());
268 scoped_ptr<aura::Window> window(
269 CreatePanelWindow(gfx::Point(0, 0)));
270 DetachReattachTest(window.get(), -1, 0);
273 TEST_F(PanelWindowResizerTest, PanelDetachReattachTop) {
274 if (!SupportsHostWindowResize())
277 ash::Shell* shell = ash::Shell::GetInstance();
278 shell->SetShelfAlignment(SHELF_ALIGNMENT_TOP, shell->GetPrimaryRootWindow());
279 scoped_ptr<aura::Window> window(
280 CreatePanelWindow(gfx::Point(0, 0)));
281 DetachReattachTest(window.get(), 0, 1);
284 TEST_F(PanelWindowResizerTest, PanelDetachReattachMultipleDisplays) {
285 if (!SupportsMultipleDisplays())
288 UpdateDisplay("600x400,600x400");
289 Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
290 scoped_ptr<aura::Window> window(
291 CreatePanelWindow(gfx::Point(600, 0)));
292 EXPECT_EQ(root_windows[1], window->GetRootWindow());
293 DetachReattachTest(window.get(), 0, -1);
296 TEST_F(PanelWindowResizerTest, DetachThenDragAcrossDisplays) {
297 if (!SupportsMultipleDisplays())
300 UpdateDisplay("600x400,600x400");
301 Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
302 scoped_ptr<aura::Window> window(
303 CreatePanelWindow(gfx::Point(0, 0)));
304 gfx::Rect initial_bounds = window->GetBoundsInScreen();
305 EXPECT_EQ(root_windows[0], window->GetRootWindow());
306 DragStart(window.get());
309 EXPECT_EQ(root_windows[0], window->GetRootWindow());
310 EXPECT_EQ(initial_bounds.x(), window->GetBoundsInScreen().x());
311 EXPECT_EQ(initial_bounds.y() - 100, window->GetBoundsInScreen().y());
312 EXPECT_FALSE(wm::GetWindowState(window.get())->panel_attached());
313 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
315 DragStart(window.get());
318 EXPECT_EQ(root_windows[1], window->GetRootWindow());
319 EXPECT_EQ(initial_bounds.x() + 500, window->GetBoundsInScreen().x());
320 EXPECT_EQ(initial_bounds.y() - 100, window->GetBoundsInScreen().y());
321 EXPECT_FALSE(wm::GetWindowState(window.get())->panel_attached());
322 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
325 TEST_F(PanelWindowResizerTest, DetachAcrossDisplays) {
326 if (!SupportsMultipleDisplays())
329 UpdateDisplay("600x400,600x400");
330 Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
331 scoped_ptr<aura::Window> window(
332 CreatePanelWindow(gfx::Point(0, 0)));
333 gfx::Rect initial_bounds = window->GetBoundsInScreen();
334 EXPECT_EQ(root_windows[0], window->GetRootWindow());
335 DragStart(window.get());
338 EXPECT_EQ(root_windows[1], window->GetRootWindow());
339 EXPECT_EQ(initial_bounds.x() + 500, window->GetBoundsInScreen().x());
340 EXPECT_EQ(initial_bounds.y() - 100, window->GetBoundsInScreen().y());
341 EXPECT_FALSE(wm::GetWindowState(window.get())->panel_attached());
342 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
345 TEST_F(PanelWindowResizerTest, DetachThenAttachToSecondDisplay) {
346 if (!SupportsMultipleDisplays())
349 UpdateDisplay("600x400,600x600");
350 Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
351 scoped_ptr<aura::Window> window(
352 CreatePanelWindow(gfx::Point(0, 0)));
353 gfx::Rect initial_bounds = window->GetBoundsInScreen();
354 EXPECT_EQ(root_windows[0], window->GetRootWindow());
356 // Detach the window.
357 DragStart(window.get());
360 EXPECT_EQ(root_windows[0], window->GetRootWindow());
361 EXPECT_FALSE(wm::GetWindowState(window.get())->panel_attached());
363 // Drag the window just above the other display's launcher.
364 DragStart(window.get());
366 EXPECT_EQ(initial_bounds.x() + 500, window->GetBoundsInScreen().x());
368 // Should stick to other launcher.
369 EXPECT_EQ(initial_bounds.y() + 200, window->GetBoundsInScreen().y());
372 // When dropped should move to second display's panel container.
373 EXPECT_EQ(root_windows[1], window->GetRootWindow());
374 EXPECT_TRUE(wm::GetWindowState(window.get())->panel_attached());
375 EXPECT_EQ(internal::kShellWindowId_PanelContainer, window->parent()->id());
378 TEST_F(PanelWindowResizerTest, AttachToSecondDisplay) {
379 if (!SupportsMultipleDisplays())
382 UpdateDisplay("600x400,600x600");
383 Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
384 scoped_ptr<aura::Window> window(
385 CreatePanelWindow(gfx::Point(0, 0)));
386 gfx::Rect initial_bounds = window->GetBoundsInScreen();
387 EXPECT_EQ(root_windows[0], window->GetRootWindow());
389 // Drag the window just above the other display's launcher.
390 DragStart(window.get());
392 EXPECT_EQ(initial_bounds.x() + 500, window->GetBoundsInScreen().x());
394 // Should stick to other launcher.
395 EXPECT_EQ(initial_bounds.y() + 200, window->GetBoundsInScreen().y());
398 // When dropped should move to second display's panel container.
399 EXPECT_EQ(root_windows[1], window->GetRootWindow());
400 EXPECT_TRUE(wm::GetWindowState(window.get())->panel_attached());
401 EXPECT_EQ(internal::kShellWindowId_PanelContainer, window->parent()->id());
404 TEST_F(PanelWindowResizerTest, RevertDragRestoresAttachment) {
405 scoped_ptr<aura::Window> window(
406 CreatePanelWindow(gfx::Point(0, 0)));
407 EXPECT_TRUE(wm::GetWindowState(window.get())->panel_attached());
408 EXPECT_EQ(internal::kShellWindowId_PanelContainer, window->parent()->id());
409 DragStart(window.get());
412 EXPECT_TRUE(wm::GetWindowState(window.get())->panel_attached());
413 EXPECT_EQ(internal::kShellWindowId_PanelContainer, window->parent()->id());
416 DragStart(window.get());
419 EXPECT_FALSE(wm::GetWindowState(window.get())->panel_attached());
420 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
422 // Drag back to launcher.
423 DragStart(window.get());
426 // When the drag is reverted it should remain detached.
428 EXPECT_FALSE(wm::GetWindowState(window.get())->panel_attached());
429 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
432 TEST_F(PanelWindowResizerTest, DragMovesToPanelLayer) {
433 scoped_ptr<aura::Window> window(CreatePanelWindow(gfx::Point(0, 0)));
434 DragStart(window.get());
437 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
439 // While moving the panel window should be moved to the panel container.
440 DragStart(window.get());
442 EXPECT_EQ(internal::kShellWindowId_PanelContainer, window->parent()->id());
445 // When dropped it should return to the default container.
446 EXPECT_EQ(internal::kShellWindowId_DefaultContainer,
447 window->parent()->id());
450 TEST_P(PanelWindowResizerTextDirectionTest, DragReordersPanelsHorizontal) {
451 if (!SupportsHostWindowResize())
454 DragAlongShelfReorder(base::i18n::IsRTL() ? 1 : -1, 0);
457 TEST_F(PanelWindowResizerTest, DragReordersPanelsVertical) {
458 if (!SupportsHostWindowResize())
461 ash::Shell* shell = ash::Shell::GetInstance();
462 shell->SetShelfAlignment(SHELF_ALIGNMENT_LEFT, shell->GetPrimaryRootWindow());
463 DragAlongShelfReorder(0, -1);
466 // Tests that panels can have transient children of different types.
467 // The transient children should be reparented in sync with the panel.
468 TEST_P(PanelWindowResizerTransientTest, PanelWithTransientChild) {
469 if (!SupportsHostWindowResize())
472 scoped_ptr<aura::Window> window(CreatePanelWindow(gfx::Point(0, 0)));
473 scoped_ptr<aura::Window> child(CreateTestWindowInShellWithDelegateAndType(
474 NULL, transient_window_type_, 0, gfx::Rect(20, 20, 150, 40)));
475 window->AddTransientChild(child.get());
476 if (window->parent() != child->parent())
477 window->parent()->AddChild(child.get());
478 EXPECT_EQ(window.get(), child->transient_parent());
480 // Drag the child to the shelf. Its new position should not be overridden.
481 const gfx::Rect attached_bounds(window->GetBoundsInScreen());
482 const int dy = window->GetBoundsInScreen().bottom() -
483 child->GetBoundsInScreen().bottom();
484 DragStart(child.get());
486 // While moving the transient child window should be in the panel container.
487 EXPECT_EQ(internal::kShellWindowId_PanelContainer, child->parent()->id());
489 // Child should move, |window| should not.
490 EXPECT_EQ(gfx::Point(20 + 50, 20 + dy).ToString(),
491 child->GetBoundsInScreen().origin().ToString());
492 EXPECT_EQ(attached_bounds.ToString(), window->GetBoundsInScreen().ToString());
494 // Drag the child along the the shelf past the |window|.
495 // Its new position should not be overridden.
496 DragStart(child.get());
498 // While moving the transient child window should be in the panel container.
499 EXPECT_EQ(internal::kShellWindowId_PanelContainer, child->parent()->id());
501 // |child| should move, |window| should not.
502 EXPECT_EQ(gfx::Point(20 + 50 + 350, 20 + dy).ToString(),
503 child->GetBoundsInScreen().origin().ToString());
504 EXPECT_EQ(attached_bounds.ToString(), window->GetBoundsInScreen().ToString());
506 DragStart(window.get());
508 // While moving the windows should be in the panel container.
509 EXPECT_EQ(internal::kShellWindowId_PanelContainer, window->parent()->id());
510 EXPECT_EQ(internal::kShellWindowId_PanelContainer, child->parent()->id());
512 // When dropped they should return to the default container.
513 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
514 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, child->parent()->id());
516 // While moving the window and child should be moved to the panel container.
517 DragStart(window.get());
519 EXPECT_EQ(internal::kShellWindowId_PanelContainer, window->parent()->id());
520 EXPECT_EQ(internal::kShellWindowId_PanelContainer, child->parent()->id());
523 // When dropped they should return to the default container.
524 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
525 EXPECT_EQ(internal::kShellWindowId_DefaultContainer, child->parent()->id());
528 INSTANTIATE_TEST_CASE_P(LtrRtl, PanelWindowResizerTextDirectionTest,
530 INSTANTIATE_TEST_CASE_P(NormalPanelPopup, PanelWindowResizerTransientTest,
531 testing::Values(aura::client::WINDOW_TYPE_NORMAL,
532 aura::client::WINDOW_TYPE_PANEL,
533 aura::client::WINDOW_TYPE_POPUP));
535 } // namespace internal