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