docked_window_layout_manager_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_layout_manager.h"
6
7#include "ash/ash_switches.h"
8#include "ash/root_window_controller.h"
9#include "ash/shelf/shelf.h"
10#include "ash/shelf/shelf_layout_manager.h"
11#include "ash/shelf/shelf_model.h"
12#include "ash/shelf/shelf_types.h"
13#include "ash/shelf/shelf_widget.h"
14#include "ash/shell.h"
15#include "ash/shell_window_ids.h"
16#include "ash/test/ash_test_base.h"
17#include "ash/test/shelf_test_api.h"
18#include "ash/test/shelf_view_test_api.h"
19#include "ash/test/shell_test_api.h"
20#include "ash/test/test_shelf_delegate.h"
21#include "ash/wm/coordinate_conversion.h"
22#include "ash/wm/panels/panel_layout_manager.h"
23#include "ash/wm/window_resizer.h"
24#include "ash/wm/window_state.h"
25#include "ash/wm/window_util.h"
26#include "base/basictypes.h"
27#include "base/command_line.h"
28#include "base/strings/string_number_conversions.h"
29#include "ui/aura/client/aura_constants.h"
30#include "ui/aura/root_window.h"
31#include "ui/aura/test/test_window_delegate.h"
32#include "ui/aura/window.h"
33#include "ui/base/hit_test.h"
34#include "ui/gfx/screen.h"
35#include "ui/views/widget/widget.h"
36
37namespace ash {
38namespace internal {
39
40class DockedWindowLayoutManagerTest
41    : public test::AshTestBase,
42      public testing::WithParamInterface<ui::wm::WindowType> {
43 public:
44  DockedWindowLayoutManagerTest() : window_type_(GetParam()) {}
45  virtual ~DockedWindowLayoutManagerTest() {}
46
47  virtual void SetUp() OVERRIDE {
48    AshTestBase::SetUp();
49    UpdateDisplay("600x600");
50    ASSERT_TRUE(test::TestShelfDelegate::instance());
51
52    shelf_view_test_.reset(new test::ShelfViewTestAPI(
53        test::ShelfTestAPI(Shelf::ForPrimaryDisplay()).shelf_view()));
54    shelf_view_test_->SetAnimationDuration(1);
55  }
56
57 protected:
58  enum DockedEdge {
59    DOCKED_EDGE_NONE,
60    DOCKED_EDGE_LEFT,
61    DOCKED_EDGE_RIGHT,
62  };
63
64  int min_dock_gap() const { return DockedWindowLayoutManager::kMinDockGap; }
65  int ideal_width() const { return DockedWindowLayoutManager::kIdealWidth; }
66  int docked_width(const DockedWindowLayoutManager* layout_manager) const {
67    return layout_manager->docked_width_;
68  }
69
70  aura::Window* CreateTestWindow(const gfx::Rect& bounds) {
71    aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
72        NULL, window_type_, 0, bounds);
73    if (window_type_ == ui::wm::WINDOW_TYPE_PANEL) {
74      test::TestShelfDelegate* shelf_delegate =
75          test::TestShelfDelegate::instance();
76      shelf_delegate->AddShelfItem(window);
77      PanelLayoutManager* manager =
78          static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
79              layout_manager());
80      manager->Relayout();
81    }
82    return window;
83  }
84
85  aura::Window* CreateTestWindowWithDelegate(
86      const gfx::Rect& bounds,
87      aura::test::TestWindowDelegate* delegate) {
88    aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
89        delegate, window_type_, 0, bounds);
90    if (window_type_ == ui::wm::WINDOW_TYPE_PANEL) {
91      test::TestShelfDelegate* shelf_delegate =
92          test::TestShelfDelegate::instance();
93      shelf_delegate->AddShelfItem(window);
94      PanelLayoutManager* manager =
95          static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
96              layout_manager());
97      manager->Relayout();
98    }
99    return window;
100  }
101
102  aura::Window* GetPanelContainer(aura::Window* panel) {
103    return Shell::GetContainer(panel->GetRootWindow(),
104                               internal::kShellWindowId_PanelContainer);
105  }
106
107  static WindowResizer* CreateSomeWindowResizer(
108      aura::Window* window,
109      const gfx::Point& point_in_parent,
110      int window_component) {
111    return CreateWindowResizer(
112        window,
113        point_in_parent,
114        window_component,
115        aura::client::WINDOW_MOVE_SOURCE_MOUSE).release();
116  }
117
118  void DragStart(aura::Window* window) {
119    DragStartAtOffsetFromwindowOrigin(window, 0, 0);
120  }
121
122  void DragStartAtOffsetFromwindowOrigin(aura::Window* window,
123                                         int dx, int dy) {
124    initial_location_in_parent_ =
125        window->bounds().origin() + gfx::Vector2d(dx, dy);
126    resizer_.reset(CreateSomeWindowResizer(window,
127                                           initial_location_in_parent_,
128                                           HTCAPTION));
129    ASSERT_TRUE(resizer_.get());
130  }
131
132  void DragMove(int dx, int dy) {
133    resizer_->Drag(initial_location_in_parent_ + gfx::Vector2d(dx, dy), 0);
134  }
135
136  void DragEnd() {
137    resizer_->CompleteDrag();
138    resizer_.reset();
139  }
140
141  void DragRevert() {
142    resizer_->RevertDrag();
143    resizer_.reset();
144  }
145
146  // Panels are parented by panel container during drags.
147  // Docked windows are parented by dock container during drags.
148  // All other windows that we are testing here have default container as a
149  // parent.
150  int CorrectContainerIdDuringDrag() {
151    if (window_type_ == ui::wm::WINDOW_TYPE_PANEL)
152      return internal::kShellWindowId_PanelContainer;
153    return internal::kShellWindowId_DockedContainer;
154  }
155
156  // Test dragging the window vertically (to detach if it is a panel) and then
157  // horizontally to the edge with an added offset from the edge of |dx|.
158  void DragRelativeToEdge(DockedEdge edge, aura::Window* window, int dx) {
159    DragVerticallyAndRelativeToEdge(
160        edge,
161        window,
162        dx,
163        window_type_ == ui::wm::WINDOW_TYPE_PANEL ? -100 : 20);
164  }
165
166  void DragToVerticalPositionAndToEdge(DockedEdge edge,
167                                       aura::Window* window,
168                                       int y) {
169    DragToVerticalPositionRelativeToEdge(edge, window, 0, y);
170  }
171
172  void DragToVerticalPositionRelativeToEdge(DockedEdge edge,
173                                            aura::Window* window,
174                                            int dx,
175                                            int y) {
176    gfx::Rect initial_bounds = window->GetBoundsInScreen();
177    DragVerticallyAndRelativeToEdge(edge, window, dx, y - initial_bounds.y());
178  }
179
180  // Detach if our window is a panel, then drag it vertically by |dy| and
181  // horizontally to the edge with an added offset from the edge of |dx|.
182  void DragVerticallyAndRelativeToEdge(DockedEdge edge,
183                                       aura::Window* window,
184                                       int dx, int dy) {
185    gfx::Rect initial_bounds = window->GetBoundsInScreen();
186    // avoid snap by clicking away from the border
187    ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(window, 25, 5));
188
189    gfx::Rect work_area =
190        Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
191    gfx::Point initial_location_in_screen = initial_location_in_parent_;
192    wm::ConvertPointToScreen(window->parent(), &initial_location_in_screen);
193    // Drag the window left or right to the edge (or almost to it).
194    if (edge == DOCKED_EDGE_LEFT)
195      dx += work_area.x() - initial_location_in_screen.x();
196    else if (edge == DOCKED_EDGE_RIGHT)
197      dx += work_area.right() - 1 - initial_location_in_screen.x();
198    DragMove(dx, dy);
199    EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
200    // Release the mouse and the panel should be attached to the dock.
201    DragEnd();
202
203    // x-coordinate can get adjusted by snapping or sticking.
204    // y-coordinate could be changed by possible automatic layout if docked.
205    if (window->parent()->id() != internal::kShellWindowId_DockedContainer &&
206        !wm::GetWindowState(window)->HasRestoreBounds()) {
207      EXPECT_EQ(initial_bounds.y() + dy, window->GetBoundsInScreen().y());
208    }
209  }
210
211 private:
212  scoped_ptr<WindowResizer> resizer_;
213  scoped_ptr<test::ShelfViewTestAPI> shelf_view_test_;
214  ui::wm::WindowType window_type_;
215
216  // Location at start of the drag in |window->parent()|'s coordinates.
217  gfx::Point initial_location_in_parent_;
218
219  DISALLOW_COPY_AND_ASSIGN(DockedWindowLayoutManagerTest);
220};
221
222// Tests that a created window is successfully added to the dock
223// layout manager.
224TEST_P(DockedWindowLayoutManagerTest, AddOneWindow) {
225  if (!SupportsHostWindowResize())
226    return;
227
228  gfx::Rect bounds(0, 0, 201, 201);
229  scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
230  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
231
232  // The window should be attached and docked at the right edge.
233  // Its width should shrink or grow to ideal width.
234  EXPECT_EQ(window->GetRootWindow()->bounds().right(),
235            window->GetBoundsInScreen().right());
236  EXPECT_EQ(ideal_width(), window->bounds().width());
237  EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
238}
239
240// Tests that with a window docked on the left the auto-placing logic in
241// RearrangeVisibleWindowOnShow places windows flush with work area edges.
242TEST_P(DockedWindowLayoutManagerTest, AutoPlacingLeft) {
243  if (!SupportsHostWindowResize())
244    return;
245
246  gfx::Rect bounds(0, 0, 201, 201);
247  scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
248  DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), 0);
249
250  // The window should be attached and snapped to the right side of the screen.
251  EXPECT_EQ(window->GetRootWindow()->bounds().x(),
252            window->GetBoundsInScreen().x());
253  EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
254
255  DockedWindowLayoutManager* manager = static_cast<DockedWindowLayoutManager*>(
256      window->parent()->layout_manager());
257
258  // Create two additional windows and test their auto-placement
259  scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(1));
260  gfx::Rect desktop_area = window1->parent()->bounds();
261  wm::GetWindowState(window1.get())->set_window_position_managed(true);
262  window1->Hide();
263  window1->SetBounds(gfx::Rect(250, 32, 231, 320));
264  window1->Show();
265  // |window1| should be centered in work area.
266  EXPECT_EQ(base::IntToString(
267      docked_width(manager) + min_dock_gap() +
268      (desktop_area.width() - docked_width(manager) -
269       min_dock_gap() - window1->bounds().width()) / 2) +
270      ",32 231x320", window1->bounds().ToString());
271
272  scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(2));
273  wm::GetWindowState(window2.get())->set_window_position_managed(true);
274  // To avoid any auto window manager changes due to SetBounds, the window
275  // gets first hidden and then shown again.
276  window2->Hide();
277  window2->SetBounds(gfx::Rect(250, 48, 150, 300));
278  window2->Show();
279
280  // |window1| should be flush left and |window2| flush right.
281  EXPECT_EQ(
282      base::IntToString(docked_width(manager) + min_dock_gap()) +
283      ",32 231x320", window1->bounds().ToString());
284  EXPECT_EQ(
285      base::IntToString(
286          desktop_area.width() - window2->bounds().width()) +
287      ",48 150x300", window2->bounds().ToString());
288}
289
290// Tests that with a window docked on the right the auto-placing logic in
291// RearrangeVisibleWindowOnShow places windows flush with work area edges.
292TEST_P(DockedWindowLayoutManagerTest, AutoPlacingRight) {
293  if (!SupportsHostWindowResize())
294    return;
295
296  gfx::Rect bounds(0, 0, 201, 201);
297  scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
298  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
299
300  // The window should be attached and snapped to the right side of the screen.
301  EXPECT_EQ(window->GetRootWindow()->bounds().right(),
302            window->GetBoundsInScreen().right());
303  EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
304
305  DockedWindowLayoutManager* manager = static_cast<DockedWindowLayoutManager*>(
306      window->parent()->layout_manager());
307
308  // Create two additional windows and test their auto-placement
309  scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(1));
310  gfx::Rect desktop_area = window1->parent()->bounds();
311  wm::GetWindowState(window1.get())->set_window_position_managed(true);
312  window1->Hide();
313  window1->SetBounds(gfx::Rect(16, 32, 231, 320));
314  window1->Show();
315
316  // |window1| should be centered in work area.
317  EXPECT_EQ(base::IntToString(
318      (desktop_area.width() - docked_width(manager) -
319       min_dock_gap() - window1->bounds().width()) / 2) +
320      ",32 231x320", window1->bounds().ToString());
321
322  scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(2));
323  wm::GetWindowState(window2.get())->set_window_position_managed(true);
324  // To avoid any auto window manager changes due to SetBounds, the window
325  // gets first hidden and then shown again.
326  window2->Hide();
327  window2->SetBounds(gfx::Rect(32, 48, 256, 512));
328  window2->Show();
329
330  // |window1| should be flush left and |window2| flush right.
331  EXPECT_EQ("0,32 231x320", window1->bounds().ToString());
332  EXPECT_EQ(
333      base::IntToString(
334          desktop_area.width() - window2->bounds().width() -
335          docked_width(manager) - min_dock_gap()) +
336      ",48 256x512", window2->bounds().ToString());
337}
338
339// Tests that with a window docked on the right the auto-placing logic in
340// RearrangeVisibleWindowOnShow places windows flush with work area edges.
341// Test case for the secondary screen.
342TEST_P(DockedWindowLayoutManagerTest, AutoPlacingRightSecondScreen) {
343  if (!SupportsMultipleDisplays() || !SupportsHostWindowResize())
344    return;
345
346  // Create a dual screen layout.
347  UpdateDisplay("600x600,600x600");
348
349  gfx::Rect bounds(600, 0, 201, 201);
350  scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
351  // Drag pointer to the right edge of the second screen.
352  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
353
354  // The window should be attached and snapped to the right side of the screen.
355  EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
356            window->GetBoundsInScreen().right());
357  EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
358
359  DockedWindowLayoutManager* manager = static_cast<DockedWindowLayoutManager*>(
360      window->parent()->layout_manager());
361
362  // Create two additional windows and test their auto-placement
363  bounds = gfx::Rect(616, 32, 231, 320);
364  scoped_ptr<aura::Window> window1(
365      CreateTestWindowInShellWithDelegate(NULL, 1, bounds));
366  gfx::Rect desktop_area = window1->parent()->bounds();
367  wm::GetWindowState(window1.get())->set_window_position_managed(true);
368  window1->Hide();
369  window1->Show();
370
371  // |window1| should be centered in work area.
372  EXPECT_EQ(base::IntToString(
373      600 + (desktop_area.width() - docked_width(manager) -
374          min_dock_gap() - window1->bounds().width()) / 2) +
375      ",32 231x320", window1->GetBoundsInScreen().ToString());
376
377  bounds = gfx::Rect(632, 48, 256, 512);
378  scoped_ptr<aura::Window> window2(
379      CreateTestWindowInShellWithDelegate(NULL, 2, bounds));
380  wm::GetWindowState(window2.get())->set_window_position_managed(true);
381  // To avoid any auto window manager changes due to SetBounds, the window
382  // gets first hidden and then shown again.
383  window2->Hide();
384  window2->Show();
385
386  // |window1| should be flush left and |window2| flush right.
387  EXPECT_EQ("600,32 231x320", window1->GetBoundsInScreen().ToString());
388  EXPECT_EQ(
389      base::IntToString(
390          600 + desktop_area.width() - window2->bounds().width() -
391          docked_width(manager) - min_dock_gap()) +
392      ",48 256x512", window2->GetBoundsInScreen().ToString());
393}
394
395// Adds two windows and tests that the gaps are evenly distributed.
396TEST_P(DockedWindowLayoutManagerTest, AddTwoWindows) {
397  if (!SupportsHostWindowResize())
398    return;
399
400  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
401  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
402  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
403  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
404
405  // The windows should be attached and snapped to the right side of the screen.
406  EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
407            w1->GetBoundsInScreen().right());
408  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
409  EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
410            w2->GetBoundsInScreen().right());
411  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
412
413  // Test that the gaps differ at most by a single pixel.
414  gfx::Rect work_area =
415      Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
416  int gap1 = w1->GetBoundsInScreen().y();
417  int gap2 = w2->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
418  int gap3 = work_area.bottom() - w2->GetBoundsInScreen().bottom();
419  EXPECT_EQ(0, gap1);
420  EXPECT_NEAR(gap2, min_dock_gap(), 1);
421  EXPECT_EQ(0, gap3);
422}
423
424// Adds two non-overlapping windows and tests layout after a drag.
425TEST_P(DockedWindowLayoutManagerTest, TwoWindowsDragging) {
426  if (!SupportsHostWindowResize())
427    return;
428
429  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
430  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
431  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
432  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
433
434  // The windows should be attached and snapped to the right side of the screen.
435  EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
436            w1->GetBoundsInScreen().right());
437  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
438  EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
439            w2->GetBoundsInScreen().right());
440  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
441
442  // Drag w2 above w1.
443  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(w2.get(), 0, 20));
444  DragMove(0, -w2->bounds().height() / 2 - min_dock_gap() - 1);
445  DragEnd();
446
447  // Test the new windows order and that the gaps differ at most by a pixel.
448  gfx::Rect work_area =
449      Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
450  int gap1 = w2->GetBoundsInScreen().y() - work_area.y();
451  int gap2 = w1->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
452  int gap3 = work_area.bottom() - w1->GetBoundsInScreen().bottom();
453  EXPECT_EQ(0, gap1);
454  EXPECT_NEAR(gap2, min_dock_gap(), 1);
455  EXPECT_EQ(0, gap3);
456}
457
458// Adds three overlapping windows and tests layout after a drag.
459TEST_P(DockedWindowLayoutManagerTest, ThreeWindowsDragging) {
460  if (!SupportsHostWindowResize())
461    return;
462  UpdateDisplay("600x1000");
463
464  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 310)));
465  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
466  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 310)));
467  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 500);
468  scoped_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 0, 220, 310)));
469  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w3.get(), 600);
470
471  // All windows should be attached and snapped to the right side of the screen.
472  EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
473            w1->GetBoundsInScreen().right());
474  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
475  EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
476            w2->GetBoundsInScreen().right());
477  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
478  EXPECT_EQ(w3->GetRootWindow()->bounds().right(),
479            w3->GetBoundsInScreen().right());
480  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w3->parent()->id());
481
482  // Test that the top and bottom windows are clamped in work area and
483  // that the gaps between the windows differ at most by a pixel.
484  gfx::Rect work_area =
485      Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
486  int gap1 = w1->GetBoundsInScreen().y() - work_area.y();
487  int gap2 = w2->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
488  int gap3 = w3->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
489  int gap4 = work_area.bottom() - w3->GetBoundsInScreen().bottom();
490  EXPECT_EQ(0, gap1);
491  EXPECT_NEAR(gap2, min_dock_gap(), 1);
492  EXPECT_NEAR(gap3, min_dock_gap(), 1);
493  EXPECT_EQ(0, gap4);
494
495  // Drag w1 below the point where w1 and w2 would swap places. This point is
496  // half way between the tops of those two windows.
497  // A bit more vertical drag is needed to account for a window bounds changing
498  // to its restore bounds during the drag.
499  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(w1.get(), 0, 20));
500  DragMove(0, min_dock_gap() + w2->bounds().height() / 2 + 10);
501
502  // During the drag the windows get rearranged and the top and the bottom
503  // should be limited by the work area.
504  EXPECT_EQ(work_area.y(), w2->GetBoundsInScreen().y());
505  EXPECT_GT(w1->GetBoundsInScreen().y(), w2->GetBoundsInScreen().y());
506  EXPECT_EQ(work_area.bottom(), w3->GetBoundsInScreen().bottom());
507  DragEnd();
508
509  // Test the new windows order and that the gaps differ at most by a pixel.
510  gap1 = w2->GetBoundsInScreen().y() - work_area.y();
511  gap2 = w1->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
512  gap3 = w3->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
513  gap4 = work_area.bottom() - w3->GetBoundsInScreen().bottom();
514  EXPECT_EQ(0, gap1);
515  EXPECT_NEAR(gap2, min_dock_gap(), 1);
516  EXPECT_NEAR(gap3, min_dock_gap(), 1);
517  EXPECT_EQ(0, gap4);
518}
519
520// Adds three windows in bottom display and tests layout after a drag.
521TEST_P(DockedWindowLayoutManagerTest, ThreeWindowsDraggingSecondScreen) {
522  if (!SupportsMultipleDisplays() || !SupportsHostWindowResize())
523    return;
524
525  // Create two screen vertical layout.
526  UpdateDisplay("600x1000,600x1000");
527  // Layout the secondary display to the bottom of the primary.
528  DisplayLayout layout(DisplayLayout::BOTTOM, 0);
529  ASSERT_GT(Shell::GetScreen()->GetNumDisplays(), 1);
530  Shell::GetInstance()->display_manager()->
531      SetLayoutForCurrentDisplays(layout);
532
533  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 1000, 201, 310)));
534  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 1000 + 20);
535  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 1000, 210, 310)));
536  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 1000 + 500);
537  scoped_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 1000, 220, 310)));
538  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w3.get(), 1000 + 600);
539
540  // All windows should be attached and snapped to the right side of the screen.
541  EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
542            w1->GetBoundsInScreen().right());
543  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
544  EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
545            w2->GetBoundsInScreen().right());
546  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
547  EXPECT_EQ(w3->GetRootWindow()->bounds().right(),
548            w3->GetBoundsInScreen().right());
549  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w3->parent()->id());
550
551  gfx::Rect work_area =
552      Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
553  // Test that the top and bottom windows are clamped in work area and
554  // that the overlaps between the windows differ at most by a pixel.
555  int gap1 = w1->GetBoundsInScreen().y() - work_area.y();
556  int gap2 = w2->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
557  int gap3 = w3->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
558  int gap4 = work_area.bottom() - w3->GetBoundsInScreen().bottom();
559  EXPECT_EQ(0, gap1);
560  EXPECT_NEAR(gap2, min_dock_gap(), 1);
561  EXPECT_NEAR(gap3, min_dock_gap(), 1);
562  EXPECT_EQ(0, gap4);
563
564  // Drag w1 below the point where w1 and w2 would swap places. This point is
565  // half way between the tops of those two windows.
566  // A bit more vertical drag is needed to account for a window bounds changing
567  // to its restore bounds during the drag.
568  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(w1.get(), 0, 20));
569  DragMove(0, min_dock_gap() + w2->bounds().height() / 2 + 10);
570
571  // During the drag the windows get rearranged and the top and the bottom
572  // should be limited by the work area.
573  EXPECT_EQ(work_area.y(), w2->GetBoundsInScreen().y());
574  EXPECT_GT(w1->GetBoundsInScreen().y(), w2->GetBoundsInScreen().y());
575  EXPECT_EQ(work_area.bottom(), w3->GetBoundsInScreen().bottom());
576  DragEnd();
577
578  // Test the new windows order and that the overlaps differ at most by a pixel.
579  gap1 = w2->GetBoundsInScreen().y() - work_area.y();
580  gap2 = w1->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
581  gap3 = w3->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
582  gap4 = work_area.bottom() - w3->GetBoundsInScreen().bottom();
583  EXPECT_EQ(0, gap1);
584  EXPECT_NEAR(gap2, min_dock_gap(), 1);
585  EXPECT_NEAR(gap3, min_dock_gap(), 1);
586  EXPECT_EQ(0, gap4);
587}
588
589// Tests that a second window added to the dock is resized to match.
590TEST_P(DockedWindowLayoutManagerTest, TwoWindowsWidthNew) {
591  if (!SupportsHostWindowResize())
592    return;
593
594  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
595  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 280, 202)));
596  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
597  // The first window should get resized to ideal width.
598  EXPECT_EQ(ideal_width(), w1->bounds().width());
599
600  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
601  // The second window should get resized to the existing dock.
602  EXPECT_EQ(ideal_width(), w2->bounds().width());
603}
604
605// Tests that a first non-resizable window added to the dock is not resized.
606TEST_P(DockedWindowLayoutManagerTest, TwoWindowsWidthNonResizableFirst) {
607  if (!SupportsHostWindowResize())
608    return;
609
610  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
611  w1->SetProperty(aura::client::kCanResizeKey, false);
612  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 280, 202)));
613  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
614  // The first window should not get resized.
615  EXPECT_EQ(201, w1->bounds().width());
616
617  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
618  // The second window should get resized to the first window's width.
619  EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
620}
621
622// Tests that a second non-resizable window added to the dock is not resized.
623TEST_P(DockedWindowLayoutManagerTest, TwoWindowsWidthNonResizableSecond) {
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, 280, 202)));
629  w2->SetProperty(aura::client::kCanResizeKey, false);
630  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
631  // The first window should get resized to ideal width.
632  EXPECT_EQ(ideal_width(), w1->bounds().width());
633
634  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
635  // The second window should not get resized.
636  EXPECT_EQ(280, w2->bounds().width());
637
638  // The first window should get resized again - to match the second window.
639  EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
640}
641
642// Test that restrictions on minimum and maximum width of windows are honored.
643TEST_P(DockedWindowLayoutManagerTest, TwoWindowsWidthRestrictions) {
644  if (!SupportsHostWindowResize())
645    return;
646
647  aura::test::TestWindowDelegate delegate1;
648  delegate1.set_maximum_size(gfx::Size(240, 0));
649  scoped_ptr<aura::Window> w1(CreateTestWindowWithDelegate(
650      gfx::Rect(0, 0, 201, 201), &delegate1));
651  aura::test::TestWindowDelegate delegate2;
652  delegate2.set_minimum_size(gfx::Size(260, 0));
653  scoped_ptr<aura::Window> w2(CreateTestWindowWithDelegate(
654      gfx::Rect(0, 0, 280, 202), &delegate2));
655  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
656  // The first window should get resized to its maximum width.
657  EXPECT_EQ(240, w1->bounds().width());
658
659  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
660  // The second window should get resized to its minimum width.
661  EXPECT_EQ(260, w2->bounds().width());
662
663  // The first window should be centered relative to the second.
664  EXPECT_EQ(w1->bounds().CenterPoint().x(), w2->bounds().CenterPoint().x());
665}
666
667// Test that restrictions on minimum width of windows are honored.
668TEST_P(DockedWindowLayoutManagerTest, WidthMoreThanMax) {
669  if (!SupportsHostWindowResize())
670    return;
671
672  aura::test::TestWindowDelegate delegate;
673  delegate.set_minimum_size(gfx::Size(400, 0));
674  scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
675      gfx::Rect(0, 0, 400, 201), &delegate));
676  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, window.get(), 20);
677
678  // Secondary drag ensures that we are testing the minimum size restriction
679  // and not just failure to get past the tiling step in SnapSizer.
680  ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(window.get(),
681                                                            25, 5));
682  DragMove(150,0);
683  DragEnd();
684
685  // The window should not get docked even though it is dragged past the edge.
686  EXPECT_NE(window->GetRootWindow()->bounds().right(),
687            window->GetBoundsInScreen().right());
688  EXPECT_NE(internal::kShellWindowId_DockedContainer, window->parent()->id());
689}
690
691// Docks three windows and tests that the very first window gets minimized.
692TEST_P(DockedWindowLayoutManagerTest, ThreeWindowsMinimize) {
693  if (!SupportsHostWindowResize())
694    return;
695
696  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
697  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
698  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
699  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 200);
700  scoped_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 0, 220, 204)));
701  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w3.get(), 300);
702
703  // The last two windows should be attached and snapped to the right edge.
704  EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
705            w2->GetBoundsInScreen().right());
706  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
707  EXPECT_EQ(w3->GetRootWindow()->bounds().right(),
708            w3->GetBoundsInScreen().right());
709  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w3->parent()->id());
710
711  // The first window should get minimized but parented by the dock container.
712  EXPECT_TRUE(wm::GetWindowState(w1.get())->IsMinimized());
713  EXPECT_TRUE(wm::GetWindowState(w2.get())->IsNormalShowState());
714  EXPECT_TRUE(wm::GetWindowState(w3.get())->IsNormalShowState());
715  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
716}
717
718// Docks up to three windows and tests that they split vertical space.
719TEST_P(DockedWindowLayoutManagerTest, ThreeWindowsSplitHeightEvenly) {
720  if (!SupportsHostWindowResize())
721    return;
722
723  UpdateDisplay("600x1000");
724  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
725  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
726  scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
727  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 200);
728
729  // The two windows should be attached and snapped to the right edge.
730  EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
731            w1->GetBoundsInScreen().right());
732  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
733  EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
734            w2->GetBoundsInScreen().right());
735  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
736
737  // The two windows should be same size vertically and almost 1/2 of work area.
738  gfx::Rect work_area =
739      Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
740  EXPECT_NEAR(w1->GetBoundsInScreen().height(),
741              w2->GetBoundsInScreen().height(),
742              1);
743  EXPECT_NEAR(work_area.height() / 2, w1->GetBoundsInScreen().height(),
744              min_dock_gap() * 2);
745
746  // Create and dock the third window.
747  scoped_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 0, 220, 204)));
748  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w3.get(), 300);
749
750  // All three windows should be docked and snapped to the right edge.
751  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
752  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
753  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w3->parent()->id());
754
755  // All windows should be near same size vertically and about 1/3 of work area.
756  EXPECT_NEAR(w1->GetBoundsInScreen().height(),
757              w2->GetBoundsInScreen().height(),
758              1);
759  EXPECT_NEAR(w2->GetBoundsInScreen().height(),
760              w3->GetBoundsInScreen().height(),
761              1);
762  EXPECT_NEAR(work_area.height() / 3, w1->GetBoundsInScreen().height(),
763              min_dock_gap() * 2);
764}
765
766// Docks two windows and tests that restrictions on vertical size are honored.
767TEST_P(DockedWindowLayoutManagerTest, TwoWindowsHeightRestrictions) {
768  if (!SupportsHostWindowResize())
769    return;
770
771  // The first window is fixed height.
772  aura::test::TestWindowDelegate delegate1;
773  delegate1.set_minimum_size(gfx::Size(0, 300));
774  delegate1.set_maximum_size(gfx::Size(0, 300));
775  scoped_ptr<aura::Window> w1(CreateTestWindowWithDelegate(
776      gfx::Rect(0, 0, 201, 300), &delegate1));
777  // The second window has maximum height.
778  aura::test::TestWindowDelegate delegate2;
779  delegate2.set_maximum_size(gfx::Size(0, 100));
780  scoped_ptr<aura::Window> w2(CreateTestWindowWithDelegate(
781      gfx::Rect(0, 0, 280, 90), &delegate2));
782
783  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
784  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 200);
785
786  // The two windows should be attached and snapped to the right edge.
787  EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
788            w1->GetBoundsInScreen().right());
789  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
790  EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
791            w2->GetBoundsInScreen().right());
792  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
793
794  // The two windows should have their heights restricted.
795  EXPECT_EQ(300, w1->GetBoundsInScreen().height());
796  EXPECT_EQ(100, w2->GetBoundsInScreen().height());
797
798  // w1 should be more than half of the work area height (even with a margin).
799  // w2 should be less than half of the work area height (even with a margin).
800  gfx::Rect work_area =
801      Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
802  EXPECT_GT(w1->GetBoundsInScreen().height(), work_area.height() / 2 + 10);
803  EXPECT_LT(w2->GetBoundsInScreen().height(), work_area.height() / 2 - 10);
804}
805
806// Tests that a docked window is moved to primary display when secondary display
807// is disconnected and that it stays docked and properly positioned.
808TEST_P(DockedWindowLayoutManagerTest, DisplayDisconnectionMovesDocked) {
809  if (!SupportsMultipleDisplays() || !SupportsHostWindowResize())
810    return;
811
812  // Create a dual screen layout.
813  UpdateDisplay("600x700,800x600");
814
815  gfx::Rect bounds(600, 0, 201, 201);
816  scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
817  // Drag pointer to the right edge of the second screen.
818  DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
819
820  // Simulate disconnection of the secondary display.
821  UpdateDisplay("600x700");
822
823  // The window should be still docked at the right edge.
824  // Its height should grow to match the new work area.
825  EXPECT_EQ(window->GetRootWindow()->bounds().right(),
826            window->GetBoundsInScreen().right());
827  EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
828  EXPECT_EQ(ideal_width(), window->bounds().width());
829  gfx::Rect work_area =
830      Shell::GetScreen()->GetDisplayNearestWindow(window.get()).work_area();
831  EXPECT_EQ(work_area.height(), window->GetBoundsInScreen().height());
832}
833
834// Tests run twice - on both panels and normal windows
835INSTANTIATE_TEST_CASE_P(NormalOrPanel,
836                        DockedWindowLayoutManagerTest,
837                        testing::Values(ui::wm::WINDOW_TYPE_NORMAL,
838                                        ui::wm::WINDOW_TYPE_PANEL));
839}  // namespace internal
840}  // namespace ash
841