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/test/test_window_delegate.h" 31 #include "ui/aura/window.h" 32 #include "ui/aura/window_event_dispatcher.h" 33 #include "ui/base/hit_test.h" 34 #include "ui/gfx/screen.h" 35 #include "ui/views/widget/widget.h" 36 #include "ui/wm/core/coordinate_conversion.h" 37 38 namespace ash { 39 40 class 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 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 kShellWindowId_PanelContainer; 153 return 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() != 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. 224 TEST_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(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. 242 TEST_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(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. 292 TEST_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(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. 342 TEST_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(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. 396 TEST_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(kShellWindowId_DockedContainer, w1->parent()->id()); 409 EXPECT_EQ(w2->GetRootWindow()->bounds().right(), 410 w2->GetBoundsInScreen().right()); 411 EXPECT_EQ(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. 425 TEST_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(kShellWindowId_DockedContainer, w1->parent()->id()); 438 EXPECT_EQ(w2->GetRootWindow()->bounds().right(), 439 w2->GetBoundsInScreen().right()); 440 EXPECT_EQ(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. 459 TEST_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(kShellWindowId_DockedContainer, w1->parent()->id()); 475 EXPECT_EQ(w2->GetRootWindow()->bounds().right(), 476 w2->GetBoundsInScreen().right()); 477 EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id()); 478 EXPECT_EQ(w3->GetRootWindow()->bounds().right(), 479 w3->GetBoundsInScreen().right()); 480 EXPECT_EQ(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. 521 TEST_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(kShellWindowId_DockedContainer, w1->parent()->id()); 544 EXPECT_EQ(w2->GetRootWindow()->bounds().right(), 545 w2->GetBoundsInScreen().right()); 546 EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id()); 547 EXPECT_EQ(w3->GetRootWindow()->bounds().right(), 548 w3->GetBoundsInScreen().right()); 549 EXPECT_EQ(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. 590 TEST_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. 606 TEST_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. 623 TEST_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. 643 TEST_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. 668 TEST_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(kShellWindowId_DockedContainer, window->parent()->id()); 689 } 690 691 // Docks three windows and tests that the very first window gets minimized. 692 TEST_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(kShellWindowId_DockedContainer, w2->parent()->id()); 707 EXPECT_EQ(w3->GetRootWindow()->bounds().right(), 708 w3->GetBoundsInScreen().right()); 709 EXPECT_EQ(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())->IsNormalStateType()); 714 EXPECT_TRUE(wm::GetWindowState(w3.get())->IsNormalStateType()); 715 EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id()); 716 } 717 718 // Docks up to three windows and tests that they split vertical space. 719 TEST_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(kShellWindowId_DockedContainer, w1->parent()->id()); 733 EXPECT_EQ(w2->GetRootWindow()->bounds().right(), 734 w2->GetBoundsInScreen().right()); 735 EXPECT_EQ(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(kShellWindowId_DockedContainer, w1->parent()->id()); 752 EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id()); 753 EXPECT_EQ(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. 767 TEST_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(kShellWindowId_DockedContainer, w1->parent()->id()); 790 EXPECT_EQ(w2->GetRootWindow()->bounds().right(), 791 w2->GetBoundsInScreen().right()); 792 EXPECT_EQ(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. 808 TEST_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(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 835 INSTANTIATE_TEST_CASE_P(NormalOrPanel, 836 DockedWindowLayoutManagerTest, 837 testing::Values(ui::wm::WINDOW_TYPE_NORMAL, 838 ui::wm::WINDOW_TYPE_PANEL)); 839 840 } // namespace ash 841