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