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.
4
5#include "ash/wm/dock/docked_window_resizer.h"
6
7#include "ash/ash_switches.h"
8#include "ash/root_window_controller.h"
9#include "ash/screen_util.h"
10#include "ash/shelf/shelf.h"
11#include "ash/shelf/shelf_layout_manager.h"
12#include "ash/shelf/shelf_model.h"
13#include "ash/shelf/shelf_types.h"
14#include "ash/shelf/shelf_widget.h"
15#include "ash/shell.h"
16#include "ash/shell_window_ids.h"
17#include "ash/test/ash_test_base.h"
18#include "ash/test/cursor_manager_test_api.h"
19#include "ash/test/shell_test_api.h"
20#include "ash/test/test_shelf_delegate.h"
21#include "ash/wm/dock/docked_window_layout_manager.h"
22#include "ash/wm/drag_window_resizer.h"
23#include "ash/wm/panels/panel_layout_manager.h"
24#include "ash/wm/window_state.h"
25#include "ash/wm/window_util.h"
26#include "ash/wm/wm_event.h"
27#include "base/command_line.h"
28#include "ui/aura/client/aura_constants.h"
29#include "ui/aura/client/window_tree_client.h"
30#include "ui/aura/test/test_window_delegate.h"
31#include "ui/aura/window_event_dispatcher.h"
32#include "ui/base/hit_test.h"
33#include "ui/base/ui_base_types.h"
34#include "ui/events/test/event_generator.h"
35#include "ui/views/widget/widget.h"
36#include "ui/wm/core/coordinate_conversion.h"
37#include "ui/wm/core/window_util.h"
38
39namespace ash {
40
41class DockedWindowResizerTest
42    : public test::AshTestBase,
43      public testing::WithParamInterface<ui::wm::WindowType> {
44 public:
45  DockedWindowResizerTest() : model_(NULL), window_type_(GetParam()) {}
46  virtual ~DockedWindowResizerTest() {}
47
48  virtual void SetUp() OVERRIDE {
49    AshTestBase::SetUp();
50    UpdateDisplay("600x400");
51    test::ShellTestApi test_api(Shell::GetInstance());
52    model_ = test_api.shelf_model();
53  }
54
55  virtual void TearDown() OVERRIDE {
56    AshTestBase::TearDown();
57  }
58
59 protected:
60  enum DockedEdge {
61    DOCKED_EDGE_NONE,
62    DOCKED_EDGE_LEFT,
63    DOCKED_EDGE_RIGHT,
64  };
65
66  int ideal_width() const { return DockedWindowLayoutManager::kIdealWidth; }
67  int min_dock_gap() const { return DockedWindowLayoutManager::kMinDockGap; }
68  int max_width() const { return DockedWindowLayoutManager::kMaxDockWidth; }
69  int docked_width(const DockedWindowLayoutManager* layout_manager) const {
70    return layout_manager->docked_width_;
71  }
72  int docked_alignment(const DockedWindowLayoutManager* layout_manager) const {
73    return layout_manager->alignment_;
74  }
75  aura::Window* CreateTestWindow(const gfx::Rect& bounds) {
76    aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
77        &delegate_,
78        window_type_,
79        0,
80        bounds);
81    if (window_type_ == ui::wm::WINDOW_TYPE_PANEL) {
82      test::TestShelfDelegate* shelf_delegate =
83          test::TestShelfDelegate::instance();
84      shelf_delegate->AddShelfItem(window);
85      PanelLayoutManager* manager = static_cast<PanelLayoutManager*>(
86          Shell::GetContainer(window->GetRootWindow(),
87                              kShellWindowId_PanelContainer)->layout_manager());
88      manager->Relayout();
89    }
90    return window;
91  }
92
93  aura::Window* CreateModalWindow(const gfx::Rect& bounds) {
94    aura::Window* window = new aura::Window(&delegate_);
95    window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
96    window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
97    window->Init(aura::WINDOW_LAYER_TEXTURED);
98    window->Show();
99
100    if (bounds.IsEmpty()) {
101      ParentWindowInPrimaryRootWindow(window);
102    } else {
103      gfx::Display display =
104          Shell::GetScreen()->GetDisplayMatching(bounds);
105      aura::Window* root = ash::Shell::GetInstance()->display_controller()->
106          GetRootWindowForDisplayId(display.id());
107      gfx::Point origin = bounds.origin();
108      ::wm::ConvertPointFromScreen(root, &origin);
109      window->SetBounds(gfx::Rect(origin, bounds.size()));
110      aura::client::ParentWindowWithContext(window, root, bounds);
111    }
112    return window;
113  }
114
115  static WindowResizer* CreateSomeWindowResizer(
116      aura::Window* window,
117      const gfx::Point& point_in_parent,
118      int window_component) {
119    return CreateWindowResizer(
120        window,
121        point_in_parent,
122        window_component,
123        aura::client::WINDOW_MOVE_SOURCE_MOUSE).release();
124  }
125
126  void DragStart(aura::Window* window) {
127    DragStartAtOffsetFromWindowOrigin(window, 0, 0);
128  }
129
130  void DragStartAtOffsetFromWindowOrigin(aura::Window* window,
131                                         int dx, int dy) {
132    initial_location_in_parent_ =
133        window->bounds().origin() + gfx::Vector2d(dx, dy);
134    resizer_.reset(CreateSomeWindowResizer(window,
135                                           initial_location_in_parent_,
136                                           HTCAPTION));
137    ASSERT_TRUE(resizer_.get());
138  }
139
140  void ResizeStartAtOffsetFromWindowOrigin(aura::Window* window,
141                                           int dx, int dy,
142                                           int window_component) {
143    initial_location_in_parent_ =
144        window->bounds().origin() + gfx::Vector2d(dx, dy);
145    resizer_.reset(CreateSomeWindowResizer(window,
146                                           initial_location_in_parent_,
147                                           window_component));
148    ASSERT_TRUE(resizer_.get());
149  }
150
151  void DragMove(int dx, int dy) {
152    resizer_->Drag(initial_location_in_parent_ + gfx::Vector2d(dx, dy), 0);
153  }
154
155  void DragEnd() {
156    resizer_->CompleteDrag();
157    resizer_.reset();
158  }
159
160  void DragRevert() {
161    resizer_->RevertDrag();
162    resizer_.reset();
163  }
164
165  // Panels are parented by panel container during drags.
166  // All other windows that are tested here are parented by dock container
167  // during drags.
168  int CorrectContainerIdDuringDrag() {
169    if (window_type_ == ui::wm::WINDOW_TYPE_PANEL)
170      return kShellWindowId_PanelContainer;
171    return kShellWindowId_DockedContainer;
172  }
173
174  // Test dragging the window vertically (to detach if it is a panel) and then
175  // horizontally to the edge with an added offset from the edge of |dx|.
176  void DragRelativeToEdge(DockedEdge edge,
177                          aura::Window* window,
178                          int dx) {
179    DragVerticallyAndRelativeToEdge(
180        edge,
181        window,
182        dx,
183        window_type_ == ui::wm::WINDOW_TYPE_PANEL ? -100 : 20,
184        25,
185        5);
186  }
187
188  void DragToVerticalPositionAndToEdge(DockedEdge edge,
189                                       aura::Window* window,
190                                       int y) {
191    DragToVerticalPositionRelativeToEdge(edge, window, 0, y);
192  }
193
194  void DragToVerticalPositionRelativeToEdge(DockedEdge edge,
195                                            aura::Window* window,
196                                            int dx,
197                                            int y) {
198    gfx::Rect initial_bounds = window->GetBoundsInScreen();
199    DragVerticallyAndRelativeToEdge(edge,
200                                    window,
201                                    dx, y - initial_bounds.y(),
202                                    25, 5);
203  }
204
205  // Detach if our window is a panel, then drag it vertically by |dy| and
206  // horizontally to the edge with an added offset from the edge of |dx|.
207  void DragVerticallyAndRelativeToEdge(DockedEdge edge,
208                                       aura::Window* window,
209                                       int dx, int dy,
210                                       int grab_x, int grab_y) {
211    gfx::Rect initial_bounds = window->GetBoundsInScreen();
212    // avoid snap by clicking away from the border
213    ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(window,
214                                                              grab_x, grab_y));
215
216    gfx::Rect work_area =
217        Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
218    gfx::Point initial_location_in_screen = initial_location_in_parent_;
219    ::wm::ConvertPointToScreen(window->parent(), &initial_location_in_screen);
220    // Drag the window left or right to the edge (or almost to it).
221    if (edge == DOCKED_EDGE_LEFT)
222      dx += work_area.x() - initial_location_in_screen.x();
223    else if (edge == DOCKED_EDGE_RIGHT)
224      dx += work_area.right() - 1 - initial_location_in_screen.x();
225    DragMove(dx, dy);
226    EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
227    // Release the mouse and the panel should be attached to the dock.
228    DragEnd();
229
230    // x-coordinate can get adjusted by snapping or sticking.
231    // y-coordinate could be changed by possible automatic layout if docked.
232    if (window->parent()->id() != kShellWindowId_DockedContainer &&
233        !wm::GetWindowState(window)->HasRestoreBounds()) {
234      EXPECT_EQ(initial_bounds.y() + dy, window->GetBoundsInScreen().y());
235    }
236  }
237
238  bool test_panels() const { return window_type_ == ui::wm::WINDOW_TYPE_PANEL; }
239
240  aura::test::TestWindowDelegate* delegate() {
241    return &delegate_;
242  }
243
244  const gfx::Point& initial_location_in_parent() const {
245    return initial_location_in_parent_;
246  }
247
248 private:
249  scoped_ptr<WindowResizer> resizer_;
250  ShelfModel* model_;
251  ui::wm::WindowType window_type_;
252  aura::test::TestWindowDelegate delegate_;
253
254  // Location at start of the drag in |window->parent()|'s coordinates.
255  gfx::Point initial_location_in_parent_;
256
257  DISALLOW_COPY_AND_ASSIGN(DockedWindowResizerTest);
258};
259
260// Verifies a window can be dragged and attached to the dock.
261TEST_P(DockedWindowResizerTest, AttachRightPrecise) {
262  if (!SupportsHostWindowResize())
263    return;
264
265  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
266  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
267
268  // The window should be docked at the right edge.
269  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
270            window->GetBoundsInScreen().right());
271  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
272}
273
274// Verifies a window can be dragged and attached to the dock
275// even if pointer overshoots the screen edge by a few pixels (sticky edge)
276TEST_P(DockedWindowResizerTest, AttachRightOvershoot) {
277  if (!SupportsHostWindowResize())
278    return;
279
280  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
281  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), +4);
282
283  // The window should be docked at the right edge.
284  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
285            window->GetBoundsInScreen().right());
286  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
287}
288
289// Verifies a window can be dragged and then if a pointer is not quite reaching
290// the screen edge the window does not get docked and stays in the desktop.
291TEST_P(DockedWindowResizerTest, AttachRightUndershoot) {
292  if (!SupportsHostWindowResize())
293    return;
294
295  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
296  // Grabbing at 70px ensures that at least 30% of the window is in screen,
297  // otherwise the window would be adjusted in
298  // WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded.
299  const int kGrabOffsetX = 70;
300  const int kUndershootBy = 1;
301  DragVerticallyAndRelativeToEdge(DOCKED_EDGE_RIGHT,
302                                  window.get(),
303                                  -kUndershootBy, test_panels() ? -100 : 20,
304                                  kGrabOffsetX, 5);
305
306  // The window right should be past the screen edge but not docked.
307  // Initial touch point is 70px to the right which helps to find where the edge
308  // should be.
309  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right() +
310            window->bounds().width() - kGrabOffsetX - kUndershootBy - 1,
311            window->GetBoundsInScreen().right());
312  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
313}
314
315// Verifies a window can be dragged and attached to the dock.
316TEST_P(DockedWindowResizerTest, AttachLeftPrecise) {
317  if (!SupportsHostWindowResize())
318    return;
319
320  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
321  DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), 0);
322
323  // The window should be docked at the left edge.
324  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().x(),
325            window->GetBoundsInScreen().x());
326  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
327}
328
329// Verifies a window can be dragged and attached to the dock
330// even if pointer overshoots the screen edge by a few pixels (sticky edge)
331TEST_P(DockedWindowResizerTest, AttachLeftOvershoot) {
332  if (!SupportsHostWindowResize())
333    return;
334
335  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
336  DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), -4);
337
338  // The window should be docked at the left edge.
339  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().x(),
340            window->GetBoundsInScreen().x());
341  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
342}
343
344// Verifies a window can be dragged and then if a pointer is not quite reaching
345// the screen edge the window does not get docked and stays in the desktop.
346TEST_P(DockedWindowResizerTest, AttachLeftUndershoot) {
347  if (!SupportsHostWindowResize())
348    return;
349
350  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
351  gfx::Rect initial_bounds(window->bounds());
352  DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), 1);
353
354  // The window should be crossing the screen edge but not docked.
355  int expected_x = initial_bounds.x() - initial_location_in_parent().x() + 1;
356  EXPECT_EQ(expected_x, window->GetBoundsInScreen().x());
357  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
358}
359
360// Dock on the right side, change shelf alignment, check that windows move to
361// the opposite side.
362TEST_P(DockedWindowResizerTest, AttachRightChangeShelf) {
363  if (!SupportsHostWindowResize())
364    return;
365
366  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
367  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
368
369  // The window should be docked at the right edge.
370  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
371            window->GetBoundsInScreen().right());
372  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
373
374  // set launcher shelf to be aligned on the right
375  ash::Shell* shell = ash::Shell::GetInstance();
376  shell->SetShelfAlignment(SHELF_ALIGNMENT_RIGHT,
377                           shell->GetPrimaryRootWindow());
378  // The window should have moved and get attached to the left dock.
379  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().x(),
380            window->GetBoundsInScreen().x());
381  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
382
383  // set launcher shelf to be aligned on the left
384  shell->SetShelfAlignment(SHELF_ALIGNMENT_LEFT,
385                           shell->GetPrimaryRootWindow());
386  // The window should have moved and get attached to the right edge.
387  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
388            window->GetBoundsInScreen().right());
389  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
390
391  // set launcher shelf to be aligned at the bottom
392  shell->SetShelfAlignment(SHELF_ALIGNMENT_BOTTOM,
393                           shell->GetPrimaryRootWindow());
394  // The window should stay in the right edge.
395  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
396            window->GetBoundsInScreen().right());
397  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
398}
399
400// Dock on the right side, try to undock, then drag more to really undock
401TEST_P(DockedWindowResizerTest, AttachTryDetach) {
402  if (!SupportsHostWindowResize())
403    return;
404
405  scoped_ptr<aura::Window> window(CreateTestWindow(
406      gfx::Rect(0, 0, ideal_width() + 10, 201)));
407  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
408
409  // The window should be docked at the right edge.
410  // Its width should shrink to ideal width.
411  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
412            window->GetBoundsInScreen().right());
413  EXPECT_EQ(ideal_width(), window->GetBoundsInScreen().width());
414  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
415
416  // Try to detach by dragging left less than kSnapToDockDistance.
417  // The window should stay docked.
418  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(
419      window.get(), 10, 0));
420  DragMove(-4, -10);
421  // Release the mouse and the window should be still attached to the dock.
422  DragEnd();
423
424  // The window should be still attached to the right edge.
425  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
426            window->GetBoundsInScreen().right());
427  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
428
429  // Try to detach by dragging left by kSnapToDockDistance or more.
430  // The window should get undocked.
431  const int left_edge = window->bounds().x();
432  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(
433      window.get(), 10, 0));
434  DragMove(-32, -10);
435  // Release the mouse and the window should be no longer attached to the dock.
436  DragEnd();
437
438  // The window should be floating on the desktop again and moved to the left.
439  EXPECT_EQ(left_edge - 32, window->GetBoundsInScreen().x());
440  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
441}
442
443// Dock on the right side, and undock by dragging the right edge of the window
444// header. This test is useful because both the position of the dragged window
445// and the position of the mouse are used in determining whether a window should
446// be undocked.
447TEST_P(DockedWindowResizerTest, AttachTryDetachDragRightEdgeOfHeader) {
448  if (!SupportsHostWindowResize())
449    return;
450
451  scoped_ptr<aura::Window> window(CreateTestWindow(
452      gfx::Rect(0, 0, ideal_width() + 10, 201)));
453  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
454
455  // The window should be docked at the right edge.
456  // Its width should shrink to ideal width.
457  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
458            window->GetBoundsInScreen().right());
459  EXPECT_EQ(ideal_width(), window->GetBoundsInScreen().width());
460  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
461
462  // Try to detach by dragging left less than kSnapToDockDistance.
463  // The window should stay docked.
464  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(
465      window.get(), ideal_width() - 10, 0));
466  DragMove(-4, -10);
467  // Release the mouse and the window should be still attached to the dock.
468  DragEnd();
469
470  // The window should be still attached to the right edge.
471  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
472            window->GetBoundsInScreen().right());
473  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
474
475  // Try to detach by dragging left by kSnapToDockDistance or more.
476  // The window should get undocked.
477  const int left_edge = window->bounds().x();
478  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(
479      window.get(), ideal_width() - 10, 0));
480  DragMove(-32, -10);
481  // Release the mouse and the window should be no longer attached to the dock.
482  DragEnd();
483
484  // The window should be floating on the desktop again and moved to the left.
485  EXPECT_EQ(left_edge - 32, window->GetBoundsInScreen().x());
486  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
487}
488
489// Minimize a docked window, then restore it and check that it is still docked.
490TEST_P(DockedWindowResizerTest, AttachMinimizeRestore) {
491  if (!SupportsHostWindowResize())
492    return;
493
494  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
495  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
496
497  // The window should be docked at the right edge.
498  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
499            window->GetBoundsInScreen().right());
500  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
501
502  wm::WindowState* window_state = wm::GetWindowState(window.get());
503  // Minimize the window, it should be hidden.
504  window_state->Minimize();
505  RunAllPendingInMessageLoop();
506  EXPECT_FALSE(window->IsVisible());
507  EXPECT_TRUE(window_state->IsMinimized());
508  // Restore the window; window should be visible.
509  window_state->Restore();
510  RunAllPendingInMessageLoop();
511  EXPECT_TRUE(window->IsVisible());
512  EXPECT_TRUE(window_state->IsNormalStateType());
513}
514
515// Maximize a docked window and check that it is maximized and no longer docked.
516TEST_P(DockedWindowResizerTest, AttachMaximize) {
517  if (!SupportsHostWindowResize())
518    return;
519
520  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
521  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
522
523  // The window should be docked at the right edge.
524  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
525            window->GetBoundsInScreen().right());
526  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
527
528  wm::WindowState* window_state = wm::GetWindowState(window.get());
529  // Maximize the window, it should get undocked and maximized in a desktop.
530  window_state->Maximize();
531  RunAllPendingInMessageLoop();
532  EXPECT_TRUE(window->IsVisible());
533  EXPECT_TRUE(window_state->IsMaximized());
534  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
535}
536
537// Dock two windows, undock one, check that the other one is still docked.
538TEST_P(DockedWindowResizerTest, AttachTwoWindows) {
539  if (!SupportsHostWindowResize())
540    return;
541  UpdateDisplay("600x600");
542
543  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
544  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
545  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
546  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 50);
547
548  // Docking second window should not minimize the first.
549  wm::WindowState* window_state1 = wm::GetWindowState(w1.get());
550  EXPECT_FALSE(window_state1->IsMinimized());
551
552  // Both windows should be docked at the right edge.
553  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
554            w1->GetBoundsInScreen().right());
555  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
556
557  EXPECT_EQ(w2->GetRootWindow()->GetBoundsInScreen().right(),
558            w2->GetBoundsInScreen().right());
559  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
560
561  // Detach by dragging left (should get undocked).
562  const int left_edge = w2->bounds().x();
563  ASSERT_NO_FATAL_FAILURE(DragStart(w2.get()));
564  // Drag up as well to avoid attaching panels to launcher shelf.
565  DragMove(-32, -100);
566  // Release the mouse and the window should be no longer attached to the edge.
567  DragEnd();
568
569  // The first window should be still docked.
570  EXPECT_FALSE(window_state1->IsMinimized());
571  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
572            w1->GetBoundsInScreen().right());
573  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
574
575  // The window should be floating on the desktop again and moved to the left.
576  EXPECT_EQ(left_edge - 32, w2->GetBoundsInScreen().x());
577  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
578}
579
580// Create two windows, dock one and change shelf to auto-hide.
581TEST_P(DockedWindowResizerTest, AttachOneAutoHideShelf) {
582  if (!SupportsHostWindowResize())
583    return;
584
585  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
586  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
587
588  // w1 should be docked at the right edge.
589  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
590            w1->GetBoundsInScreen().right());
591  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
592
593  scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegateAndType(
594      NULL, ui::wm::WINDOW_TYPE_NORMAL, 0, gfx::Rect(20, 20, 150, 20)));
595  wm::GetWindowState(w2.get())->Maximize();
596  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
597  EXPECT_TRUE(wm::GetWindowState(w2.get())->IsMaximized());
598
599  gfx::Rect work_area =
600      Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
601  DockedWindowLayoutManager* manager =
602      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
603
604  // Docked window should be centered vertically in the work area.
605  EXPECT_EQ(work_area.CenterPoint().y(), w1->bounds().CenterPoint().y());
606  // Docked background should extend to the bottom of work area.
607  EXPECT_EQ(work_area.bottom(), manager->docked_bounds().bottom());
608
609  // set launcher shelf to be aligned on the right
610  ash::Shell* shell = ash::Shell::GetInstance();
611  shell->SetShelfAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS,
612                                  shell->GetPrimaryRootWindow());
613  work_area =
614        Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
615  // Docked window should be centered vertically in the work area.
616  EXPECT_EQ(work_area.CenterPoint().y(), w1->bounds().CenterPoint().y());
617  // Docked background should extend to the bottom of work area.
618  EXPECT_EQ(work_area.bottom(), manager->docked_bounds().bottom());
619}
620
621// Dock one window, try to dock another window on the opposite side (should not
622// dock).
623TEST_P(DockedWindowResizerTest, AttachOnTwoSides) {
624  if (!SupportsHostWindowResize())
625    return;
626
627  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
628  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
629  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
630  gfx::Rect initial_bounds(w2->bounds());
631  DragToVerticalPositionAndToEdge(DOCKED_EDGE_LEFT, w2.get(), 50);
632
633  // The first window should be docked at the right edge.
634  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
635            w1->GetBoundsInScreen().right());
636  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
637
638  // The second window should be near the left edge but not snapped.
639  // Normal window will get side-maximized while panels will not.
640  int expected_x = test_panels() ?
641      (initial_bounds.x() - initial_location_in_parent().x()) : 0;
642  EXPECT_EQ(expected_x, w2->GetBoundsInScreen().x());
643  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
644}
645
646// Tests that reverting a drag restores docked state if a window was docked.
647TEST_P(DockedWindowResizerTest, RevertDragRestoresAttachment) {
648  if (!SupportsHostWindowResize())
649    return;
650
651  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
652  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
653
654  // The window should be docked at the right edge.
655  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
656            window->GetBoundsInScreen().right());
657  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
658
659  // Drag the window out but revert the drag
660  ASSERT_NO_FATAL_FAILURE(DragStart(window.get()));
661  DragMove(-50, 0);
662  DragRevert();
663  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
664
665  // Detach window.
666  ASSERT_NO_FATAL_FAILURE(DragStart(window.get()));
667  DragMove(-50, 0);
668  DragEnd();
669  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
670}
671
672// Tests that reverting drag restores undocked state if a window was not docked.
673TEST_P(DockedWindowResizerTest, RevertDockedDragRevertsAttachment) {
674  if (!SupportsHostWindowResize())
675    return;
676  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
677  aura::Window* dock_container = Shell::GetContainer(
678      window->GetRootWindow(),
679      kShellWindowId_DockedContainer);
680  DockedWindowLayoutManager* manager =
681      static_cast<DockedWindowLayoutManager*>(dock_container->layout_manager());
682  int previous_container_id = window->parent()->id();
683  // Drag the window out but revert the drag
684  ASSERT_NO_FATAL_FAILURE(DragStart(window.get()));
685  DragMove(-50 - window->bounds().x(), 50 - window->bounds().y());
686  EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
687  DragRevert();
688  EXPECT_EQ(previous_container_id, window->parent()->id());
689  EXPECT_EQ(DOCKED_ALIGNMENT_NONE, docked_alignment(manager));
690
691  // Drag a window to the left so that it overlaps the screen edge.
692  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(
693      window.get(),
694      window->bounds().width()/2 + 10,
695      0));
696  DragMove(-50 - window->bounds().x(), 50 - window->bounds().y());
697  DragEnd();
698  // The window now overlaps the left screen edge but is not docked.
699  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
700  EXPECT_EQ(DOCKED_ALIGNMENT_NONE, docked_alignment(manager));
701  EXPECT_LT(window->bounds().x(), 0);
702  EXPECT_GT(window->bounds().right(), 0);
703
704  // Drag the window further left and revert the drag.
705  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(
706      window.get(),
707      window->bounds().width()/2 + 10,
708      0));
709  DragMove(-10, 10);
710  DragRevert();
711  // The window should be in default container and not docked.
712  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
713  // Docked area alignment should be cleared.
714  EXPECT_EQ(DOCKED_ALIGNMENT_NONE, docked_alignment(manager));
715}
716
717// Move a docked window to the second display
718TEST_P(DockedWindowResizerTest, DragAcrossDisplays) {
719  if (!SupportsMultipleDisplays())
720    return;
721
722  UpdateDisplay("800x800,800x800");
723  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
724  EXPECT_EQ(2, static_cast<int>(root_windows.size()));
725  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
726  gfx::Rect initial_bounds = window->GetBoundsInScreen();
727  EXPECT_EQ(root_windows[0], window->GetRootWindow());
728
729  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
730  // The window should be docked at the right edge.
731  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
732            window->GetBoundsInScreen().right());
733  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
734
735  // Try dragging to the right - enough to get it peeking at the other screen
736  // but not enough to land in the other screen.
737  // The window should stay on the left screen.
738  ASSERT_NO_FATAL_FAILURE(DragStart(window.get()));
739  DragMove(100, 0);
740  EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
741  DragEnd();
742  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
743            window->GetBoundsInScreen().right());
744  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
745  EXPECT_EQ(root_windows[0], window->GetRootWindow());
746
747  // Undock and move to the right - enough to get the mouse pointer past the
748  // edge of the screen and into the second screen. The window should now be
749  // in the second screen and not docked.
750  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(
751      window.get(),
752      window->bounds().width()/2 + 10,
753      0));
754  DragMove(window->bounds().width()/2 - 5, 0);
755  EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
756  DragEnd();
757  EXPECT_NE(window->GetRootWindow()->GetBoundsInScreen().right(),
758            window->GetBoundsInScreen().right());
759  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
760  EXPECT_EQ(root_windows[1], window->GetRootWindow());
761
762  // Keep dragging it to the right until its left edge touches the screen edge.
763  // The window should now be in the second screen and not docked.
764  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(
765      window.get(),
766      window->bounds().width()/2 + 10,
767      0));
768  DragMove(window->GetRootWindow()->GetBoundsInScreen().x() -
769           window->GetBoundsInScreen().x(),
770           0);
771  EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
772  DragEnd();
773  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().x(),
774            window->GetBoundsInScreen().x());
775  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
776  EXPECT_EQ(root_windows[1], window->GetRootWindow());
777}
778
779// Dock two windows, undock one.
780// Test the docked windows area size and default container resizing.
781TEST_P(DockedWindowResizerTest, AttachTwoWindowsDetachOne) {
782  if (!SupportsHostWindowResize())
783    return;
784  UpdateDisplay("600x600");
785
786  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
787  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 201)));
788  // Work area should cover the whole screen.
789  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width(),
790            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
791
792  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
793  // A window should be docked at the right edge.
794  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
795            w1->GetBoundsInScreen().right());
796  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
797  DockedWindowLayoutManager* manager =
798      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
799  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
800  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
801
802  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100);
803  // Both windows should now be docked at the right edge.
804  EXPECT_EQ(w2->GetRootWindow()->GetBoundsInScreen().right(),
805            w2->GetBoundsInScreen().right());
806  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
807  // Dock width should be set to a wider window.
808  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
809  EXPECT_EQ(std::max(w1->bounds().width(), w2->bounds().width()),
810            docked_width(manager));
811
812  // Try to detach by dragging left a bit (should not get undocked).
813  // This would normally detach a single docked window but since we have another
814  // window and the mouse pointer does not leave the dock area the window
815  // should stay docked.
816  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(w2.get(), 60, 0));
817  // Drag up as well as left to avoid attaching panels to launcher shelf.
818  DragMove(-40, -40);
819  // Release the mouse and the window should be still attached to the edge.
820  DragEnd();
821
822  // The first window should be still docked.
823  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
824            w1->GetBoundsInScreen().right());
825  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
826
827  // The second window should be still docked.
828  EXPECT_EQ(w2->GetRootWindow()->GetBoundsInScreen().right(),
829            w2->GetBoundsInScreen().right());
830  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
831
832  // Detach by dragging left more (should get undocked).
833  const int left_edge = w2->bounds().x();
834  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(
835      w2.get(),
836      w2->bounds().width()/2 + 10,
837      0));
838  // Drag up as well to avoid attaching panels to launcher shelf.
839  const int drag_x = -(w2->bounds().width()/2 + 20);
840  DragMove(drag_x, -100);
841  // Release the mouse and the window should be no longer attached to the edge.
842  DragEnd();
843
844  // The second window should be floating on the desktop again.
845  EXPECT_EQ(left_edge + drag_x, w2->bounds().x());
846  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
847  // Dock width should be set to remaining single docked window.
848  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
849  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
850  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
851}
852
853// Dock one of the windows. Maximize other testing desktop resizing.
854TEST_P(DockedWindowResizerTest, AttachWindowMaximizeOther) {
855  if (!SupportsHostWindowResize())
856    return;
857
858  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
859  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 201)));
860  // Work area should cover the whole screen.
861  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width(),
862            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
863
864  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
865  // A window should be docked at the right edge.
866  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
867            w1->GetBoundsInScreen().right());
868  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
869  DockedWindowLayoutManager* manager =
870      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
871  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
872  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
873
874  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(w2.get(), 25, 5));
875  DragMove(w2->GetRootWindow()->bounds().width()
876           -w2->bounds().width()
877           -(w2->bounds().width()/2 + 20)
878           -w2->bounds().x(),
879           50 - w2->bounds().y());
880  DragEnd();
881  // The first window should be still docked.
882  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
883            w1->GetBoundsInScreen().right());
884  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
885
886  // The second window should be floating on the desktop.
887  EXPECT_EQ(w2->GetRootWindow()->GetBoundsInScreen().right() -
888            (w2->bounds().width()/2 + 20),
889            w2->GetBoundsInScreen().right());
890  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
891  // Dock width should be set to remaining single docked window.
892  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
893  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
894  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
895  // Desktop work area should now shrink.
896  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width() -
897            docked_width(manager) - min_dock_gap(),
898            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
899
900  // Maximize the second window - Maximized area should be shrunk.
901  const gfx::Rect restored_bounds = w2->bounds();
902  wm::WindowState* w2_state = wm::GetWindowState(w2.get());
903  w2_state->Maximize();
904  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width() -
905            docked_width(manager) - min_dock_gap(),
906            w2->bounds().width());
907
908  // Detach the first window (this should require very little drag).
909  ASSERT_NO_FATAL_FAILURE(DragStart(w1.get()));
910  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
911  DragMove(-35, 10);
912  // Alignment is set to "NONE" when drag starts.
913  EXPECT_EQ(DOCKED_ALIGNMENT_NONE, docked_alignment(manager));
914  // Release the mouse and the window should be no longer attached to the edge.
915  DragEnd();
916  EXPECT_EQ(DOCKED_ALIGNMENT_NONE, docked_alignment(manager));
917  // Dock should get shrunk and desktop should get expanded.
918  EXPECT_EQ(kShellWindowId_DefaultContainer, w1->parent()->id());
919  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
920  EXPECT_EQ(DOCKED_ALIGNMENT_NONE, docked_alignment(manager));
921  EXPECT_EQ(0, docked_width(manager));
922  // The second window should now get resized and take up the whole screen.
923  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width(),
924            w2->bounds().width());
925
926  // Dock the first window to the left edge.
927  // Click at an offset from origin to prevent snapping.
928  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(w1.get(), 10, 0));
929  // Drag left to get pointer touching the screen edge.
930  DragMove(-w1->bounds().x() - 10, 0);
931  // Alignment set to "NONE" during the drag of the window when none are docked.
932  EXPECT_EQ(DOCKED_ALIGNMENT_NONE, docked_alignment(manager));
933  // Release the mouse and the window should be now attached to the edge.
934  DragEnd();
935  // Dock should get expanded and desktop should get shrunk.
936  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
937  EXPECT_EQ(DOCKED_ALIGNMENT_LEFT, docked_alignment(manager));
938  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
939  // Second window should still be in the desktop.
940  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
941  // Maximized window should be shrunk.
942  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width() -
943            docked_width(manager) - min_dock_gap(),
944            w2->bounds().width());
945
946  // Unmaximize the second window.
947  w2_state->Restore();
948  // Its bounds should get restored.
949  EXPECT_EQ(restored_bounds, w2->bounds());
950}
951
952// Dock one window. Test the sticky behavior near screen or desktop edge.
953TEST_P(DockedWindowResizerTest, AttachOneTestSticky) {
954  if (!SupportsHostWindowResize())
955    return;
956
957  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
958  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 201)));
959  // Work area should cover the whole screen.
960  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width(),
961            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
962
963  DragToVerticalPositionAndToEdge(DOCKED_EDGE_LEFT, w1.get(), 20);
964  // A window should be docked at the left edge.
965  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().x(),
966            w1->GetBoundsInScreen().x());
967  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
968  DockedWindowLayoutManager* manager =
969      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
970  // The first window should be docked.
971  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().x(),
972            w1->GetBoundsInScreen().x());
973  // Dock width should be set to that of a single docked window.
974  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
975  EXPECT_EQ(DOCKED_ALIGNMENT_LEFT, docked_alignment(manager));
976  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
977
978  // Position second window in the desktop 20px to the right of the docked w1.
979  DragToVerticalPositionRelativeToEdge(DOCKED_EDGE_LEFT,
980                                       w2.get(),
981                                       20 + 25 -
982                                       min_dock_gap(),
983                                       50);
984  // The second window should be floating on the desktop.
985  EXPECT_EQ(w2->GetRootWindow()->GetBoundsInScreen().x() +
986                (w1->bounds().right() + 20),
987            w2->GetBoundsInScreen().x());
988  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
989  // Dock width should be set to that of a single docked window.
990  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
991  EXPECT_EQ(DOCKED_ALIGNMENT_LEFT, docked_alignment(manager));
992  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
993
994  // Drag w2 almost to the dock, the mouse pointer not quite reaching the dock.
995  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(w2.get(), 10, 0));
996  DragMove(1 + docked_width(manager) - w2->bounds().x(), 0);
997  // Alignment set to "LEFT" during the drag because dock has a window in it.
998  EXPECT_EQ(DOCKED_ALIGNMENT_LEFT, docked_alignment(manager));
999  // Release the mouse and the window should not be attached to the edge.
1000  DragEnd();
1001  // Dock should still have only one window in it.
1002  EXPECT_EQ(DOCKED_ALIGNMENT_LEFT, docked_alignment(manager));
1003  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1004  // The second window should still be in the desktop.
1005  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1006  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
1007
1008  // Drag w2 by a bit more - it should resist the drag (stuck edges)
1009  int start_x = w2->bounds().x();
1010  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromWindowOrigin(w2.get(), 100, 5));
1011  DragMove(-2, 0);
1012  // Window should not actually move.
1013  EXPECT_EQ(start_x, w2->bounds().x());
1014  // Alignment set to "LEFT" during the drag because dock has a window in it.
1015  EXPECT_EQ(DOCKED_ALIGNMENT_LEFT, docked_alignment(manager));
1016  // Release the mouse and the window should not be attached to the edge.
1017  DragEnd();
1018  // Window should be still where it was before the last drag started.
1019  EXPECT_EQ(start_x, w2->bounds().x());
1020  // Dock should still have only one window in it
1021  EXPECT_EQ(DOCKED_ALIGNMENT_LEFT, docked_alignment(manager));
1022  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1023  // The second window should still be in the desktop
1024  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1025  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
1026
1027  // Drag w2 by more than the stuck threshold and drop it into the dock.
1028  ASSERT_NO_FATAL_FAILURE(DragStart(w2.get()));
1029  DragMove(-100, 0);
1030  // Window should actually move.
1031  EXPECT_NE(start_x, w2->bounds().x());
1032  // Alignment set to "LEFT" during the drag because dock has a window in it.
1033  EXPECT_EQ(DOCKED_ALIGNMENT_LEFT, docked_alignment(manager));
1034  // Release the mouse and the window should be attached to the edge.
1035  DragEnd();
1036  // Both windows are docked now.
1037  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1038  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
1039  // Dock should get expanded and desktop should get shrunk.
1040  EXPECT_EQ(DOCKED_ALIGNMENT_LEFT, docked_alignment(manager));
1041  EXPECT_EQ(std::max(w1->bounds().width(), w2->bounds().width()),
1042            docked_width(manager));
1043  // Desktop work area should now shrink by dock width.
1044  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width() -
1045            docked_width(manager) - min_dock_gap(),
1046            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
1047}
1048
1049// Dock two windows, resize one.
1050// Test the docked windows area size and remaining desktop resizing.
1051TEST_P(DockedWindowResizerTest, ResizeOneOfTwoWindows) {
1052  if (!SupportsHostWindowResize())
1053    return;
1054
1055  // Wider display to start since panels are limited to half the display width.
1056  UpdateDisplay("1000x600");
1057  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
1058  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 201)));
1059  // Work area should cover the whole screen.
1060  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width(),
1061            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
1062
1063  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
1064  // A window should be docked at the right edge.
1065  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
1066            w1->GetBoundsInScreen().right());
1067  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1068  DockedWindowLayoutManager* manager =
1069      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
1070  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1071  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1072
1073  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100);
1074  // Both windows should now be docked at the right edge.
1075  EXPECT_EQ(w2->GetRootWindow()->GetBoundsInScreen().right(),
1076            w2->GetBoundsInScreen().right());
1077  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
1078  // Dock width should be set to a wider window.
1079  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1080  EXPECT_EQ(std::max(w1->bounds().width(), w2->bounds().width()),
1081            docked_width(manager));
1082
1083  // Resize the first window left by a bit and test that the dock expands.
1084  int previous_width = w1->bounds().width();
1085  const int kResizeSpan1 = 30;
1086  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(w1.get(),
1087                                                              0, 20,
1088                                                              HTLEFT));
1089  DragMove(-kResizeSpan1, 0);
1090  // Alignment set to "RIGHT" during the drag because dock has a window in it.
1091  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1092  // Release the mouse and the window should be attached to the edge.
1093  DragEnd();
1094  // Dock should still have both windows in it.
1095  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1096  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
1097  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1098  // w1 is now wider than before. The dock should expand and be as wide as w1.
1099  EXPECT_EQ(previous_width + kResizeSpan1, w1->bounds().width());
1100  // Both windows should get resized since they both don't have min/max size.
1101  EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
1102  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1103  // Desktop work area should shrink.
1104  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width() -
1105            docked_width(manager) - min_dock_gap(),
1106            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
1107
1108  // Resize the first window left by more than the dock maximum width.
1109  // This should cause the window width to be restricted by maximum dock width.
1110  previous_width = w1->bounds().width();
1111  const int kResizeSpan2 = 250;
1112  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(w1.get(),
1113                                                              0, 20,
1114                                                              HTLEFT));
1115  DragMove(-kResizeSpan2, 0);
1116  // Alignment set to "RIGHT" during the drag because dock has a window in it.
1117  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1118  // Release the mouse and the window should be attached to the edge.
1119  DragEnd();
1120  // Dock should still have both windows in it.
1121  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1122  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
1123  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1124  // w1 is now as wide as the maximum dock width and the dock should get
1125  // resized to the maximum width.
1126  EXPECT_EQ(max_width(), w1->bounds().width());
1127  // Both windows should get resized since they both don't have min/max size.
1128  EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
1129  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1130  // Desktop work area should shrink.
1131  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width() -
1132            docked_width(manager) - min_dock_gap(),
1133            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
1134
1135  // Resize the first window right to get it completely inside the docked area.
1136  previous_width = w1->bounds().width();
1137  const int kResizeSpan3 = 100;
1138  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(w1.get(),
1139                                                              0, 20,
1140                                                              HTLEFT));
1141  DragMove(kResizeSpan3, 0);
1142  // Alignment set to "RIGHT" during the drag because dock has a window in it.
1143  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1144  // Release the mouse and the window should be docked.
1145  DragEnd();
1146  // Dock should still have both windows in it.
1147  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1148  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
1149  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1150  // w1 should be narrower than before by the length of the drag.
1151  EXPECT_EQ(previous_width - kResizeSpan3, w1->bounds().width());
1152  // Both windows should get resized since they both don't have min/max size.
1153  EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
1154  // The dock should be as wide as w1 or w2.
1155  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1156  // Desktop work area should shrink.
1157  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w2.get()).width() -
1158            docked_width(manager) - min_dock_gap(),
1159            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
1160
1161  // Resize the first window left to be overhang again.
1162  previous_width = w1->bounds().width();
1163  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(w1.get(),
1164                                                              0, 20,
1165                                                              HTLEFT));
1166  DragMove(-kResizeSpan3, 0);
1167  DragEnd();
1168  EXPECT_EQ(previous_width + kResizeSpan3, w1->bounds().width());
1169  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1170  // Docked area should be as wide as possible (maximum) and same as w1.
1171  EXPECT_EQ(max_width(), docked_width(manager));
1172  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1173
1174  // Undock the first window. Docked area should shrink to its ideal size.
1175  ASSERT_NO_FATAL_FAILURE(DragStart(w1.get()));
1176  // Drag up as well to avoid attaching panels to launcher shelf.
1177  DragMove(-(400 - 210), -100);
1178  // Alignment set to "RIGHT" since we have another window docked.
1179  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1180  // Release the mouse and the window should be no longer attached to the edge.
1181  DragEnd();
1182  EXPECT_EQ(kShellWindowId_DefaultContainer, w1->parent()->id());
1183  // Dock should be as wide as w2 (and same as ideal width).
1184  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1185  EXPECT_EQ(ideal_width(), docked_width(manager));
1186  EXPECT_EQ(w2->bounds().width(), docked_width(manager));
1187  // The second window should be still docked.
1188  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
1189  // Desktop work area should be inset.
1190  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w1.get()).width() -
1191            docked_width(manager) - min_dock_gap(),
1192            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w1.get()).width());
1193}
1194
1195// Dock a window, resize it and test that undocking it preserves the width.
1196TEST_P(DockedWindowResizerTest, ResizingKeepsWidth) {
1197  if (!SupportsHostWindowResize())
1198    return;
1199
1200  // Wider display to start since panels are limited to half the display width.
1201  UpdateDisplay("1000x600");
1202  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
1203
1204  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
1205  // Window should be docked at the right edge.
1206  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
1207            w1->GetBoundsInScreen().right());
1208  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1209  DockedWindowLayoutManager* manager =
1210      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
1211  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1212  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1213
1214  // Resize the window left by a bit and test that the dock expands.
1215  int previous_width = w1->bounds().width();
1216  const int kResizeSpan1 = 30;
1217  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(w1.get(),
1218                                                              0, 20,
1219                                                              HTLEFT));
1220  DragMove(-kResizeSpan1, 0);
1221  // Alignment stays "RIGHT" during the drag because the only docked window
1222  // is being resized.
1223  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1224  // Release the mouse and the window should be attached to the edge.
1225  DragEnd();
1226  // The window should get docked.
1227  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1228  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1229  // w1 is now wider and the dock should expand to be as wide as w1.
1230  EXPECT_EQ(previous_width + kResizeSpan1, w1->bounds().width());
1231  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1232
1233  // Undock by dragging almost to the left edge.
1234  DragToVerticalPositionRelativeToEdge(DOCKED_EDGE_LEFT, w1.get(), 100, 20);
1235  // Width should be preserved.
1236  EXPECT_EQ(previous_width + kResizeSpan1, w1->bounds().width());
1237  // Height should be restored to what it was originally.
1238  EXPECT_EQ(201, w1->bounds().height());
1239
1240  // Dock again.
1241  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
1242  // Width should be reset to initial ideal width (25px).
1243  EXPECT_EQ(ideal_width(), w1->bounds().width());
1244
1245  // Undock again by dragging left.
1246  DragToVerticalPositionRelativeToEdge(DOCKED_EDGE_LEFT, w1.get(), 100, 20);
1247  // Width should be reset to what it was last time the window was not docked.
1248  EXPECT_EQ(previous_width + kResizeSpan1, w1->bounds().width());
1249  // Height should be restored to what it was originally.
1250  EXPECT_EQ(201, w1->bounds().height());
1251}
1252
1253// Dock a window, resize it and test that it stays docked.
1254TEST_P(DockedWindowResizerTest, ResizingKeepsDockedState) {
1255  if (!SupportsHostWindowResize())
1256    return;
1257
1258  // Wider display to start since panels are limited to half the display width.
1259  UpdateDisplay("1000x600");
1260  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
1261
1262  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
1263  // Window should be docked at the right edge.
1264  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
1265            w1->GetBoundsInScreen().right());
1266  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1267  DockedWindowLayoutManager* manager =
1268      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
1269  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1270  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1271
1272  // Resize the window left by a bit and test that the dock expands.
1273  int previous_width = w1->bounds().width();
1274  const int kResizeSpan1 = 30;
1275  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(
1276      w1.get(), 0, 20, HTLEFT));
1277  DragMove(-kResizeSpan1, 0);
1278  // Normally alignment would be reset to "NONE" during the drag when there is
1279  // only a single window docked and it is being dragged. However because that
1280  // window is being resized rather than moved the alignment is not changed.
1281  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1282  // Release the mouse and the window should be attached to the edge.
1283  DragEnd();
1284  // The window should stay docked.
1285  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1286  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1287  // w1 is now wider and the dock should expand to be as wide as w1.
1288  EXPECT_EQ(previous_width + kResizeSpan1, w1->bounds().width());
1289  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1290
1291  // Resize the window by dragging its right edge left a bit and test that the
1292  // window stays docked.
1293  previous_width = w1->bounds().width();
1294  const int kResizeSpan2 = 15;
1295  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(
1296      w1.get(), w1->bounds().width(), 20, HTRIGHT));
1297  DragMove(-kResizeSpan2, 0);
1298  // Alignment stays "RIGHT" during the drag because the window is being
1299  // resized rather than dragged.
1300  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1301  // Release the mouse and the window should be attached to the edge.
1302  DragEnd();
1303  // The window should stay docked.
1304  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1305  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1306  // The dock should stay as wide as w1 is now (a bit less than before).
1307  EXPECT_EQ(previous_width - kResizeSpan2, w1->bounds().width());
1308  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1309}
1310
1311// Dock two windows, resize one. Test the docked windows area size.
1312TEST_P(DockedWindowResizerTest, ResizeTwoWindows) {
1313  if (!SupportsHostWindowResize())
1314    return;
1315
1316  // Wider display to start since panels are limited to half the display width.
1317  UpdateDisplay("1000x600");
1318  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
1319  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 201)));
1320
1321  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
1322  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100);
1323  // Both windows should now be docked at the right edge.
1324  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1325  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
1326  // Dock width should be set to ideal width.
1327  DockedWindowLayoutManager* manager =
1328      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
1329  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1330  EXPECT_EQ(ideal_width(), docked_width(manager));
1331
1332  // Resize the first window left by a bit and test that the dock expands.
1333  int previous_width = w1->bounds().width();
1334  const int kResizeSpan1 = 30;
1335  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(w1.get(),
1336                                                              0, 20,
1337                                                              HTLEFT));
1338  DragMove(-kResizeSpan1, 0);
1339  DragEnd();
1340  // w1 is now wider than before.
1341  EXPECT_EQ(previous_width + kResizeSpan1, w1->bounds().width());
1342  // Both windows should get resized since they both don't have min/max size.
1343  EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
1344  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1345
1346  // Resize the second window left by a bit more and test that the dock expands.
1347  previous_width = w2->bounds().width();
1348  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(w2.get(),
1349                                                              0, 20,
1350                                                              HTLEFT));
1351  DragMove(-kResizeSpan1, 0);
1352  DragEnd();
1353  // w2 should get wider since it was resized by a user.
1354  EXPECT_EQ(previous_width + kResizeSpan1, w2->bounds().width());
1355  // w1 should stay as wide as w2 since both were flush with the dock edge.
1356  EXPECT_EQ(w2->bounds().width(), w1->bounds().width());
1357  EXPECT_EQ(w2->bounds().width(), docked_width(manager));
1358
1359  // Undock w2 and then dock it back.
1360  DragToVerticalPositionRelativeToEdge(DOCKED_EDGE_RIGHT, w2.get(), -400, 100);
1361  EXPECT_EQ(kShellWindowId_DefaultContainer, w2->parent()->id());
1362  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100);
1363  EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
1364  // w2 should become same width as w1.
1365  EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
1366  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1367
1368  // Make w1 even wider.
1369  ASSERT_NO_FATAL_FAILURE(ResizeStartAtOffsetFromWindowOrigin(w1.get(),
1370                                                              0, 20,
1371                                                              HTLEFT));
1372  DragMove(-kResizeSpan1, 0);
1373  DragEnd();
1374  // Making w1 wider should make both windows wider since w2 no longer remembers
1375  // user width.
1376  EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
1377  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1378}
1379
1380// Tests that dragging a window down to shelf attaches a panel but does not
1381// attach a regular window.
1382TEST_P(DockedWindowResizerTest, DragToShelf) {
1383  if (!SupportsHostWindowResize())
1384    return;
1385
1386  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
1387  // Work area should cover the whole screen.
1388  EXPECT_EQ(ScreenUtil::GetDisplayBoundsInParent(w1.get()).width(),
1389            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w1.get()).width());
1390
1391  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
1392  // A window should be docked at the right edge.
1393  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
1394            w1->GetBoundsInScreen().right());
1395  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1396  DockedWindowLayoutManager* manager =
1397      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
1398  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1399  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1400
1401  // Detach and drag down to shelf.
1402  ASSERT_NO_FATAL_FAILURE(DragStart(w1.get()));
1403  DragMove(-40, 0);
1404  // Alignment is set to "NONE" when drag starts.
1405  EXPECT_EQ(DOCKED_ALIGNMENT_NONE, docked_alignment(manager));
1406  // Release the mouse and the window should be no longer attached to the edge.
1407  DragEnd();
1408  EXPECT_EQ(DOCKED_ALIGNMENT_NONE, docked_alignment(manager));
1409
1410  // Drag down almost to shelf. A panel will snap, a regular window won't.
1411  ShelfWidget* shelf = Shelf::ForPrimaryDisplay()->shelf_widget();
1412  const int shelf_y = shelf->GetWindowBoundsInScreen().y();
1413  const int kDistanceFromShelf = 10;
1414  ASSERT_NO_FATAL_FAILURE(DragStart(w1.get()));
1415  DragMove(0, -kDistanceFromShelf + shelf_y - w1->bounds().bottom());
1416  DragEnd();
1417  if (test_panels()) {
1418    // The panel should be touching the shelf and attached.
1419    EXPECT_EQ(shelf_y, w1->bounds().bottom());
1420    EXPECT_TRUE(wm::GetWindowState(w1.get())->panel_attached());
1421  } else {
1422    // The window should not be touching the shelf.
1423    EXPECT_EQ(shelf_y - kDistanceFromShelf, w1->bounds().bottom());
1424  }
1425}
1426
1427// Tests that docking and undocking a |window| with a transient child properly
1428// maintains the parent of that transient child to be the same as the |window|.
1429TEST_P(DockedWindowResizerTest, DragWindowWithTransientChild) {
1430  if (!SupportsHostWindowResize())
1431    return;
1432
1433  // Create a window with a transient child.
1434  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
1435  scoped_ptr<aura::Window> child(CreateTestWindowInShellWithDelegateAndType(
1436      NULL, ui::wm::WINDOW_TYPE_NORMAL, 0, gfx::Rect(20, 20, 150, 20)));
1437  ::wm::AddTransientChild(window.get(), child.get());
1438  if (window->parent() != child->parent())
1439    window->parent()->AddChild(child.get());
1440  EXPECT_EQ(window.get(), ::wm::GetTransientParent(child.get()));
1441
1442  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, window.get(), 20);
1443
1444  // A window should be docked at the right edge.
1445  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
1446  EXPECT_EQ(kShellWindowId_DockedContainer, child->parent()->id());
1447
1448  // Drag the child - it should move freely and stay where it is dragged.
1449  ASSERT_NO_FATAL_FAILURE(DragStart(child.get()));
1450  DragMove(500, 20);
1451  DragEnd();
1452  EXPECT_EQ(gfx::Point(20 + 500, 20 + 20).ToString(),
1453            child->GetBoundsInScreen().origin().ToString());
1454
1455  // Undock the window by dragging left.
1456  ASSERT_NO_FATAL_FAILURE(DragStart(window.get()));
1457  DragMove(-32, -10);
1458  DragEnd();
1459
1460  // The window should be undocked and the transient child should be reparented.
1461  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
1462  EXPECT_EQ(kShellWindowId_DefaultContainer, child->parent()->id());
1463  // The child should not have moved.
1464  EXPECT_EQ(gfx::Point(20 + 500, 20 + 20).ToString(),
1465            child->GetBoundsInScreen().origin().ToString());
1466}
1467
1468// Tests that reparenting windows during the drag does not affect system modal
1469// windows that are transient children of the dragged windows.
1470TEST_P(DockedWindowResizerTest, DragWindowWithModalTransientChild) {
1471  if (!SupportsHostWindowResize())
1472    return;
1473
1474  // Create a window.
1475  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
1476  gfx::Rect bounds(window->bounds());
1477
1478  // Start dragging the window.
1479  ASSERT_NO_FATAL_FAILURE(DragStart(window.get()));
1480  gfx::Vector2d move_vector(40, test_panels() ? -60 : 60);
1481  DragMove(move_vector.x(), move_vector.y());
1482  EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
1483
1484  // While still dragging create a modal window and make it a transient child of
1485  // the |window|.
1486  scoped_ptr<aura::Window> child(CreateModalWindow(gfx::Rect(20, 20, 150, 20)));
1487  ::wm::AddTransientChild(window.get(), child.get());
1488  EXPECT_EQ(window.get(), ::wm::GetTransientParent(child.get()));
1489  EXPECT_EQ(kShellWindowId_SystemModalContainer, child->parent()->id());
1490
1491  // End the drag, the |window| should have moved (if it is a panel it will
1492  // no longer be attached to the shelf since we dragged it above).
1493  DragEnd();
1494  bounds.Offset(move_vector);
1495  EXPECT_EQ(bounds.ToString(), window->GetBoundsInScreen().ToString());
1496
1497  // The original |window| should be in the default container (not docked or
1498  // attached).
1499  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
1500  // The transient |child| should still be in system modal container.
1501  EXPECT_EQ(kShellWindowId_SystemModalContainer, child->parent()->id());
1502  // The |child| should not have moved.
1503  EXPECT_EQ(gfx::Point(20, 20).ToString(),
1504            child->GetBoundsInScreen().origin().ToString());
1505  // The |child| should still be a transient child of |window|.
1506  EXPECT_EQ(window.get(), ::wm::GetTransientParent(child.get()));
1507}
1508
1509// Tests that side snapping a window undocks it, closes the dock and then snaps.
1510TEST_P(DockedWindowResizerTest, SideSnapDocked) {
1511  if (!SupportsHostWindowResize() || test_panels())
1512    return;
1513
1514  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
1515  wm::WindowState* window_state = wm::GetWindowState(w1.get());
1516  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
1517  // A window should be docked at the right edge.
1518  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
1519            w1->GetBoundsInScreen().right());
1520  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1521  DockedWindowLayoutManager* manager =
1522      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
1523  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1524  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1525  EXPECT_TRUE(window_state->IsDocked());
1526  EXPECT_FALSE(window_state->IsSnapped());
1527
1528  // Side snap at right edge.
1529  const wm::WMEvent snap_right(wm::WM_EVENT_SNAP_RIGHT);
1530  window_state->OnWMEvent(&snap_right);
1531  // The window should be snapped at the right edge and the dock should close.
1532  gfx::Rect work_area(ScreenUtil::GetDisplayWorkAreaBoundsInParent(w1.get()));
1533  EXPECT_EQ(0, docked_width(manager));
1534  EXPECT_EQ(work_area.height(), w1->bounds().height());
1535  EXPECT_EQ(work_area.right(), w1->bounds().right());
1536  EXPECT_EQ(kShellWindowId_DefaultContainer, w1->parent()->id());
1537  EXPECT_FALSE(window_state->IsDocked());
1538  EXPECT_TRUE(window_state->IsSnapped());
1539
1540  // Dock again.
1541  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
1542  // A window should be docked at the right edge.
1543  EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
1544            w1->GetBoundsInScreen().right());
1545  EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
1546  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1547  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
1548  EXPECT_TRUE(window_state->IsDocked());
1549  EXPECT_FALSE(window_state->IsSnapped());
1550
1551  // Side snap at left edge.
1552  const wm::WMEvent snap_left(wm::WM_EVENT_SNAP_LEFT);
1553  window_state->OnWMEvent(&snap_left);
1554  // The window should be snapped at the right edge and the dock should close.
1555  EXPECT_EQ(work_area.ToString(),
1556            ScreenUtil::GetDisplayWorkAreaBoundsInParent(w1.get()).ToString());
1557  EXPECT_EQ(0, docked_width(manager));
1558  EXPECT_EQ(work_area.height(), w1->bounds().height());
1559  EXPECT_EQ(work_area.x(), w1->bounds().x());
1560  EXPECT_EQ(kShellWindowId_DefaultContainer, w1->parent()->id());
1561  EXPECT_FALSE(window_state->IsDocked());
1562  EXPECT_TRUE(window_state->IsSnapped());
1563}
1564
1565// Tests that a window is undocked if the window is maximized via a keyboard
1566// accelerator during a drag.
1567TEST_P(DockedWindowResizerTest, MaximizedDuringDrag) {
1568  if (!SupportsHostWindowResize() || test_panels())
1569    return;
1570
1571  scoped_ptr<aura::Window> window(CreateTestWindow(
1572      gfx::Rect(0, 0, ideal_width(), 201)));
1573  wm::WindowState* window_state = wm::GetWindowState(window.get());
1574
1575  // Dock the window to the right edge.
1576  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, window.get(), 20);
1577  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
1578            window->GetBoundsInScreen().right());
1579  EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
1580  DockedWindowLayoutManager* manager =
1581      static_cast<DockedWindowLayoutManager*>(
1582          window->parent()->layout_manager());
1583  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
1584  EXPECT_EQ(window->bounds().width(), docked_width(manager));
1585  EXPECT_TRUE(window_state->IsDocked());
1586
1587  // Maximize the window while in a real drag. In particular,
1588  // ToplevelWindowEventHandler::ScopedWindowResizer::OnWindowStateTypeChanged()
1589  // must be called in order for the maximized window's size to be correct.
1590  delegate()->set_window_component(HTCAPTION);
1591  ui::test::EventGenerator& generator = GetEventGenerator();
1592  generator.MoveMouseTo(window->GetBoundsInScreen().origin());
1593  generator.PressLeftButton();
1594  generator.MoveMouseBy(10, 10);
1595  window_state->Maximize();
1596  generator.ReleaseLeftButton();
1597
1598  // |window| should get undocked.
1599  EXPECT_EQ(kShellWindowId_DefaultContainer, window->parent()->id());
1600  EXPECT_EQ(0, docked_width(manager));
1601  EXPECT_EQ(
1602      ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString(),
1603      window->bounds().ToString());
1604  EXPECT_TRUE(window_state->IsMaximized());
1605}
1606
1607// Tests run twice - on both panels and normal windows
1608INSTANTIATE_TEST_CASE_P(NormalOrPanel,
1609                        DockedWindowResizerTest,
1610                        testing::Values(ui::wm::WINDOW_TYPE_NORMAL,
1611                                        ui::wm::WINDOW_TYPE_PANEL));
1612
1613}  // namespace ash
1614