1 // Copyright 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/workspace_controller.h" 6 7 #include <map> 8 9 #include "ash/root_window_controller.h" 10 #include "ash/screen_util.h" 11 #include "ash/shelf/shelf_layout_manager.h" 12 #include "ash/shelf/shelf_widget.h" 13 #include "ash/shell.h" 14 #include "ash/shell_window_ids.h" 15 #include "ash/system/status_area_widget.h" 16 #include "ash/test/ash_test_base.h" 17 #include "ash/test/shell_test_api.h" 18 #include "ash/test/test_shelf_delegate.h" 19 #include "ash/wm/panels/panel_layout_manager.h" 20 #include "ash/wm/window_state.h" 21 #include "ash/wm/window_util.h" 22 #include "ash/wm/workspace/workspace_window_resizer.h" 23 #include "base/strings/string_number_conversions.h" 24 #include "ui/aura/client/aura_constants.h" 25 #include "ui/aura/test/test_window_delegate.h" 26 #include "ui/aura/test/test_windows.h" 27 #include "ui/aura/window.h" 28 #include "ui/aura/window_event_dispatcher.h" 29 #include "ui/base/hit_test.h" 30 #include "ui/base/ui_base_types.h" 31 #include "ui/compositor/layer.h" 32 #include "ui/compositor/scoped_animation_duration_scale_mode.h" 33 #include "ui/events/event_utils.h" 34 #include "ui/events/test/event_generator.h" 35 #include "ui/gfx/screen.h" 36 #include "ui/views/widget/widget.h" 37 #include "ui/wm/core/window_animations.h" 38 #include "ui/wm/core/window_util.h" 39 40 using aura::Window; 41 42 namespace ash { 43 44 // Returns a string containing the names of all the children of |window| (in 45 // order). Each entry is separated by a space. 46 std::string GetWindowNames(const aura::Window* window) { 47 std::string result; 48 for (size_t i = 0; i < window->children().size(); ++i) { 49 if (i != 0) 50 result += " "; 51 result += window->children()[i]->name(); 52 } 53 return result; 54 } 55 56 // Returns a string containing the names of windows corresponding to each of the 57 // child layers of |window|'s layer. Any layers that don't correspond to a child 58 // Window of |window| are ignored. The result is ordered based on the layer 59 // ordering. 60 std::string GetLayerNames(const aura::Window* window) { 61 typedef std::map<const ui::Layer*, std::string> LayerToWindowNameMap; 62 LayerToWindowNameMap window_names; 63 for (size_t i = 0; i < window->children().size(); ++i) { 64 window_names[window->children()[i]->layer()] = 65 window->children()[i]->name(); 66 } 67 68 std::string result; 69 const std::vector<ui::Layer*>& layers(window->layer()->children()); 70 for (size_t i = 0; i < layers.size(); ++i) { 71 LayerToWindowNameMap::iterator layer_i = 72 window_names.find(layers[i]); 73 if (layer_i != window_names.end()) { 74 if (!result.empty()) 75 result += " "; 76 result += layer_i->second; 77 } 78 } 79 return result; 80 } 81 82 class WorkspaceControllerTest : public test::AshTestBase { 83 public: 84 WorkspaceControllerTest() {} 85 virtual ~WorkspaceControllerTest() {} 86 87 aura::Window* CreateTestWindowUnparented() { 88 aura::Window* window = new aura::Window(NULL); 89 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 90 window->SetType(ui::wm::WINDOW_TYPE_NORMAL); 91 window->Init(aura::WINDOW_LAYER_TEXTURED); 92 return window; 93 } 94 95 aura::Window* CreateTestWindow() { 96 aura::Window* window = new aura::Window(NULL); 97 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 98 window->SetType(ui::wm::WINDOW_TYPE_NORMAL); 99 window->Init(aura::WINDOW_LAYER_TEXTURED); 100 ParentWindowInPrimaryRootWindow(window); 101 return window; 102 } 103 104 aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) { 105 aura::Window* window = CreateTestWindow(); 106 window->SetBounds(bounds); 107 wm::WindowState* window_state = wm::GetWindowState(window); 108 window_state->set_window_position_managed(true); 109 window->Show(); 110 return window; 111 } 112 113 aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) { 114 aura::Window* window = CreateTestWindowInShellWithBounds(bounds); 115 window->Show(); 116 return window; 117 } 118 119 aura::Window* CreateTestPanel(aura::WindowDelegate* delegate, 120 const gfx::Rect& bounds) { 121 aura::Window* window = CreateTestWindowInShellWithDelegateAndType( 122 delegate, 123 ui::wm::WINDOW_TYPE_PANEL, 124 0, 125 bounds); 126 test::TestShelfDelegate* shelf_delegate = 127 test::TestShelfDelegate::instance(); 128 shelf_delegate->AddShelfItem(window); 129 PanelLayoutManager* manager = static_cast<PanelLayoutManager*>( 130 Shell::GetContainer(window->GetRootWindow(), 131 kShellWindowId_PanelContainer)->layout_manager()); 132 manager->Relayout(); 133 return window; 134 } 135 136 aura::Window* GetDesktop() { 137 return Shell::GetContainer(Shell::GetPrimaryRootWindow(), 138 kShellWindowId_DefaultContainer); 139 } 140 141 gfx::Rect GetFullscreenBounds(aura::Window* window) { 142 return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds(); 143 } 144 145 ShelfWidget* shelf_widget() { 146 return Shell::GetPrimaryRootWindowController()->shelf(); 147 } 148 149 ShelfLayoutManager* shelf_layout_manager() { 150 return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager(); 151 } 152 153 bool GetWindowOverlapsShelf() { 154 return shelf_layout_manager()->window_overlaps_shelf(); 155 } 156 157 private: 158 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest); 159 }; 160 161 // Assertions around adding a normal window. 162 TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) { 163 scoped_ptr<Window> w1(CreateTestWindow()); 164 w1->SetBounds(gfx::Rect(0, 0, 250, 251)); 165 166 wm::WindowState* window_state = wm::GetWindowState(w1.get()); 167 168 EXPECT_FALSE(window_state->HasRestoreBounds()); 169 170 w1->Show(); 171 172 EXPECT_FALSE(window_state->HasRestoreBounds()); 173 174 ASSERT_TRUE(w1->layer() != NULL); 175 EXPECT_TRUE(w1->layer()->visible()); 176 177 EXPECT_EQ("0,0 250x251", w1->bounds().ToString()); 178 179 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 180 } 181 182 // Assertions around maximizing/unmaximizing. 183 TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) { 184 scoped_ptr<Window> w1(CreateTestWindow()); 185 w1->SetBounds(gfx::Rect(0, 0, 250, 251)); 186 187 w1->Show(); 188 wm::ActivateWindow(w1.get()); 189 190 EXPECT_TRUE(wm::IsActiveWindow(w1.get())); 191 192 ASSERT_TRUE(w1->layer() != NULL); 193 EXPECT_TRUE(w1->layer()->visible()); 194 195 EXPECT_EQ("0,0 250x251", w1->bounds().ToString()); 196 197 // Maximize the window. 198 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 199 200 EXPECT_TRUE(wm::IsActiveWindow(w1.get())); 201 202 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 203 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).width(), 204 w1->bounds().width()); 205 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).height(), 206 w1->bounds().height()); 207 208 // Restore the window. 209 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 210 211 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 212 EXPECT_EQ("0,0 250x251", w1->bounds().ToString()); 213 } 214 215 // Assertions around two windows and toggling one to be fullscreen. 216 TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) { 217 scoped_ptr<Window> w1(CreateTestWindow()); 218 scoped_ptr<Window> w2(CreateTestWindow()); 219 w1->SetBounds(gfx::Rect(0, 0, 250, 251)); 220 w1->Show(); 221 222 ASSERT_TRUE(w1->layer() != NULL); 223 EXPECT_TRUE(w1->layer()->visible()); 224 225 w2->SetBounds(gfx::Rect(0, 0, 50, 51)); 226 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 227 w2->Show(); 228 wm::ActivateWindow(w2.get()); 229 230 // Both windows should be in the same workspace. 231 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 232 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]); 233 234 gfx::Rect work_area( 235 ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get())); 236 EXPECT_EQ(work_area.width(), w2->bounds().width()); 237 EXPECT_EQ(work_area.height(), w2->bounds().height()); 238 239 // Restore w2, which should then go back to one workspace. 240 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 241 EXPECT_EQ(50, w2->bounds().width()); 242 EXPECT_EQ(51, w2->bounds().height()); 243 EXPECT_TRUE(wm::IsActiveWindow(w2.get())); 244 } 245 246 // Makes sure requests to change the bounds of a normal window go through. 247 TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) { 248 scoped_ptr<Window> w1(CreateTestWindow()); 249 w1->Show(); 250 251 // Setting the bounds should go through since the window is in the normal 252 // workspace. 253 w1->SetBounds(gfx::Rect(0, 0, 200, 500)); 254 EXPECT_EQ(200, w1->bounds().width()); 255 EXPECT_EQ(500, w1->bounds().height()); 256 } 257 258 // Verifies the bounds is not altered when showing and grid is enabled. 259 TEST_F(WorkspaceControllerTest, SnapToGrid) { 260 scoped_ptr<Window> w1(CreateTestWindowUnparented()); 261 w1->SetBounds(gfx::Rect(1, 6, 25, 30)); 262 ParentWindowInPrimaryRootWindow(w1.get()); 263 // We are not aligning this anymore this way. When the window gets shown 264 // the window is expected to be handled differently, but this cannot be 265 // tested with this test. So the result of this test should be that the 266 // bounds are exactly as passed in. 267 EXPECT_EQ("1,6 25x30", w1->bounds().ToString()); 268 } 269 270 // Assertions around a fullscreen window. 271 TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) { 272 scoped_ptr<Window> w1(CreateTestWindow()); 273 w1->SetBounds(gfx::Rect(0, 0, 250, 251)); 274 // Make the window fullscreen. 275 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 276 w1->Show(); 277 wm::ActivateWindow(w1.get()); 278 279 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 280 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width()); 281 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height()); 282 283 // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up 284 // with when using views::Widget. 285 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT); 286 EXPECT_EQ("0,0 250x251", w1->bounds().ToString()); 287 288 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 289 EXPECT_EQ(250, w1->bounds().width()); 290 EXPECT_EQ(251, w1->bounds().height()); 291 292 // Back to fullscreen. 293 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 294 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]); 295 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width()); 296 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height()); 297 wm::WindowState* window_state = wm::GetWindowState(w1.get()); 298 299 ASSERT_TRUE(window_state->HasRestoreBounds()); 300 EXPECT_EQ("0,0 250x251", window_state->GetRestoreBoundsInScreen().ToString()); 301 } 302 303 // Assertions around minimizing a single window. 304 TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) { 305 scoped_ptr<Window> w1(CreateTestWindow()); 306 307 w1->Show(); 308 309 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 310 EXPECT_FALSE(w1->layer()->IsDrawn()); 311 EXPECT_TRUE(w1->layer()->GetTargetTransform().IsIdentity()); 312 313 // Show the window. 314 w1->Show(); 315 EXPECT_TRUE(wm::GetWindowState(w1.get())->IsNormalStateType()); 316 EXPECT_TRUE(w1->layer()->IsDrawn()); 317 } 318 319 // Assertions around minimizing a fullscreen window. 320 TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) { 321 // Two windows, w1 normal, w2 fullscreen. 322 scoped_ptr<Window> w1(CreateTestWindow()); 323 scoped_ptr<Window> w2(CreateTestWindow()); 324 w1->Show(); 325 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 326 w2->Show(); 327 328 wm::WindowState* w1_state = wm::GetWindowState(w1.get()); 329 wm::WindowState* w2_state = wm::GetWindowState(w2.get()); 330 331 w2_state->Activate(); 332 333 // Minimize w2. 334 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 335 EXPECT_TRUE(w1->layer()->IsDrawn()); 336 EXPECT_FALSE(w2->layer()->IsDrawn()); 337 338 // Show the window, which should trigger unminimizing. 339 w2->Show(); 340 w2_state->Activate(); 341 342 EXPECT_TRUE(w2_state->IsFullscreen()); 343 EXPECT_TRUE(w1->layer()->IsDrawn()); 344 EXPECT_TRUE(w2->layer()->IsDrawn()); 345 346 // Minimize the window, which should hide the window. 347 EXPECT_TRUE(w2_state->IsActive()); 348 w2_state->Minimize(); 349 EXPECT_FALSE(w2_state->IsActive()); 350 EXPECT_FALSE(w2->layer()->IsDrawn()); 351 EXPECT_TRUE(w1_state->IsActive()); 352 EXPECT_EQ(w2.get(), GetDesktop()->children()[0]); 353 EXPECT_EQ(w1.get(), GetDesktop()->children()[1]); 354 355 // Make the window normal. 356 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 357 // Setting back to normal doesn't change the activation. 358 EXPECT_FALSE(w2_state->IsActive()); 359 EXPECT_TRUE(w1_state->IsActive()); 360 EXPECT_EQ(w2.get(), GetDesktop()->children()[0]); 361 EXPECT_EQ(w1.get(), GetDesktop()->children()[1]); 362 EXPECT_TRUE(w2->layer()->IsDrawn()); 363 } 364 365 // Verifies ShelfLayoutManager's visibility/auto-hide state is correctly 366 // updated. 367 TEST_F(WorkspaceControllerTest, ShelfStateUpdated) { 368 // Since ShelfLayoutManager queries for mouse location, move the mouse so 369 // it isn't over the shelf. 370 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 371 gfx::Point()); 372 generator.MoveMouseTo(0, 0); 373 374 scoped_ptr<Window> w1(CreateTestWindow()); 375 const gfx::Rect w1_bounds(0, 1, 101, 102); 376 ShelfLayoutManager* shelf = shelf_layout_manager(); 377 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); 378 const gfx::Rect touches_shelf_bounds( 379 0, shelf->GetIdealBounds().y() - 10, 101, 102); 380 // Move |w1| to overlap the shelf. 381 w1->SetBounds(touches_shelf_bounds); 382 EXPECT_FALSE(GetWindowOverlapsShelf()); 383 384 // A visible ignored window should not trigger the overlap. 385 scoped_ptr<Window> w_ignored(CreateTestWindow()); 386 w_ignored->SetBounds(touches_shelf_bounds); 387 wm::GetWindowState(&(*w_ignored))->set_ignored_by_shelf(true); 388 w_ignored->Show(); 389 EXPECT_FALSE(GetWindowOverlapsShelf()); 390 391 // Make it visible, since visible shelf overlaps should be true. 392 w1->Show(); 393 EXPECT_TRUE(GetWindowOverlapsShelf()); 394 395 wm::ActivateWindow(w1.get()); 396 w1->SetBounds(w1_bounds); 397 w1->Show(); 398 wm::ActivateWindow(w1.get()); 399 400 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 401 402 // Maximize the window. 403 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 404 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 405 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 406 407 // Restore. 408 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 409 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 410 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 411 412 // Fullscreen. 413 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 414 EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state()); 415 416 // Normal. 417 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 418 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 419 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 420 EXPECT_FALSE(GetWindowOverlapsShelf()); 421 422 // Move window so it obscures shelf. 423 w1->SetBounds(touches_shelf_bounds); 424 EXPECT_TRUE(GetWindowOverlapsShelf()); 425 426 // Move it back. 427 w1->SetBounds(w1_bounds); 428 EXPECT_FALSE(GetWindowOverlapsShelf()); 429 430 // Maximize again. 431 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 432 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 433 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 434 435 // Minimize. 436 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 437 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 438 439 // Since the restore from minimize will restore to the pre-minimize 440 // state (tested elsewhere), we abandon the current size and restore 441 // rect and set them to the window. 442 wm::WindowState* window_state = wm::GetWindowState(w1.get()); 443 444 gfx::Rect restore = window_state->GetRestoreBoundsInScreen(); 445 EXPECT_EQ("0,0 800x597", w1->bounds().ToString()); 446 EXPECT_EQ("0,1 101x102", restore.ToString()); 447 window_state->ClearRestoreBounds(); 448 w1->SetBounds(restore); 449 450 // Restore. 451 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 452 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 453 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 454 455 // Create another window, maximized. 456 scoped_ptr<Window> w2(CreateTestWindow()); 457 w2->SetBounds(gfx::Rect(10, 11, 250, 251)); 458 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 459 w2->Show(); 460 wm::ActivateWindow(w2.get()); 461 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 462 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 463 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 464 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent( 465 w2->parent()).ToString(), 466 w2->bounds().ToString()); 467 468 // Switch to w1. 469 wm::ActivateWindow(w1.get()); 470 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 471 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 472 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent( 473 w2->parent()).ToString(), 474 w2->bounds().ToString()); 475 476 // Switch to w2. 477 wm::ActivateWindow(w2.get()); 478 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); 479 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 480 EXPECT_EQ("0,1 101x102", w1->bounds().ToString()); 481 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w2.get()).ToString(), 482 w2->bounds().ToString()); 483 484 // Turn off auto-hide, switch back to w2 (maximized) and verify overlap. 485 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); 486 wm::ActivateWindow(w2.get()); 487 EXPECT_FALSE(GetWindowOverlapsShelf()); 488 489 // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since 490 // the window isn't in the visible workspace. 491 w1->SetBounds(touches_shelf_bounds); 492 EXPECT_FALSE(GetWindowOverlapsShelf()); 493 494 // Activate w1. Although w1 is visible, the overlap state is still false since 495 // w2 is maximized. 496 wm::ActivateWindow(w1.get()); 497 EXPECT_FALSE(GetWindowOverlapsShelf()); 498 499 // Restore w2. 500 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 501 EXPECT_TRUE(GetWindowOverlapsShelf()); 502 } 503 504 // Verifies going from maximized to minimized sets the right state for painting 505 // the background of the launcher. 506 TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) { 507 scoped_ptr<Window> w1(CreateTestWindow()); 508 w1->Show(); 509 wm::ActivateWindow(w1.get()); 510 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 511 EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType()); 512 513 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 514 EXPECT_EQ(SHELF_VISIBLE, 515 shelf_layout_manager()->visibility_state()); 516 EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType()); 517 } 518 519 // Verifies window visibility during various workspace changes. 520 TEST_F(WorkspaceControllerTest, VisibilityTests) { 521 scoped_ptr<Window> w1(CreateTestWindow()); 522 w1->Show(); 523 EXPECT_TRUE(w1->IsVisible()); 524 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 525 526 // Create another window, activate it and make it fullscreen. 527 scoped_ptr<Window> w2(CreateTestWindow()); 528 w2->Show(); 529 wm::ActivateWindow(w2.get()); 530 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 531 EXPECT_TRUE(w2->IsVisible()); 532 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity()); 533 EXPECT_TRUE(w1->IsVisible()); 534 535 // Switch to w1. |w1| should be visible on top of |w2|. 536 wm::ActivateWindow(w1.get()); 537 EXPECT_TRUE(w1->IsVisible()); 538 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 539 EXPECT_TRUE(w2->IsVisible()); 540 541 // Switch back to |w2|. 542 wm::ActivateWindow(w2.get()); 543 EXPECT_TRUE(w2->IsVisible()); 544 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity()); 545 EXPECT_TRUE(w1->IsVisible()); 546 547 // Restore |w2|, both windows should be visible. 548 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 549 EXPECT_TRUE(w1->IsVisible()); 550 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 551 EXPECT_TRUE(w2->IsVisible()); 552 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity()); 553 554 // Make |w2| fullscreen again, then close it. 555 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); 556 w2->Hide(); 557 EXPECT_FALSE(w2->IsVisible()); 558 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 559 EXPECT_TRUE(w1->IsVisible()); 560 561 // Create |w2| and maximize it. 562 w2.reset(CreateTestWindow()); 563 w2->Show(); 564 wm::ActivateWindow(w2.get()); 565 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 566 EXPECT_TRUE(w2->IsVisible()); 567 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity()); 568 EXPECT_TRUE(w1->IsVisible()); 569 570 // Close |w2|. 571 w2.reset(); 572 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity()); 573 EXPECT_TRUE(w1->IsVisible()); 574 } 575 576 // Verifies windows that are offscreen don't move when switching workspaces. 577 TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) { 578 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 579 gfx::Point()); 580 generator.MoveMouseTo(0, 0); 581 582 scoped_ptr<Window> w1(CreateTestWindow()); 583 ShelfLayoutManager* shelf = shelf_layout_manager(); 584 const gfx::Rect touches_shelf_bounds( 585 0, shelf->GetIdealBounds().y() - 10, 101, 102); 586 // Move |w1| to overlap the shelf. 587 w1->SetBounds(touches_shelf_bounds); 588 w1->Show(); 589 wm::ActivateWindow(w1.get()); 590 591 // Create another window and maximize it. 592 scoped_ptr<Window> w2(CreateTestWindow()); 593 w2->SetBounds(gfx::Rect(10, 11, 250, 251)); 594 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 595 w2->Show(); 596 wm::ActivateWindow(w2.get()); 597 598 // Switch to w1. 599 wm::ActivateWindow(w1.get()); 600 EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString()); 601 } 602 603 // Verifies that windows that are completely offscreen move when switching 604 // workspaces. 605 TEST_F(WorkspaceControllerTest, MoveOnSwitch) { 606 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 607 gfx::Point()); 608 generator.MoveMouseTo(0, 0); 609 610 scoped_ptr<Window> w1(CreateTestWindow()); 611 ShelfLayoutManager* shelf = shelf_layout_manager(); 612 const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200); 613 // Move |w1| so that the top edge is the same as the top edge of the shelf. 614 w1->SetBounds(w1_bounds); 615 w1->Show(); 616 wm::ActivateWindow(w1.get()); 617 EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString()); 618 619 // Create another window and maximize it. 620 scoped_ptr<Window> w2(CreateTestWindow()); 621 w2->SetBounds(gfx::Rect(10, 11, 250, 251)); 622 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 623 w2->Show(); 624 wm::ActivateWindow(w2.get()); 625 626 // Increase the size of the WorkAreaInsets. This would make |w1| fall 627 // completely out of the display work area. 628 gfx::Insets insets = 629 Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets(); 630 insets.Set(0, 0, insets.bottom() + 30, 0); 631 Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets); 632 633 // Switch to w1. The window should have moved. 634 wm::ActivateWindow(w1.get()); 635 EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString()); 636 } 637 638 namespace { 639 640 // WindowDelegate used by DontCrashOnChangeAndActivate. 641 class DontCrashOnChangeAndActivateDelegate 642 : public aura::test::TestWindowDelegate { 643 public: 644 DontCrashOnChangeAndActivateDelegate() : window_(NULL) {} 645 646 void set_window(aura::Window* window) { window_ = window; } 647 648 // WindowDelegate overrides: 649 virtual void OnBoundsChanged(const gfx::Rect& old_bounds, 650 const gfx::Rect& new_bounds) OVERRIDE { 651 if (window_) { 652 wm::ActivateWindow(window_); 653 window_ = NULL; 654 } 655 } 656 657 private: 658 aura::Window* window_; 659 660 DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate); 661 }; 662 663 } // namespace 664 665 // Exercises possible crash in W2. Here's the sequence: 666 // . minimize a maximized window. 667 // . remove the window (which happens when switching displays). 668 // . add the window back. 669 // . show the window and during the bounds change activate it. 670 TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) { 671 // Force the shelf 672 ShelfLayoutManager* shelf = shelf_layout_manager(); 673 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); 674 675 DontCrashOnChangeAndActivateDelegate delegate; 676 scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate( 677 &delegate, 1000, gfx::Rect(10, 11, 250, 251))); 678 679 w1->Show(); 680 wm::WindowState* w1_state = wm::GetWindowState(w1.get()); 681 w1_state->Activate(); 682 w1_state->Maximize(); 683 w1_state->Minimize(); 684 685 w1->parent()->RemoveChild(w1.get()); 686 687 // Do this so that when we Show() the window a resize occurs and we make the 688 // window active. 689 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); 690 691 ParentWindowInPrimaryRootWindow(w1.get()); 692 delegate.set_window(w1.get()); 693 w1->Show(); 694 } 695 696 // Verifies a window with a transient parent not managed by workspace works. 697 TEST_F(WorkspaceControllerTest, TransientParent) { 698 // Normal window with no transient parent. 699 scoped_ptr<Window> w2(CreateTestWindow()); 700 w2->SetBounds(gfx::Rect(10, 11, 250, 251)); 701 w2->Show(); 702 wm::ActivateWindow(w2.get()); 703 704 // Window with a transient parent. We set the transient parent to the root, 705 // which would never happen but is enough to exercise the bug. 706 scoped_ptr<Window> w1(CreateTestWindowUnparented()); 707 ::wm::AddTransientChild( 708 Shell::GetInstance()->GetPrimaryRootWindow(), w1.get()); 709 w1->SetBounds(gfx::Rect(10, 11, 250, 251)); 710 ParentWindowInPrimaryRootWindow(w1.get()); 711 w1->Show(); 712 wm::ActivateWindow(w1.get()); 713 714 // The window with the transient parent should get added to the same parent as 715 // the normal window. 716 EXPECT_EQ(w2->parent(), w1->parent()); 717 } 718 719 // Test the placement of newly created windows. 720 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) { 721 if (!SupportsHostWindowResize()) 722 return; 723 UpdateDisplay("1600x1200"); 724 // Creating a popup handler here to make sure it does not interfere with the 725 // existing windows. 726 gfx::Rect source_browser_bounds(16, 32, 640, 320); 727 scoped_ptr<aura::Window> browser_window(CreateBrowserLikeWindow( 728 source_browser_bounds)); 729 730 // Creating a popup to make sure it does not interfere with the positioning. 731 scoped_ptr<aura::Window> browser_popup(CreatePopupLikeWindow( 732 gfx::Rect(16, 32, 128, 256))); 733 734 browser_window->Show(); 735 browser_popup->Show(); 736 737 { // With a shown window it's size should get returned. 738 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow( 739 source_browser_bounds)); 740 // The position should be right flush. 741 EXPECT_EQ("960,32 640x320", new_browser_window->bounds().ToString()); 742 } 743 744 { // With the window shown - but more on the right side then on the left 745 // side (and partially out of the screen), it should default to the other 746 // side and inside the screen. 747 gfx::Rect source_browser_bounds(gfx::Rect(1000, 600, 640, 320)); 748 browser_window->SetBounds(source_browser_bounds); 749 750 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow( 751 source_browser_bounds)); 752 // The position should be left & bottom flush. 753 EXPECT_EQ("0,600 640x320", new_browser_window->bounds().ToString()); 754 755 // If the other window was already beyond the point to get right flush 756 // it will remain where it is. 757 EXPECT_EQ("1000,600 640x320", browser_window->bounds().ToString()); 758 } 759 760 { // Make sure that popups do not get changed. 761 scoped_ptr<aura::Window> new_popup_window(CreatePopupLikeWindow( 762 gfx::Rect(50, 100, 300, 150))); 763 EXPECT_EQ("50,100 300x150", new_popup_window->bounds().ToString()); 764 } 765 766 browser_window->Hide(); 767 { // If a window is there but not shown the default should be centered. 768 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow( 769 gfx::Rect(50, 100, 300, 150))); 770 EXPECT_EQ("650,100 300x150", new_browser_window->bounds().ToString()); 771 } 772 } 773 774 // Test that adding a second window shifts both the first window and its 775 // transient child. 776 TEST_F(WorkspaceControllerTest, AutoPlacingMovesTransientChild) { 777 // Create an auto-positioned window. 778 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 779 gfx::Rect desktop_area = window1->parent()->bounds(); 780 wm::GetWindowState(window1.get())->set_window_position_managed(true); 781 // Hide and then show |window1| to trigger auto-positioning logic. 782 window1->Hide(); 783 window1->SetBounds(gfx::Rect(16, 32, 300, 300)); 784 window1->Show(); 785 786 // |window1| should be horizontally centered. 787 int x_window1 = (desktop_area.width() - 300) / 2; 788 EXPECT_EQ(base::IntToString(x_window1) + ",32 300x300", 789 window1->bounds().ToString()); 790 791 // Create a |child| window and make it a transient child of |window1|. 792 scoped_ptr<Window> child(CreateTestWindowUnparented()); 793 ::wm::AddTransientChild(window1.get(), child.get()); 794 const int x_child = x_window1 + 50; 795 child->SetBounds(gfx::Rect(x_child, 20, 200, 200)); 796 ParentWindowInPrimaryRootWindow(child.get()); 797 child->Show(); 798 wm::ActivateWindow(child.get()); 799 800 // The |child| should be where it was created. 801 EXPECT_EQ(base::IntToString(x_child) + ",20 200x200", 802 child->bounds().ToString()); 803 804 // Create and show a second window forcing the first window and its child to 805 // move. 806 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 807 wm::GetWindowState(window2.get())->set_window_position_managed(true); 808 // Hide and then show |window2| to trigger auto-positioning logic. 809 window2->Hide(); 810 window2->SetBounds(gfx::Rect(32, 48, 250, 250)); 811 window2->Show(); 812 813 // Check that both |window1| and |child| have moved left. 814 EXPECT_EQ("0,32 300x300", window1->bounds().ToString()); 815 int x = x_child - x_window1; 816 EXPECT_EQ(base::IntToString(x) + ",20 200x200", child->bounds().ToString()); 817 // Check that |window2| has moved right. 818 x = desktop_area.width() - window2->bounds().width(); 819 EXPECT_EQ(base::IntToString(x) + ",48 250x250", window2->bounds().ToString()); 820 } 821 822 // Test the basic auto placement of one and or two windows in a "simulated 823 // session" of sequential window operations. 824 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) { 825 // Test 1: In case there is no manageable window, no window should shift. 826 827 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 828 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 829 gfx::Rect desktop_area = window1->parent()->bounds(); 830 831 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 832 // Trigger the auto window placement function by making it visible. 833 // Note that the bounds are getting changed while it is invisible. 834 window2->Hide(); 835 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 836 window2->Show(); 837 838 // Check the initial position of the windows is unchanged. 839 EXPECT_EQ("16,32 640x320", window1->bounds().ToString()); 840 EXPECT_EQ("32,48 256x512", window2->bounds().ToString()); 841 842 // Remove the second window and make sure that the first window 843 // does NOT get centered. 844 window2.reset(); 845 EXPECT_EQ("16,32 640x320", window1->bounds().ToString()); 846 847 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 848 // Test 2: Set up two managed windows and check their auto positioning. 849 window1_state->set_window_position_managed(true); 850 851 scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2)); 852 wm::GetWindowState(window3.get())->set_window_position_managed(true); 853 // To avoid any auto window manager changes due to SetBounds, the window 854 // gets first hidden and then shown again. 855 window3->Hide(); 856 window3->SetBounds(gfx::Rect(32, 48, 256, 512)); 857 window3->Show(); 858 // |window1| should be flush left and |window3| flush right. 859 EXPECT_EQ("0,32 640x320", window1->bounds().ToString()); 860 EXPECT_EQ(base::IntToString( 861 desktop_area.width() - window3->bounds().width()) + 862 ",48 256x512", window3->bounds().ToString()); 863 864 // After removing |window3|, |window1| should be centered again. 865 window3.reset(); 866 EXPECT_EQ( 867 base::IntToString( 868 (desktop_area.width() - window1->bounds().width()) / 2) + 869 ",32 640x320", window1->bounds().ToString()); 870 871 // Test 3: Set up a manageable and a non manageable window and check 872 // positioning. 873 scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3)); 874 // To avoid any auto window manager changes due to SetBounds, the window 875 // gets first hidden and then shown again. 876 window1->Hide(); 877 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 878 window4->SetBounds(gfx::Rect(32, 48, 256, 512)); 879 window1->Show(); 880 // |window1| should be centered and |window4| untouched. 881 EXPECT_EQ( 882 base::IntToString( 883 (desktop_area.width() - window1->bounds().width()) / 2) + 884 ",32 640x320", window1->bounds().ToString()); 885 EXPECT_EQ("32,48 256x512", window4->bounds().ToString()); 886 887 // Test4: A single manageable window should get centered. 888 window4.reset(); 889 window1_state->set_bounds_changed_by_user(false); 890 // Trigger the auto window placement function by showing (and hiding) it. 891 window1->Hide(); 892 window1->Show(); 893 // |window1| should be centered. 894 EXPECT_EQ( 895 base::IntToString( 896 (desktop_area.width() - window1->bounds().width()) / 2) + 897 ",32 640x320", window1->bounds().ToString()); 898 } 899 900 // Test the proper usage of user window movement interaction. 901 TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) { 902 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 903 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 904 gfx::Rect desktop_area = window1->parent()->bounds(); 905 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 906 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 907 window1->Hide(); 908 window2->Hide(); 909 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 910 wm::WindowState* window2_state = wm::GetWindowState(window2.get()); 911 912 window1_state->set_window_position_managed(true); 913 window2_state->set_window_position_managed(true); 914 EXPECT_FALSE(window1_state->bounds_changed_by_user()); 915 EXPECT_FALSE(window2_state->bounds_changed_by_user()); 916 917 // Check that the current location gets preserved if the user has 918 // positioned it previously. 919 window1_state->set_bounds_changed_by_user(true); 920 window1->Show(); 921 EXPECT_EQ("16,32 640x320", window1->bounds().ToString()); 922 // Flag should be still set. 923 EXPECT_TRUE(window1_state->bounds_changed_by_user()); 924 EXPECT_FALSE(window2_state->bounds_changed_by_user()); 925 926 // Turn on the second window and make sure that both windows are now 927 // positionable again (user movement cleared). 928 window2->Show(); 929 930 // |window1| should be flush left and |window2| flush right. 931 EXPECT_EQ("0,32 640x320", window1->bounds().ToString()); 932 EXPECT_EQ( 933 base::IntToString(desktop_area.width() - window2->bounds().width()) + 934 ",48 256x512", window2->bounds().ToString()); 935 // FLag should now be reset. 936 EXPECT_FALSE(window1_state->bounds_changed_by_user()); 937 EXPECT_FALSE(window2_state->bounds_changed_by_user()); 938 939 // Going back to one shown window should keep the state. 940 window1_state->set_bounds_changed_by_user(true); 941 window2->Hide(); 942 EXPECT_EQ("0,32 640x320", window1->bounds().ToString()); 943 EXPECT_TRUE(window1_state->bounds_changed_by_user()); 944 } 945 946 // Test if the single window will be restored at original position. 947 TEST_F(WorkspaceControllerTest, TestSingleWindowsRestoredBounds) { 948 scoped_ptr<aura::Window> window1( 949 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); 950 scoped_ptr<aura::Window> window2( 951 CreateTestWindowInShellWithBounds(gfx::Rect(110, 110, 100, 100))); 952 scoped_ptr<aura::Window> window3( 953 CreateTestWindowInShellWithBounds(gfx::Rect(120, 120, 100, 100))); 954 window1->Hide(); 955 window2->Hide(); 956 window3->Hide(); 957 wm::GetWindowState(window1.get())->set_window_position_managed(true); 958 wm::GetWindowState(window2.get())->set_window_position_managed(true); 959 wm::GetWindowState(window3.get())->set_window_position_managed(true); 960 961 window1->Show(); 962 wm::ActivateWindow(window1.get()); 963 window2->Show(); 964 wm::ActivateWindow(window2.get()); 965 window3->Show(); 966 wm::ActivateWindow(window3.get()); 967 EXPECT_EQ(0, window1->bounds().x()); 968 EXPECT_EQ(window2->GetRootWindow()->bounds().right(), 969 window2->bounds().right()); 970 EXPECT_EQ(0, window3->bounds().x()); 971 972 window1->Hide(); 973 EXPECT_EQ(window2->GetRootWindow()->bounds().right(), 974 window2->bounds().right()); 975 EXPECT_EQ(0, window3->bounds().x()); 976 977 // Being a single window will retore the original location. 978 window3->Hide(); 979 wm::ActivateWindow(window2.get()); 980 EXPECT_EQ("110,110 100x100", window2->bounds().ToString()); 981 982 // Showing the 3rd will push the 2nd window left. 983 window3->Show(); 984 wm::ActivateWindow(window3.get()); 985 EXPECT_EQ(0, window2->bounds().x()); 986 EXPECT_EQ(window3->GetRootWindow()->bounds().right(), 987 window3->bounds().right()); 988 989 // Being a single window will retore the original location. 990 window2->Hide(); 991 EXPECT_EQ("120,120 100x100", window3->bounds().ToString()); 992 } 993 994 // Test that user placed windows go back to their user placement after the user 995 // closes all other windows. 996 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) { 997 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 998 gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320); 999 window1->SetBounds(user_pos); 1000 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 1001 1002 window1_state->SetPreAutoManageWindowBounds(user_pos); 1003 gfx::Rect desktop_area = window1->parent()->bounds(); 1004 1005 // Create a second window to let the auto manager kick in. 1006 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 1007 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 1008 window1->Hide(); 1009 window2->Hide(); 1010 wm::GetWindowState(window1.get())->set_window_position_managed(true); 1011 wm::GetWindowState(window2.get())->set_window_position_managed(true); 1012 window1->Show(); 1013 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString()); 1014 window2->Show(); 1015 1016 // |window1| should be flush left and |window2| flush right. 1017 EXPECT_EQ("0," + base::IntToString(user_pos.y()) + 1018 " 640x320", window1->bounds().ToString()); 1019 EXPECT_EQ( 1020 base::IntToString(desktop_area.width() - window2->bounds().width()) + 1021 ",48 256x512", window2->bounds().ToString()); 1022 window2->Hide(); 1023 1024 // After the other window get hidden the window has to move back to the 1025 // previous position and the bounds should still be set and unchanged. 1026 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString()); 1027 ASSERT_TRUE(window1_state->pre_auto_manage_window_bounds()); 1028 EXPECT_EQ(user_pos.ToString(), 1029 window1_state->pre_auto_manage_window_bounds()->ToString()); 1030 } 1031 1032 // Solo window should be restored to the bounds where a user moved to. 1033 TEST_F(WorkspaceControllerTest, TestRestoreToUserModifiedBounds) { 1034 if (!SupportsHostWindowResize()) 1035 return; 1036 1037 UpdateDisplay("400x300"); 1038 gfx::Rect default_bounds(10, 0, 100, 100); 1039 scoped_ptr<aura::Window> window1( 1040 CreateTestWindowInShellWithBounds(default_bounds)); 1041 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 1042 window1->Hide(); 1043 window1_state->set_window_position_managed(true); 1044 window1->Show(); 1045 // First window is centered. 1046 EXPECT_EQ("150,0 100x100", window1->bounds().ToString()); 1047 scoped_ptr<aura::Window> window2( 1048 CreateTestWindowInShellWithBounds(default_bounds)); 1049 wm::WindowState* window2_state = wm::GetWindowState(window2.get()); 1050 window2->Hide(); 1051 window2_state->set_window_position_managed(true); 1052 window2->Show(); 1053 1054 // Auto positioning pushes windows to each sides. 1055 EXPECT_EQ("0,0 100x100", window1->bounds().ToString()); 1056 EXPECT_EQ("300,0 100x100", window2->bounds().ToString()); 1057 1058 window2->Hide(); 1059 // Restores to the center. 1060 EXPECT_EQ("150,0 100x100", window1->bounds().ToString()); 1061 1062 // A user moved the window. 1063 scoped_ptr<WindowResizer> resizer(CreateWindowResizer( 1064 window1.get(), 1065 gfx::Point(), 1066 HTCAPTION, 1067 aura::client::WINDOW_MOVE_SOURCE_MOUSE).release()); 1068 gfx::Point location = resizer->GetInitialLocation(); 1069 location.Offset(-50, 0); 1070 resizer->Drag(location, 0); 1071 resizer->CompleteDrag(); 1072 1073 window1_state->set_bounds_changed_by_user(true); 1074 window1->SetBounds(gfx::Rect(100, 0, 100, 100)); 1075 1076 window2->Show(); 1077 EXPECT_EQ("0,0 100x100", window1->bounds().ToString()); 1078 EXPECT_EQ("300,0 100x100", window2->bounds().ToString()); 1079 1080 // Window 1 should be restored to the user modified bounds. 1081 window2->Hide(); 1082 EXPECT_EQ("100,0 100x100", window1->bounds().ToString()); 1083 } 1084 1085 // Test that a window from normal to minimize will repos the remaining. 1086 TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) { 1087 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 1088 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 1089 window1_state->set_window_position_managed(true); 1090 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 1091 gfx::Rect desktop_area = window1->parent()->bounds(); 1092 1093 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 1094 wm::WindowState* window2_state = wm::GetWindowState(window2.get()); 1095 window2_state->set_window_position_managed(true); 1096 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 1097 1098 window1_state->Minimize(); 1099 1100 // |window2| should be centered now. 1101 EXPECT_TRUE(window2->IsVisible()); 1102 EXPECT_TRUE(window2_state->IsNormalStateType()); 1103 EXPECT_EQ(base::IntToString( 1104 (desktop_area.width() - window2->bounds().width()) / 2) + 1105 ",48 256x512", window2->bounds().ToString()); 1106 1107 window1_state->Restore(); 1108 // |window1| should be flush right and |window3| flush left. 1109 EXPECT_EQ(base::IntToString( 1110 desktop_area.width() - window1->bounds().width()) + 1111 ",32 640x320", window1->bounds().ToString()); 1112 EXPECT_EQ("0,48 256x512", window2->bounds().ToString()); 1113 } 1114 1115 // Test that minimizing an initially maximized window will repos the remaining. 1116 TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) { 1117 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 1118 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 1119 window1_state->set_window_position_managed(true); 1120 gfx::Rect desktop_area = window1->parent()->bounds(); 1121 1122 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 1123 wm::WindowState* window2_state = wm::GetWindowState(window2.get()); 1124 window2_state->set_window_position_managed(true); 1125 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 1126 1127 window1_state->Maximize(); 1128 window1_state->Minimize(); 1129 1130 // |window2| should be centered now. 1131 EXPECT_TRUE(window2->IsVisible()); 1132 EXPECT_TRUE(window2_state->IsNormalStateType()); 1133 EXPECT_EQ(base::IntToString( 1134 (desktop_area.width() - window2->bounds().width()) / 2) + 1135 ",48 256x512", window2->bounds().ToString()); 1136 } 1137 1138 // Test that nomral, maximize, minimizing will repos the remaining. 1139 TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) { 1140 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 1141 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 1142 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 1143 window1_state->set_window_position_managed(true); 1144 gfx::Rect desktop_area = window1->parent()->bounds(); 1145 1146 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 1147 wm::WindowState* window2_state = wm::GetWindowState(window2.get()); 1148 window2_state->set_window_position_managed(true); 1149 window2->SetBounds(gfx::Rect(32, 40, 256, 512)); 1150 1151 // Trigger the auto window placement function by showing (and hiding) it. 1152 window1->Hide(); 1153 window1->Show(); 1154 1155 // |window1| should be flush right and |window3| flush left. 1156 EXPECT_EQ(base::IntToString( 1157 desktop_area.width() - window1->bounds().width()) + 1158 ",32 640x320", window1->bounds().ToString()); 1159 EXPECT_EQ("0,40 256x512", window2->bounds().ToString()); 1160 1161 window1_state->Maximize(); 1162 window1_state->Minimize(); 1163 1164 // |window2| should be centered now. 1165 EXPECT_TRUE(window2->IsVisible()); 1166 EXPECT_TRUE(window2_state->IsNormalStateType()); 1167 EXPECT_EQ(base::IntToString( 1168 (desktop_area.width() - window2->bounds().width()) / 2) + 1169 ",40 256x512", window2->bounds().ToString()); 1170 } 1171 1172 // Test that nomral, maximize, normal will repos the remaining. 1173 TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) { 1174 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 1175 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 1176 wm::WindowState* window1_state = wm::GetWindowState(window1.get()); 1177 window1_state->set_window_position_managed(true); 1178 gfx::Rect desktop_area = window1->parent()->bounds(); 1179 1180 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 1181 wm::GetWindowState(window2.get())->set_window_position_managed(true); 1182 window2->SetBounds(gfx::Rect(32, 40, 256, 512)); 1183 1184 // Trigger the auto window placement function by showing (and hiding) it. 1185 window1->Hide(); 1186 window1->Show(); 1187 1188 // |window1| should be flush right and |window3| flush left. 1189 EXPECT_EQ(base::IntToString( 1190 desktop_area.width() - window1->bounds().width()) + 1191 ",32 640x320", window1->bounds().ToString()); 1192 EXPECT_EQ("0,40 256x512", window2->bounds().ToString()); 1193 1194 window1_state->Maximize(); 1195 window1_state->Restore(); 1196 1197 // |window1| should be flush right and |window2| flush left. 1198 EXPECT_EQ(base::IntToString( 1199 desktop_area.width() - window1->bounds().width()) + 1200 ",32 640x320", window1->bounds().ToString()); 1201 EXPECT_EQ("0,40 256x512", window2->bounds().ToString()); 1202 } 1203 1204 // Test that animations are triggered. 1205 TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) { 1206 ui::ScopedAnimationDurationScaleMode test_duration_mode( 1207 ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); 1208 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0)); 1209 window1->Hide(); 1210 window1->SetBounds(gfx::Rect(16, 32, 640, 320)); 1211 gfx::Rect desktop_area = window1->parent()->bounds(); 1212 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); 1213 window2->Hide(); 1214 window2->SetBounds(gfx::Rect(32, 48, 256, 512)); 1215 1216 wm::GetWindowState(window1.get())->set_window_position_managed(true); 1217 wm::GetWindowState(window2.get())->set_window_position_managed(true); 1218 // Make sure nothing is animating. 1219 window1->layer()->GetAnimator()->StopAnimating(); 1220 window2->layer()->GetAnimator()->StopAnimating(); 1221 window2->Show(); 1222 1223 // The second window should now animate. 1224 EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating()); 1225 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating()); 1226 window2->layer()->GetAnimator()->StopAnimating(); 1227 1228 window1->Show(); 1229 EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating()); 1230 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating()); 1231 1232 window1->layer()->GetAnimator()->StopAnimating(); 1233 window2->layer()->GetAnimator()->StopAnimating(); 1234 // |window1| should be flush right and |window2| flush left. 1235 EXPECT_EQ(base::IntToString( 1236 desktop_area.width() - window1->bounds().width()) + 1237 ",32 640x320", window1->bounds().ToString()); 1238 EXPECT_EQ("0,48 256x512", window2->bounds().ToString()); 1239 } 1240 1241 // This tests simulates a browser and an app and verifies the ordering of the 1242 // windows and layers doesn't get out of sync as various operations occur. Its 1243 // really testing code in FocusController, but easier to simulate here. Just as 1244 // with a real browser the browser here has a transient child window 1245 // (corresponds to the status bubble). 1246 TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) { 1247 scoped_ptr<Window> browser(aura::test::CreateTestWindowWithDelegate( 1248 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL)); 1249 browser->SetName("browser"); 1250 ParentWindowInPrimaryRootWindow(browser.get()); 1251 browser->Show(); 1252 wm::ActivateWindow(browser.get()); 1253 1254 // |status_bubble| is made a transient child of |browser| and as a result 1255 // owned by |browser|. 1256 aura::test::TestWindowDelegate* status_bubble_delegate = 1257 aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(); 1258 status_bubble_delegate->set_can_focus(false); 1259 Window* status_bubble = 1260 aura::test::CreateTestWindowWithDelegate(status_bubble_delegate, 1261 ui::wm::WINDOW_TYPE_POPUP, 1262 gfx::Rect(5, 6, 7, 8), 1263 NULL); 1264 ::wm::AddTransientChild(browser.get(), status_bubble); 1265 ParentWindowInPrimaryRootWindow(status_bubble); 1266 status_bubble->SetName("status_bubble"); 1267 1268 scoped_ptr<Window> app(aura::test::CreateTestWindowWithDelegate( 1269 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL)); 1270 app->SetName("app"); 1271 ParentWindowInPrimaryRootWindow(app.get()); 1272 1273 aura::Window* parent = browser->parent(); 1274 1275 app->Show(); 1276 wm::ActivateWindow(app.get()); 1277 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1278 1279 // Minimize the app, focus should go the browser. 1280 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 1281 EXPECT_TRUE(wm::IsActiveWindow(browser.get())); 1282 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1283 1284 // Minimize the browser (neither windows are focused). 1285 browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 1286 EXPECT_FALSE(wm::IsActiveWindow(browser.get())); 1287 EXPECT_FALSE(wm::IsActiveWindow(app.get())); 1288 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1289 1290 // Show the browser (which should restore it). 1291 browser->Show(); 1292 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1293 1294 // Activate the browser. 1295 ash::wm::ActivateWindow(browser.get()); 1296 EXPECT_TRUE(wm::IsActiveWindow(browser.get())); 1297 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1298 1299 // Restore the app. This differs from above code for |browser| as internally 1300 // the app code does this. Restoring this way or using Show() should not make 1301 // a difference. 1302 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 1303 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1304 1305 // Activate the app. 1306 ash::wm::ActivateWindow(app.get()); 1307 EXPECT_TRUE(wm::IsActiveWindow(app.get())); 1308 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent)); 1309 } 1310 1311 namespace { 1312 1313 // Used by DragMaximizedNonTrackedWindow to track how many times the window 1314 // hierarchy changes affecting the specified window. 1315 class DragMaximizedNonTrackedWindowObserver 1316 : public aura::WindowObserver { 1317 public: 1318 DragMaximizedNonTrackedWindowObserver(aura::Window* window) 1319 : change_count_(0), 1320 window_(window) { 1321 } 1322 1323 // Number of times OnWindowHierarchyChanged() has been received. 1324 void clear_change_count() { change_count_ = 0; } 1325 int change_count() const { 1326 return change_count_; 1327 } 1328 1329 // aura::WindowObserver overrides: 1330 // Counts number of times a window is reparented. Ignores reparenting into and 1331 // from a docked container which is expected when a tab is dragged. 1332 virtual void OnWindowHierarchyChanged( 1333 const HierarchyChangeParams& params) OVERRIDE { 1334 if (params.target != window_ || 1335 (params.old_parent->id() == kShellWindowId_DefaultContainer && 1336 params.new_parent->id() == kShellWindowId_DockedContainer) || 1337 (params.old_parent->id() == kShellWindowId_DockedContainer && 1338 params.new_parent->id() == kShellWindowId_DefaultContainer)) { 1339 return; 1340 } 1341 change_count_++; 1342 } 1343 1344 private: 1345 int change_count_; 1346 aura::Window* window_; 1347 1348 DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver); 1349 }; 1350 1351 } // namespace 1352 1353 // Verifies that a new maximized window becomes visible after its activation 1354 // is requested, even though it does not become activated because a system 1355 // modal window is active. 1356 TEST_F(WorkspaceControllerTest, SwitchFromModal) { 1357 scoped_ptr<Window> modal_window(CreateTestWindowUnparented()); 1358 modal_window->SetBounds(gfx::Rect(10, 11, 21, 22)); 1359 modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM); 1360 ParentWindowInPrimaryRootWindow(modal_window.get()); 1361 modal_window->Show(); 1362 wm::ActivateWindow(modal_window.get()); 1363 1364 scoped_ptr<Window> maximized_window(CreateTestWindow()); 1365 maximized_window->SetProperty( 1366 aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 1367 maximized_window->Show(); 1368 wm::ActivateWindow(maximized_window.get()); 1369 EXPECT_TRUE(maximized_window->IsVisible()); 1370 } 1371 1372 namespace { 1373 1374 // Subclass of WorkspaceControllerTest that runs tests with docked windows 1375 // enabled and disabled. 1376 class WorkspaceControllerTestDragging : public WorkspaceControllerTest { 1377 public: 1378 WorkspaceControllerTestDragging() {} 1379 virtual ~WorkspaceControllerTestDragging() {} 1380 1381 private: 1382 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging); 1383 }; 1384 1385 } // namespace 1386 1387 // Verifies that when dragging a window over the shelf overlap is detected 1388 // during and after the drag. 1389 TEST_F(WorkspaceControllerTestDragging, DragWindowOverlapShelf) { 1390 aura::test::TestWindowDelegate delegate; 1391 delegate.set_window_component(HTCAPTION); 1392 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate( 1393 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL)); 1394 ParentWindowInPrimaryRootWindow(w1.get()); 1395 1396 ShelfLayoutManager* shelf = shelf_layout_manager(); 1397 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); 1398 1399 // Drag near the shelf. 1400 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 1401 gfx::Point()); 1402 generator.MoveMouseTo(10, 10); 1403 generator.PressLeftButton(); 1404 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 70); 1405 1406 // Shelf should not be in overlapped state. 1407 EXPECT_FALSE(GetWindowOverlapsShelf()); 1408 1409 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20); 1410 1411 // Shelf should detect overlap. Overlap state stays after mouse is released. 1412 EXPECT_TRUE(GetWindowOverlapsShelf()); 1413 generator.ReleaseLeftButton(); 1414 EXPECT_TRUE(GetWindowOverlapsShelf()); 1415 } 1416 1417 // Verifies that when dragging a window autohidden shelf stays hidden during 1418 // and after the drag. 1419 TEST_F(WorkspaceControllerTestDragging, DragWindowKeepsShelfAutohidden) { 1420 aura::test::TestWindowDelegate delegate; 1421 delegate.set_window_component(HTCAPTION); 1422 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate( 1423 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL)); 1424 ParentWindowInPrimaryRootWindow(w1.get()); 1425 1426 ShelfLayoutManager* shelf = shelf_layout_manager(); 1427 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); 1428 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 1429 1430 // Drag very little. 1431 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 1432 gfx::Point()); 1433 generator.MoveMouseTo(10, 10); 1434 generator.PressLeftButton(); 1435 generator.MoveMouseTo(12, 12); 1436 1437 // Shelf should be hidden during and after the drag. 1438 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 1439 generator.ReleaseLeftButton(); 1440 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state()); 1441 } 1442 1443 // Verifies that events are targeted properly just outside the window edges. 1444 TEST_F(WorkspaceControllerTest, WindowEdgeHitTest) { 1445 aura::test::TestWindowDelegate d_first, d_second; 1446 scoped_ptr<Window> first(aura::test::CreateTestWindowWithDelegate(&d_first, 1447 123, gfx::Rect(20, 10, 100, 50), NULL)); 1448 ParentWindowInPrimaryRootWindow(first.get()); 1449 first->Show(); 1450 1451 scoped_ptr<Window> second(aura::test::CreateTestWindowWithDelegate(&d_second, 1452 234, gfx::Rect(30, 40, 40, 10), NULL)); 1453 ParentWindowInPrimaryRootWindow(second.get()); 1454 second->Show(); 1455 1456 ui::EventTarget* root = first->GetRootWindow(); 1457 ui::EventTargeter* targeter = root->GetEventTargeter(); 1458 1459 // The windows overlap, and |second| is on top of |first|. Events targeted 1460 // slightly outside the edges of the |second| window should still be targeted 1461 // to |second| to allow resizing the windows easily. 1462 1463 const int kNumPoints = 4; 1464 struct { 1465 const char* direction; 1466 gfx::Point location; 1467 } points[kNumPoints] = { 1468 { "left", gfx::Point(28, 45) }, // outside the left edge. 1469 { "top", gfx::Point(50, 38) }, // outside the top edge. 1470 { "right", gfx::Point(72, 45) }, // outside the right edge. 1471 { "bottom", gfx::Point(50, 52) }, // outside the bottom edge. 1472 }; 1473 // Do two iterations, first without any transform on |second|, and the second 1474 // time after applying some transform on |second| so that it doesn't get 1475 // targeted. 1476 for (int times = 0; times < 2; ++times) { 1477 SCOPED_TRACE(times == 0 ? "Without transform" : "With transform"); 1478 aura::Window* expected_target = times == 0 ? second.get() : first.get(); 1479 for (int i = 0; i < kNumPoints; ++i) { 1480 SCOPED_TRACE(points[i].direction); 1481 const gfx::Point& location = points[i].location; 1482 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE, 1483 ui::EF_NONE); 1484 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse); 1485 EXPECT_EQ(expected_target, target); 1486 1487 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0, 1488 ui::EventTimeForNow()); 1489 target = targeter->FindTargetForEvent(root, &touch); 1490 EXPECT_EQ(expected_target, target); 1491 } 1492 // Apply a transform on |second|. After the transform is applied, the window 1493 // should no longer be targeted. 1494 gfx::Transform transform; 1495 transform.Translate(70, 40); 1496 second->SetTransform(transform); 1497 } 1498 } 1499 1500 // Verifies mouse event targeting just outside the window edges for panels. 1501 TEST_F(WorkspaceControllerTest, WindowEdgeMouseHitTestPanel) { 1502 aura::test::TestWindowDelegate delegate; 1503 scoped_ptr<Window> window(CreateTestPanel(&delegate, 1504 gfx::Rect(20, 10, 100, 50))); 1505 ui::EventTarget* root = window->GetRootWindow(); 1506 ui::EventTargeter* targeter = root->GetEventTargeter(); 1507 const gfx::Rect bounds = window->bounds(); 1508 const int kNumPoints = 5; 1509 struct { 1510 const char* direction; 1511 gfx::Point location; 1512 bool is_target_hit; 1513 } points[kNumPoints] = { 1514 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true }, 1515 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true }, 1516 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true }, 1517 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true }, 1518 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false }, 1519 }; 1520 for (int i = 0; i < kNumPoints; ++i) { 1521 SCOPED_TRACE(points[i].direction); 1522 const gfx::Point& location = points[i].location; 1523 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE, 1524 ui::EF_NONE); 1525 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse); 1526 if (points[i].is_target_hit) 1527 EXPECT_EQ(window.get(), target); 1528 else 1529 EXPECT_NE(window.get(), target); 1530 } 1531 } 1532 1533 // Verifies touch event targeting just outside the window edges for panels. 1534 // The shelf is aligned to the bottom by default, and so touches just below 1535 // the bottom edge of the panel should not target the panel itself because 1536 // an AttachedPanelWindowTargeter is installed on the panel container. 1537 TEST_F(WorkspaceControllerTest, WindowEdgeTouchHitTestPanel) { 1538 aura::test::TestWindowDelegate delegate; 1539 scoped_ptr<Window> window(CreateTestPanel(&delegate, 1540 gfx::Rect(20, 10, 100, 50))); 1541 ui::EventTarget* root = window->GetRootWindow(); 1542 ui::EventTargeter* targeter = root->GetEventTargeter(); 1543 const gfx::Rect bounds = window->bounds(); 1544 const int kNumPoints = 5; 1545 struct { 1546 const char* direction; 1547 gfx::Point location; 1548 bool is_target_hit; 1549 } points[kNumPoints] = { 1550 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true }, 1551 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true }, 1552 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true }, 1553 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), false }, 1554 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false }, 1555 }; 1556 for (int i = 0; i < kNumPoints; ++i) { 1557 SCOPED_TRACE(points[i].direction); 1558 const gfx::Point& location = points[i].location; 1559 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0, 1560 ui::EventTimeForNow()); 1561 ui::EventTarget* target = targeter->FindTargetForEvent(root, &touch); 1562 if (points[i].is_target_hit) 1563 EXPECT_EQ(window.get(), target); 1564 else 1565 EXPECT_NE(window.get(), target); 1566 } 1567 } 1568 1569 // Verifies events targeting just outside the window edges for docked windows. 1570 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestDocked) { 1571 aura::test::TestWindowDelegate delegate; 1572 // Make window smaller than the minimum docked area so that the window edges 1573 // are exposed. 1574 delegate.set_maximum_size(gfx::Size(180, 200)); 1575 scoped_ptr<Window> window(aura::test::CreateTestWindowWithDelegate(&delegate, 1576 123, gfx::Rect(20, 10, 100, 50), NULL)); 1577 ParentWindowInPrimaryRootWindow(window.get()); 1578 aura::Window* docked_container = Shell::GetContainer( 1579 window->GetRootWindow(), kShellWindowId_DockedContainer); 1580 docked_container->AddChild(window.get()); 1581 window->Show(); 1582 ui::EventTarget* root = window->GetRootWindow(); 1583 ui::EventTargeter* targeter = root->GetEventTargeter(); 1584 const gfx::Rect bounds = window->bounds(); 1585 const int kNumPoints = 5; 1586 struct { 1587 const char* direction; 1588 gfx::Point location; 1589 bool is_target_hit; 1590 } points[kNumPoints] = { 1591 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true }, 1592 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true }, 1593 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true }, 1594 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true }, 1595 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false }, 1596 }; 1597 for (int i = 0; i < kNumPoints; ++i) { 1598 SCOPED_TRACE(points[i].direction); 1599 const gfx::Point& location = points[i].location; 1600 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE, 1601 ui::EF_NONE); 1602 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse); 1603 if (points[i].is_target_hit) 1604 EXPECT_EQ(window.get(), target); 1605 else 1606 EXPECT_NE(window.get(), target); 1607 1608 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0, 1609 ui::EventTimeForNow()); 1610 target = targeter->FindTargetForEvent(root, &touch); 1611 if (points[i].is_target_hit) 1612 EXPECT_EQ(window.get(), target); 1613 else 1614 EXPECT_NE(window.get(), target); 1615 } 1616 } 1617 1618 } // namespace ash 1619