1 // Copyright 2014 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/window_state.h" 6 7 #include "ash/screen_util.h" 8 #include "ash/shell.h" 9 #include "ash/test/ash_test_base.h" 10 #include "ash/wm/window_state.h" 11 #include "ash/wm/wm_event.h" 12 #include "ui/aura/test/test_window_delegate.h" 13 #include "ui/aura/window.h" 14 15 namespace ash { 16 namespace wm { 17 namespace { 18 19 class AlwaysMaximizeTestState : public WindowState::State { 20 public: 21 explicit AlwaysMaximizeTestState(WindowStateType initial_state_type) 22 : state_type_(initial_state_type) {} 23 virtual ~AlwaysMaximizeTestState() {} 24 25 // WindowState::State overrides: 26 virtual void OnWMEvent(WindowState* window_state, 27 const WMEvent* event) OVERRIDE { 28 // We don't do anything here. 29 } 30 virtual WindowStateType GetType() const OVERRIDE { 31 return state_type_; 32 } 33 virtual void AttachState( 34 WindowState* window_state, 35 WindowState::State* previous_state) OVERRIDE { 36 // We always maximize. 37 if (state_type_ != WINDOW_STATE_TYPE_MAXIMIZED) { 38 window_state->Maximize(); 39 state_type_ = WINDOW_STATE_TYPE_MAXIMIZED; 40 } 41 } 42 virtual void DetachState(WindowState* window_state) OVERRIDE {} 43 44 private: 45 WindowStateType state_type_; 46 47 DISALLOW_COPY_AND_ASSIGN(AlwaysMaximizeTestState); 48 }; 49 50 } // namespace 51 52 typedef test::AshTestBase WindowStateTest; 53 54 // Test that a window gets properly snapped to the display's edges in a 55 // multi monitor environment. 56 TEST_F(WindowStateTest, SnapWindowBasic) { 57 if (!SupportsMultipleDisplays()) 58 return; 59 60 UpdateDisplay("0+0-500x400, 0+500-600x400"); 61 const gfx::Rect kPrimaryDisplayWorkAreaBounds = 62 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(); 63 const gfx::Rect kSecondaryDisplayWorkAreaBounds = 64 ScreenUtil::GetSecondaryDisplay().work_area(); 65 66 scoped_ptr<aura::Window> window( 67 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); 68 WindowState* window_state = GetWindowState(window.get()); 69 const WMEvent snap_left(WM_EVENT_SNAP_LEFT); 70 window_state->OnWMEvent(&snap_left); 71 gfx::Rect expected = gfx::Rect( 72 kPrimaryDisplayWorkAreaBounds.x(), 73 kPrimaryDisplayWorkAreaBounds.y(), 74 kPrimaryDisplayWorkAreaBounds.width() / 2, 75 kPrimaryDisplayWorkAreaBounds.height()); 76 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); 77 78 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT); 79 window_state->OnWMEvent(&snap_right); 80 expected.set_x(kPrimaryDisplayWorkAreaBounds.right() - expected.width()); 81 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); 82 83 // Move the window to the secondary display. 84 window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100), 85 ScreenUtil::GetSecondaryDisplay()); 86 87 window_state->OnWMEvent(&snap_right); 88 expected = gfx::Rect( 89 kSecondaryDisplayWorkAreaBounds.x() + 90 kSecondaryDisplayWorkAreaBounds.width() / 2, 91 kSecondaryDisplayWorkAreaBounds.y(), 92 kSecondaryDisplayWorkAreaBounds.width() / 2, 93 kSecondaryDisplayWorkAreaBounds.height()); 94 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); 95 96 window_state->OnWMEvent(&snap_left); 97 expected.set_x(kSecondaryDisplayWorkAreaBounds.x()); 98 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); 99 } 100 101 // Test how the minimum and maximum size specified by the aura::WindowDelegate 102 // affect snapping. 103 TEST_F(WindowStateTest, SnapWindowMinimumSize) { 104 if (!SupportsHostWindowResize()) 105 return; 106 107 UpdateDisplay("0+0-600x900"); 108 const gfx::Rect kWorkAreaBounds = 109 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(); 110 111 aura::test::TestWindowDelegate delegate; 112 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( 113 &delegate, -1, gfx::Rect(0, 100, kWorkAreaBounds.width() - 1, 100))); 114 115 // It should be possible to snap a window with a minimum size. 116 delegate.set_minimum_size(gfx::Size(kWorkAreaBounds.width() - 1, 0)); 117 WindowState* window_state = GetWindowState(window.get()); 118 EXPECT_TRUE(window_state->CanSnap()); 119 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT); 120 window_state->OnWMEvent(&snap_right); 121 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x() + 1, 122 kWorkAreaBounds.y(), 123 kWorkAreaBounds.width() - 1, 124 kWorkAreaBounds.height()); 125 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); 126 127 // It should not be possible to snap a window with a maximum size. 128 delegate.set_minimum_size(gfx::Size()); 129 delegate.set_maximum_size(gfx::Size(kWorkAreaBounds.width() - 1, INT_MAX)); 130 EXPECT_FALSE(window_state->CanSnap()); 131 } 132 133 // Test that the minimum size specified by aura::WindowDelegate gets respected. 134 TEST_F(WindowStateTest, TestRespectMinimumSize) { 135 if (!SupportsHostWindowResize()) 136 return; 137 138 UpdateDisplay("0+0-1024x768"); 139 140 aura::test::TestWindowDelegate delegate; 141 const gfx::Size minimum_size(gfx::Size(500, 300)); 142 delegate.set_minimum_size(minimum_size); 143 144 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( 145 &delegate, -1, gfx::Rect(0, 100, 100, 100))); 146 147 // Check that the window has the correct minimum size. 148 EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString()); 149 150 // Set the size to something bigger - that should work. 151 gfx::Rect bigger_bounds(700, 500, 700, 500); 152 window->SetBounds(bigger_bounds); 153 EXPECT_EQ(bigger_bounds.ToString(), window->bounds().ToString()); 154 155 // Set the size to something smaller - that should only resize to the smallest 156 // possible size. 157 gfx::Rect smaller_bounds(700, 500, 100, 100); 158 window->SetBounds(smaller_bounds); 159 EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString()); 160 } 161 162 // Test that the minimum window size specified by aura::WindowDelegate does not 163 // exceed the screen size. 164 TEST_F(WindowStateTest, TestIgnoreTooBigMinimumSize) { 165 if (!SupportsHostWindowResize()) 166 return; 167 168 UpdateDisplay("0+0-1024x768"); 169 const gfx::Size work_area_size = 170 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area().size(); 171 const gfx::Size illegal_size(1280, 960); 172 const gfx::Rect illegal_bounds(gfx::Point(0, 0), illegal_size); 173 174 aura::test::TestWindowDelegate delegate; 175 const gfx::Size minimum_size(illegal_size); 176 delegate.set_minimum_size(minimum_size); 177 178 // The creation should force the window to respect the screen size. 179 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate( 180 &delegate, -1, illegal_bounds)); 181 EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString()); 182 183 // Trying to set the size to something bigger then the screen size should be 184 // ignored. 185 window->SetBounds(illegal_bounds); 186 EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString()); 187 188 // Maximizing the window should not allow it to go bigger than that either. 189 WindowState* window_state = GetWindowState(window.get()); 190 window_state->Maximize(); 191 EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString()); 192 } 193 194 // Test that setting the bounds of a snapped window keeps its snapped. 195 TEST_F(WindowStateTest, SnapWindowSetBounds) { 196 if (!SupportsHostWindowResize()) 197 return; 198 199 UpdateDisplay("0+0-900x600"); 200 const gfx::Rect kWorkAreaBounds = 201 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(); 202 203 scoped_ptr<aura::Window> window( 204 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); 205 WindowState* window_state = GetWindowState(window.get()); 206 const WMEvent snap_left(WM_EVENT_SNAP_LEFT); 207 window_state->OnWMEvent(&snap_left); 208 EXPECT_EQ(WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType()); 209 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x(), 210 kWorkAreaBounds.y(), 211 kWorkAreaBounds.width() / 2, 212 kWorkAreaBounds.height()); 213 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); 214 215 // Snapped windows can have any width. 216 expected.set_width(500); 217 window->SetBounds(gfx::Rect(10, 10, 500, 300)); 218 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString()); 219 EXPECT_EQ(WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType()); 220 } 221 222 // Test that snapping left/right preserves the restore bounds. 223 TEST_F(WindowStateTest, RestoreBounds) { 224 scoped_ptr<aura::Window> window( 225 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100))); 226 WindowState* window_state = GetWindowState(window.get()); 227 228 EXPECT_TRUE(window_state->IsNormalStateType()); 229 230 // 1) Start with restored window with restore bounds set. 231 gfx::Rect restore_bounds = window->GetBoundsInScreen(); 232 restore_bounds.set_width(restore_bounds.width() + 1); 233 window_state->SetRestoreBoundsInScreen(restore_bounds); 234 const WMEvent snap_left(WM_EVENT_SNAP_LEFT); 235 window_state->OnWMEvent(&snap_left); 236 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT); 237 window_state->OnWMEvent(&snap_right); 238 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString()); 239 EXPECT_EQ(restore_bounds.ToString(), 240 window_state->GetRestoreBoundsInScreen().ToString()); 241 window_state->Restore(); 242 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString()); 243 244 // 2) Start with restored bounds set as a result of maximizing the window. 245 window_state->Maximize(); 246 gfx::Rect maximized_bounds = window->GetBoundsInScreen(); 247 EXPECT_NE(maximized_bounds.ToString(), restore_bounds.ToString()); 248 EXPECT_EQ(restore_bounds.ToString(), 249 window_state->GetRestoreBoundsInScreen().ToString()); 250 251 window_state->OnWMEvent(&snap_left); 252 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString()); 253 EXPECT_NE(maximized_bounds.ToString(), 254 window->GetBoundsInScreen().ToString()); 255 EXPECT_EQ(restore_bounds.ToString(), 256 window_state->GetRestoreBoundsInScreen().ToString()); 257 258 window_state->Restore(); 259 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString()); 260 } 261 262 // Test that maximizing an auto managed window, then snapping it puts the window 263 // at the snapped bounds and not at the auto-managed (centered) bounds. 264 TEST_F(WindowStateTest, AutoManaged) { 265 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); 266 WindowState* window_state = GetWindowState(window.get()); 267 window_state->set_window_position_managed(true); 268 window->Hide(); 269 window->SetBounds(gfx::Rect(100, 100, 100, 100)); 270 window->Show(); 271 272 window_state->Maximize(); 273 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT); 274 window_state->OnWMEvent(&snap_right); 275 276 const gfx::Rect kWorkAreaBounds = 277 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(); 278 gfx::Rect expected_snapped_bounds( 279 kWorkAreaBounds.x() + kWorkAreaBounds.width() / 2, 280 kWorkAreaBounds.y(), 281 kWorkAreaBounds.width() / 2, 282 kWorkAreaBounds.height()); 283 EXPECT_EQ(expected_snapped_bounds.ToString(), 284 window->GetBoundsInScreen().ToString()); 285 286 // The window should still be auto managed despite being right maximized. 287 EXPECT_TRUE(window_state->window_position_managed()); 288 } 289 290 // Test that the replacement of a State object works as expected. 291 TEST_F(WindowStateTest, SimpleStateSwap) { 292 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); 293 WindowState* window_state = GetWindowState(window.get()); 294 EXPECT_FALSE(window_state->IsMaximized()); 295 window_state->SetStateObject( 296 scoped_ptr<WindowState::State> (new AlwaysMaximizeTestState( 297 window_state->GetStateType()))); 298 EXPECT_TRUE(window_state->IsMaximized()); 299 } 300 301 // Test that the replacement of a state object, following a restore with the 302 // original one restores the window to its original state. 303 TEST_F(WindowStateTest, StateSwapRestore) { 304 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0)); 305 WindowState* window_state = GetWindowState(window.get()); 306 EXPECT_FALSE(window_state->IsMaximized()); 307 scoped_ptr<WindowState::State> old(window_state->SetStateObject( 308 scoped_ptr<WindowState::State> (new AlwaysMaximizeTestState( 309 window_state->GetStateType()))).Pass()); 310 EXPECT_TRUE(window_state->IsMaximized()); 311 window_state->SetStateObject(old.Pass()); 312 EXPECT_FALSE(window_state->IsMaximized()); 313 } 314 315 // TODO(skuhne): Add more unit test to verify the correctness for the restore 316 // operation. 317 318 } // namespace wm 319 } // namespace ash 320