Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 9 | #include "ash/root_window_controller.h" |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 10 | #include "ash/screen_util.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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" |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 18 | #include "ash/test/test_shelf_delegate.h" |
| 19 | #include "ash/wm/panels/panel_layout_manager.h" |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 20 | #include "ash/wm/window_state.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 21 | #include "ash/wm/window_util.h" |
Bo Liu | 5c02ac1 | 2014-05-01 10:37:37 -0700 | [diff] [blame] | 22 | #include "ash/wm/workspace/workspace_window_resizer.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 23 | #include "base/strings/string_number_conversions.h" |
| 24 | #include "ui/aura/client/aura_constants.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 25 | #include "ui/aura/test/event_generator.h" |
| 26 | #include "ui/aura/test/test_window_delegate.h" |
| 27 | #include "ui/aura/test/test_windows.h" |
| 28 | #include "ui/aura/window.h" |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 29 | #include "ui/aura/window_event_dispatcher.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 30 | #include "ui/base/hit_test.h" |
| 31 | #include "ui/base/ui_base_types.h" |
| 32 | #include "ui/compositor/layer.h" |
| 33 | #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 34 | #include "ui/events/event_utils.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 35 | #include "ui/gfx/screen.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 36 | #include "ui/views/widget/widget.h" |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 37 | #include "ui/wm/core/window_animations.h" |
| 38 | #include "ui/wm/core/window_util.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 39 | |
| 40 | using aura::Window; |
| 41 | |
| 42 | namespace ash { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 90 | window->SetType(ui::wm::WINDOW_TYPE_NORMAL); |
| 91 | window->Init(aura::WINDOW_LAYER_TEXTURED); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 98 | window->SetType(ui::wm::WINDOW_TYPE_NORMAL); |
| 99 | window->Init(aura::WINDOW_LAYER_TEXTURED); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 100 | ParentWindowInPrimaryRootWindow(window); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 101 | return window; |
| 102 | } |
| 103 | |
Torne (Richard Coles) | 424c4d7 | 2013-08-30 15:14:49 +0100 | [diff] [blame] | 104 | aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) { |
| 105 | aura::Window* window = CreateTestWindow(); |
| 106 | window->SetBounds(bounds); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 107 | wm::WindowState* window_state = wm::GetWindowState(window); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 108 | window_state->set_window_position_managed(true); |
Torne (Richard Coles) | 424c4d7 | 2013-08-30 15:14:49 +0100 | [diff] [blame] | 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(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 116 | return window; |
| 117 | } |
| 118 | |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 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); |
Ben Murdoch | c5cede9 | 2014-04-10 11:22:14 +0100 | [diff] [blame] | 129 | PanelLayoutManager* manager = static_cast<PanelLayoutManager*>( |
| 130 | Shell::GetContainer(window->GetRootWindow(), |
| 131 | kShellWindowId_PanelContainer)->layout_manager()); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 132 | manager->Relayout(); |
| 133 | return window; |
| 134 | } |
| 135 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 136 | aura::Window* GetDesktop() { |
| 137 | return Shell::GetContainer(Shell::GetPrimaryRootWindow(), |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 138 | kShellWindowId_DefaultContainer); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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: |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 166 | wm::WindowState* window_state = wm::GetWindowState(w1.get()); |
| 167 | |
| 168 | EXPECT_FALSE(window_state->HasRestoreBounds()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 169 | |
| 170 | w1->Show(); |
| 171 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 172 | EXPECT_FALSE(window_state->HasRestoreBounds()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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]); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 203 | EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).width(), |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 204 | w1->bounds().width()); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 205 | EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).height(), |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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( |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 235 | ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get())); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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)); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 262 | ParentWindowInPrimaryRootWindow(w1.get()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 311 | EXPECT_TRUE(w1->layer()->GetTargetTransform().IsIdentity()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 312 | |
| 313 | // Show the window. |
| 314 | w1->Show(); |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 315 | EXPECT_TRUE(wm::GetWindowState(w1.get())->IsNormalStateType()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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(); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 327 | |
| 328 | wm::WindowState* w1_state = wm::GetWindowState(w1.get()); |
| 329 | wm::WindowState* w2_state = wm::GetWindowState(w2.get()); |
| 330 | |
| 331 | w2_state->Activate(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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(); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 340 | w2_state->Activate(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 341 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 342 | EXPECT_TRUE(w2_state->IsFullscreen()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 343 | EXPECT_TRUE(w1->layer()->IsDrawn()); |
| 344 | EXPECT_TRUE(w2->layer()->IsDrawn()); |
| 345 | |
| 346 | // Minimize the window, which should hide the window. |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 347 | EXPECT_TRUE(w2_state->IsActive()); |
| 348 | w2_state->Minimize(); |
| 349 | EXPECT_FALSE(w2_state->IsActive()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 350 | EXPECT_FALSE(w2->layer()->IsDrawn()); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 351 | EXPECT_TRUE(w1_state->IsActive()); |
Torne (Richard Coles) | 23730a6 | 2014-03-21 14:25:57 +0000 | [diff] [blame] | 352 | EXPECT_EQ(w2.get(), GetDesktop()->children()[0]); |
| 353 | EXPECT_EQ(w1.get(), GetDesktop()->children()[1]); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 354 | |
| 355 | // Make the window normal. |
| 356 | w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); |
Torne (Richard Coles) | 23730a6 | 2014-03-21 14:25:57 +0000 | [diff] [blame] | 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]); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | aura::test::EventGenerator generator( |
| 371 | Shell::GetPrimaryRootWindow(), 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); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 387 | wm::GetWindowState(&(*w_ignored))->set_ignored_by_shelf(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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. |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 442 | wm::WindowState* window_state = wm::GetWindowState(w1.get()); |
| 443 | |
| 444 | gfx::Rect restore = window_state->GetRestoreBoundsInScreen(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 445 | EXPECT_EQ("0,0 800x597", w1->bounds().ToString()); |
| 446 | EXPECT_EQ("0,1 101x102", restore.ToString()); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 447 | window_state->ClearRestoreBounds(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 464 | EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent( |
| 465 | w2->parent()).ToString(), |
| 466 | w2->bounds().ToString()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 472 | EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent( |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 481 | EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w2.get()).ToString(), |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | aura::test::EventGenerator generator( |
| 579 | Shell::GetPrimaryRootWindow(), 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 | aura::test::EventGenerator generator( |
| 607 | Shell::GetPrimaryRootWindow(), 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(); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 680 | wm::WindowState* w1_state = wm::GetWindowState(w1.get()); |
| 681 | w1_state->Activate(); |
| 682 | w1_state->Maximize(); |
| 683 | w1_state->Minimize(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 691 | ParentWindowInPrimaryRootWindow(w1.get()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 707 | ::wm::AddTransientChild( |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 708 | Shell::GetInstance()->GetPrimaryRootWindow(), w1.get()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 709 | w1->SetBounds(gfx::Rect(10, 11, 250, 251)); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 710 | ParentWindowInPrimaryRootWindow(w1.get()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 424c4d7 | 2013-08-30 15:14:49 +0100 | [diff] [blame] | 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 | |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 822 | // Test the basic auto placement of one and or two windows in a "simulated |
| 823 | // session" of sequential window operations. |
Torne (Richard Coles) | 424c4d7 | 2013-08-30 15:14:49 +0100 | [diff] [blame] | 824 | TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 847 | wm::WindowState* window1_state = wm::GetWindowState(window1.get()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 848 | // Test 2: Set up two managed windows and check their auto positioning. |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 849 | window1_state->set_window_position_managed(true); |
Torne (Richard Coles) | 58537e2 | 2013-09-12 12:10:22 +0100 | [diff] [blame] | 850 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 851 | scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2)); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 852 | wm::GetWindowState(window3.get())->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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(); |
Torne (Richard Coles) | d0247b1 | 2013-09-19 22:36:51 +0100 | [diff] [blame] | 858 | // |window1| should be flush left and |window3| flush right. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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(); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 889 | window1_state->set_bounds_changed_by_user(false); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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(); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 916 | |
| 917 | // Check that the current location gets preserved if the user has |
| 918 | // positioned it previously. |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 919 | window1_state->set_bounds_changed_by_user(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 920 | window1->Show(); |
| 921 | EXPECT_EQ("16,32 640x320", window1->bounds().ToString()); |
| 922 | // Flag should be still set. |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 923 | EXPECT_TRUE(window1_state->bounds_changed_by_user()); |
| 924 | EXPECT_FALSE(window2_state->bounds_changed_by_user()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 424c4d7 | 2013-08-30 15:14:49 +0100 | [diff] [blame] | 930 | // |window1| should be flush left and |window2| flush right. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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. |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 936 | EXPECT_FALSE(window1_state->bounds_changed_by_user()); |
| 937 | EXPECT_FALSE(window2_state->bounds_changed_by_user()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 938 | |
| 939 | // Going back to one shown window should keep the state. |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 940 | window1_state->set_bounds_changed_by_user(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 941 | window2->Hide(); |
| 942 | EXPECT_EQ("0,32 640x320", window1->bounds().ToString()); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 943 | EXPECT_TRUE(window1_state->bounds_changed_by_user()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 944 | } |
| 945 | |
Torne (Richard Coles) | 424c4d7 | 2013-08-30 15:14:49 +0100 | [diff] [blame] | 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(); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 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); |
Torne (Richard Coles) | 424c4d7 | 2013-08-30 15:14:49 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1000 | wm::WindowState* window1_state = wm::GetWindowState(window1.get()); |
| 1001 | |
| 1002 | window1_state->SetPreAutoManageWindowBounds(user_pos); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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(); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1010 | wm::GetWindowState(window1.get())->set_window_position_managed(true); |
| 1011 | wm::GetWindowState(window2.get())->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1027 | ASSERT_TRUE(window1_state->pre_auto_manage_window_bounds()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1028 | EXPECT_EQ(user_pos.ToString(), |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1029 | window1_state->pre_auto_manage_window_bounds()->ToString()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1030 | } |
| 1031 | |
Bo Liu | 5c02ac1 | 2014-05-01 10:37:37 -0700 | [diff] [blame] | 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 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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)); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1088 | wm::WindowState* window1_state = wm::GetWindowState(window1.get()); |
| 1089 | window1_state->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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)); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1094 | wm::WindowState* window2_state = wm::GetWindowState(window2.get()); |
| 1095 | window2_state->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1096 | window2->SetBounds(gfx::Rect(32, 48, 256, 512)); |
| 1097 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1098 | window1_state->Minimize(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1099 | |
| 1100 | // |window2| should be centered now. |
| 1101 | EXPECT_TRUE(window2->IsVisible()); |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 1102 | EXPECT_TRUE(window2_state->IsNormalStateType()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1103 | EXPECT_EQ(base::IntToString( |
| 1104 | (desktop_area.width() - window2->bounds().width()) / 2) + |
| 1105 | ",48 256x512", window2->bounds().ToString()); |
| 1106 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1107 | window1_state->Restore(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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)); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1118 | wm::WindowState* window1_state = wm::GetWindowState(window1.get()); |
| 1119 | window1_state->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1120 | gfx::Rect desktop_area = window1->parent()->bounds(); |
| 1121 | |
| 1122 | scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1123 | wm::WindowState* window2_state = wm::GetWindowState(window2.get()); |
| 1124 | window2_state->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1125 | window2->SetBounds(gfx::Rect(32, 48, 256, 512)); |
| 1126 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1127 | window1_state->Maximize(); |
| 1128 | window1_state->Minimize(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1129 | |
| 1130 | // |window2| should be centered now. |
| 1131 | EXPECT_TRUE(window2->IsVisible()); |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 1132 | EXPECT_TRUE(window2_state->IsNormalStateType()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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)); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1142 | wm::WindowState* window1_state = wm::GetWindowState(window1.get()); |
| 1143 | window1_state->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1144 | gfx::Rect desktop_area = window1->parent()->bounds(); |
| 1145 | |
| 1146 | scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1147 | wm::WindowState* window2_state = wm::GetWindowState(window2.get()); |
| 1148 | window2_state->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1161 | window1_state->Maximize(); |
| 1162 | window1_state->Minimize(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1163 | |
| 1164 | // |window2| should be centered now. |
| 1165 | EXPECT_TRUE(window2->IsVisible()); |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 1166 | EXPECT_TRUE(window2_state->IsNormalStateType()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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)); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1176 | wm::WindowState* window1_state = wm::GetWindowState(window1.get()); |
| 1177 | window1_state->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1178 | gfx::Rect desktop_area = window1->parent()->bounds(); |
| 1179 | |
| 1180 | scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1)); |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1181 | wm::GetWindowState(window2.get())->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1194 | window1_state->Maximize(); |
| 1195 | window1_state->Restore(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 normal_duration_mode( |
| 1207 | ui::ScopedAnimationDurationScaleMode::NORMAL_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 | |
Torne (Richard Coles) | 68043e1 | 2013-09-26 13:24:57 +0100 | [diff] [blame] | 1216 | wm::GetWindowState(window1.get())->set_window_position_managed(true); |
| 1217 | wm::GetWindowState(window2.get())->set_window_position_managed(true); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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) { |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 1247 | scoped_ptr<Window> browser(aura::test::CreateTestWindowWithDelegate( |
| 1248 | NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL)); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1249 | browser->SetName("browser"); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 1250 | ParentWindowInPrimaryRootWindow(browser.get()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 = |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 1260 | aura::test::CreateTestWindowWithDelegate(status_bubble_delegate, |
| 1261 | ui::wm::WINDOW_TYPE_POPUP, |
| 1262 | gfx::Rect(5, 6, 7, 8), |
| 1263 | NULL); |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 1264 | ::wm::AddTransientChild(browser.get(), status_bubble); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 1265 | ParentWindowInPrimaryRootWindow(status_bubble); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1266 | status_bubble->SetName("status_bubble"); |
| 1267 | |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 1268 | scoped_ptr<Window> app(aura::test::CreateTestWindowWithDelegate( |
| 1269 | NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL)); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1270 | app->SetName("app"); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 1271 | ParentWindowInPrimaryRootWindow(app.get()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 1314 | // hierarchy changes affecting the specified window. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1315 | class DragMaximizedNonTrackedWindowObserver |
| 1316 | : public aura::WindowObserver { |
| 1317 | public: |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 1318 | DragMaximizedNonTrackedWindowObserver(aura::Window* window) |
| 1319 | : change_count_(0), |
| 1320 | window_(window) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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: |
Torne (Richard Coles) | 3551c9c | 2013-08-23 16:39:15 +0100 | [diff] [blame] | 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. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1332 | virtual void OnWindowHierarchyChanged( |
| 1333 | const HierarchyChangeParams& params) OVERRIDE { |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 1334 | if (params.target != window_ || |
| 1335 | (params.old_parent->id() == kShellWindowId_DefaultContainer && |
Torne (Richard Coles) | 3551c9c | 2013-08-23 16:39:15 +0100 | [diff] [blame] | 1336 | params.new_parent->id() == kShellWindowId_DockedContainer) || |
| 1337 | (params.old_parent->id() == kShellWindowId_DockedContainer && |
| 1338 | params.new_parent->id() == kShellWindowId_DefaultContainer)) { |
| 1339 | return; |
| 1340 | } |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1341 | change_count_++; |
| 1342 | } |
| 1343 | |
| 1344 | private: |
| 1345 | int change_count_; |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 1346 | aura::Window* window_; |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1347 | |
| 1348 | DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver); |
| 1349 | }; |
| 1350 | |
| 1351 | } // namespace |
| 1352 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 1360 | ParentWindowInPrimaryRootWindow(modal_window.get()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | a3f6a49 | 2013-12-18 16:25:09 +0000 | [diff] [blame] | 1372 | namespace { |
| 1373 | |
| 1374 | // Subclass of WorkspaceControllerTest that runs tests with docked windows |
| 1375 | // enabled and disabled. |
Torne (Richard Coles) | cedac22 | 2014-06-03 10:58:34 +0100 | [diff] [blame] | 1376 | class WorkspaceControllerTestDragging : public WorkspaceControllerTest { |
Torne (Richard Coles) | a3f6a49 | 2013-12-18 16:25:09 +0000 | [diff] [blame] | 1377 | public: |
| 1378 | WorkspaceControllerTestDragging() {} |
| 1379 | virtual ~WorkspaceControllerTestDragging() {} |
| 1380 | |
Torne (Richard Coles) | a3f6a49 | 2013-12-18 16:25:09 +0000 | [diff] [blame] | 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. |
Torne (Richard Coles) | cedac22 | 2014-06-03 10:58:34 +0100 | [diff] [blame] | 1389 | TEST_F(WorkspaceControllerTestDragging, DragWindowOverlapShelf) { |
Torne (Richard Coles) | a3f6a49 | 2013-12-18 16:25:09 +0000 | [diff] [blame] | 1390 | aura::test::TestWindowDelegate delegate; |
| 1391 | delegate.set_window_component(HTCAPTION); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 1392 | scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate( |
| 1393 | &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL)); |
Torne (Richard Coles) | a3f6a49 | 2013-12-18 16:25:09 +0000 | [diff] [blame] | 1394 | ParentWindowInPrimaryRootWindow(w1.get()); |
| 1395 | |
| 1396 | ShelfLayoutManager* shelf = shelf_layout_manager(); |
| 1397 | shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); |
| 1398 | |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 1399 | // Drag near the shelf. |
Torne (Richard Coles) | a3f6a49 | 2013-12-18 16:25:09 +0000 | [diff] [blame] | 1400 | aura::test::EventGenerator generator( |
| 1401 | Shell::GetPrimaryRootWindow(), gfx::Point()); |
| 1402 | generator.MoveMouseTo(10, 10); |
| 1403 | generator.PressLeftButton(); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 1404 | generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 70); |
| 1405 | |
| 1406 | // Shelf should not be in overlapped state. |
| 1407 | EXPECT_FALSE(GetWindowOverlapsShelf()); |
| 1408 | |
Torne (Richard Coles) | a3f6a49 | 2013-12-18 16:25:09 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 1417 | // Verifies that when dragging a window autohidden shelf stays hidden during |
| 1418 | // and after the drag. |
Torne (Richard Coles) | cedac22 | 2014-06-03 10:58:34 +0100 | [diff] [blame] | 1419 | TEST_F(WorkspaceControllerTestDragging, DragWindowKeepsShelfAutohidden) { |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 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 | aura::test::EventGenerator generator( |
| 1432 | Shell::GetPrimaryRootWindow(), 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 | |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 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 | |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 1500 | // Verifies mouse event targeting just outside the window edges for panels. |
| 1501 | TEST_F(WorkspaceControllerTest, WindowEdgeMouseHitTestPanel) { |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 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); |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 1530 | } |
| 1531 | } |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 1532 | |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 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; |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 1559 | ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0, |
| 1560 | ui::EventTimeForNow()); |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 1561 | ui::EventTarget* target = targeter->FindTargetForEvent(root, &touch); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 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) { |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 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( |
Ben Murdoch | c5cede9 | 2014-04-10 11:22:14 +0100 | [diff] [blame] | 1579 | window->GetRootWindow(), kShellWindowId_DockedContainer); |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1618 | } // namespace ash |