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.
5 #include "ash/wm/window_state.h"
7 #include "ash/screen_util.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/client/aura_constants.h"
13 #include "ui/aura/test/test_window_delegate.h"
14 #include "ui/aura/window.h"
20 class AlwaysMaximizeTestState : public WindowState::State {
22 explicit AlwaysMaximizeTestState(WindowStateType initial_state_type)
23 : state_type_(initial_state_type) {}
24 virtual ~AlwaysMaximizeTestState() {}
26 // WindowState::State overrides:
27 virtual void OnWMEvent(WindowState* window_state,
28 const WMEvent* event) OVERRIDE {
29 // We don't do anything here.
31 virtual WindowStateType GetType() const OVERRIDE {
34 virtual void AttachState(
35 WindowState* window_state,
36 WindowState::State* previous_state) OVERRIDE {
37 // We always maximize.
38 if (state_type_ != WINDOW_STATE_TYPE_MAXIMIZED) {
39 window_state->Maximize();
40 state_type_ = WINDOW_STATE_TYPE_MAXIMIZED;
43 virtual void DetachState(WindowState* window_state) OVERRIDE {}
46 WindowStateType state_type_;
48 DISALLOW_COPY_AND_ASSIGN(AlwaysMaximizeTestState);
53 typedef test::AshTestBase WindowStateTest;
55 // Test that a window gets properly snapped to the display's edges in a
56 // multi monitor environment.
57 TEST_F(WindowStateTest, SnapWindowBasic) {
58 if (!SupportsMultipleDisplays())
61 UpdateDisplay("0+0-500x400, 0+500-600x400");
62 const gfx::Rect kPrimaryDisplayWorkAreaBounds =
63 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
64 const gfx::Rect kSecondaryDisplayWorkAreaBounds =
65 ScreenUtil::GetSecondaryDisplay().work_area();
67 scoped_ptr<aura::Window> window(
68 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
69 WindowState* window_state = GetWindowState(window.get());
70 const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
71 window_state->OnWMEvent(&snap_left);
72 gfx::Rect expected = gfx::Rect(
73 kPrimaryDisplayWorkAreaBounds.x(),
74 kPrimaryDisplayWorkAreaBounds.y(),
75 kPrimaryDisplayWorkAreaBounds.width() / 2,
76 kPrimaryDisplayWorkAreaBounds.height());
77 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
79 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
80 window_state->OnWMEvent(&snap_right);
81 expected.set_x(kPrimaryDisplayWorkAreaBounds.right() - expected.width());
82 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
84 // Move the window to the secondary display.
85 window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100),
86 ScreenUtil::GetSecondaryDisplay());
88 window_state->OnWMEvent(&snap_right);
90 kSecondaryDisplayWorkAreaBounds.x() +
91 kSecondaryDisplayWorkAreaBounds.width() / 2,
92 kSecondaryDisplayWorkAreaBounds.y(),
93 kSecondaryDisplayWorkAreaBounds.width() / 2,
94 kSecondaryDisplayWorkAreaBounds.height());
95 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
97 window_state->OnWMEvent(&snap_left);
98 expected.set_x(kSecondaryDisplayWorkAreaBounds.x());
99 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
102 // Test how the minimum and maximum size specified by the aura::WindowDelegate
104 TEST_F(WindowStateTest, SnapWindowMinimumSize) {
105 if (!SupportsHostWindowResize())
108 UpdateDisplay("0+0-600x900");
109 const gfx::Rect kWorkAreaBounds =
110 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
112 aura::test::TestWindowDelegate delegate;
113 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
114 &delegate, -1, gfx::Rect(0, 100, kWorkAreaBounds.width() - 1, 100)));
116 // It should be possible to snap a window with a minimum size.
117 delegate.set_minimum_size(gfx::Size(kWorkAreaBounds.width() - 1, 0));
118 WindowState* window_state = GetWindowState(window.get());
119 EXPECT_TRUE(window_state->CanSnap());
120 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
121 window_state->OnWMEvent(&snap_right);
122 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x() + 1,
124 kWorkAreaBounds.width() - 1,
125 kWorkAreaBounds.height());
126 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
128 // It should not be possible to snap a window with a maximum size, or if it
129 // cannot be maximized.
130 delegate.set_maximum_size(gfx::Size(kWorkAreaBounds.width() - 1, 0));
131 EXPECT_FALSE(window_state->CanSnap());
132 delegate.set_maximum_size(gfx::Size(0, kWorkAreaBounds.height() - 1));
133 EXPECT_FALSE(window_state->CanSnap());
134 delegate.set_maximum_size(gfx::Size());
135 window->SetProperty(aura::client::kCanMaximizeKey, false);
136 EXPECT_FALSE(window_state->CanSnap());
139 // Test that the minimum size specified by aura::WindowDelegate gets respected.
140 TEST_F(WindowStateTest, TestRespectMinimumSize) {
141 if (!SupportsHostWindowResize())
144 UpdateDisplay("0+0-1024x768");
146 aura::test::TestWindowDelegate delegate;
147 const gfx::Size minimum_size(gfx::Size(500, 300));
148 delegate.set_minimum_size(minimum_size);
150 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
151 &delegate, -1, gfx::Rect(0, 100, 100, 100)));
153 // Check that the window has the correct minimum size.
154 EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString());
156 // Set the size to something bigger - that should work.
157 gfx::Rect bigger_bounds(700, 500, 700, 500);
158 window->SetBounds(bigger_bounds);
159 EXPECT_EQ(bigger_bounds.ToString(), window->bounds().ToString());
161 // Set the size to something smaller - that should only resize to the smallest
163 gfx::Rect smaller_bounds(700, 500, 100, 100);
164 window->SetBounds(smaller_bounds);
165 EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString());
168 // Test that the minimum window size specified by aura::WindowDelegate does not
169 // exceed the screen size.
170 TEST_F(WindowStateTest, TestIgnoreTooBigMinimumSize) {
171 if (!SupportsHostWindowResize())
174 UpdateDisplay("0+0-1024x768");
175 const gfx::Size work_area_size =
176 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area().size();
177 const gfx::Size illegal_size(1280, 960);
178 const gfx::Rect illegal_bounds(gfx::Point(0, 0), illegal_size);
180 aura::test::TestWindowDelegate delegate;
181 const gfx::Size minimum_size(illegal_size);
182 delegate.set_minimum_size(minimum_size);
184 // The creation should force the window to respect the screen size.
185 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
186 &delegate, -1, illegal_bounds));
187 EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString());
189 // Trying to set the size to something bigger then the screen size should be
191 window->SetBounds(illegal_bounds);
192 EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString());
194 // Maximizing the window should not allow it to go bigger than that either.
195 WindowState* window_state = GetWindowState(window.get());
196 window_state->Maximize();
197 EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString());
200 // Test that setting the bounds of a snapped window keeps its snapped.
201 TEST_F(WindowStateTest, SnapWindowSetBounds) {
202 if (!SupportsHostWindowResize())
205 UpdateDisplay("0+0-900x600");
206 const gfx::Rect kWorkAreaBounds =
207 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
209 scoped_ptr<aura::Window> window(
210 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
211 WindowState* window_state = GetWindowState(window.get());
212 const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
213 window_state->OnWMEvent(&snap_left);
214 EXPECT_EQ(WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType());
215 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x(),
217 kWorkAreaBounds.width() / 2,
218 kWorkAreaBounds.height());
219 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
221 // Snapped windows can have any width.
222 expected.set_width(500);
223 window->SetBounds(gfx::Rect(10, 10, 500, 300));
224 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
225 EXPECT_EQ(WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType());
228 // Test that snapping left/right preserves the restore bounds.
229 TEST_F(WindowStateTest, RestoreBounds) {
230 scoped_ptr<aura::Window> window(
231 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
232 WindowState* window_state = GetWindowState(window.get());
234 EXPECT_TRUE(window_state->IsNormalStateType());
236 // 1) Start with restored window with restore bounds set.
237 gfx::Rect restore_bounds = window->GetBoundsInScreen();
238 restore_bounds.set_width(restore_bounds.width() + 1);
239 window_state->SetRestoreBoundsInScreen(restore_bounds);
240 const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
241 window_state->OnWMEvent(&snap_left);
242 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
243 window_state->OnWMEvent(&snap_right);
244 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
245 EXPECT_EQ(restore_bounds.ToString(),
246 window_state->GetRestoreBoundsInScreen().ToString());
247 window_state->Restore();
248 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
250 // 2) Start with restored bounds set as a result of maximizing the window.
251 window_state->Maximize();
252 gfx::Rect maximized_bounds = window->GetBoundsInScreen();
253 EXPECT_NE(maximized_bounds.ToString(), restore_bounds.ToString());
254 EXPECT_EQ(restore_bounds.ToString(),
255 window_state->GetRestoreBoundsInScreen().ToString());
257 window_state->OnWMEvent(&snap_left);
258 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
259 EXPECT_NE(maximized_bounds.ToString(),
260 window->GetBoundsInScreen().ToString());
261 EXPECT_EQ(restore_bounds.ToString(),
262 window_state->GetRestoreBoundsInScreen().ToString());
264 window_state->Restore();
265 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
268 // Test that maximizing an auto managed window, then snapping it puts the window
269 // at the snapped bounds and not at the auto-managed (centered) bounds.
270 TEST_F(WindowStateTest, AutoManaged) {
271 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
272 WindowState* window_state = GetWindowState(window.get());
273 window_state->set_window_position_managed(true);
275 window->SetBounds(gfx::Rect(100, 100, 100, 100));
278 window_state->Maximize();
279 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
280 window_state->OnWMEvent(&snap_right);
282 const gfx::Rect kWorkAreaBounds =
283 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
284 gfx::Rect expected_snapped_bounds(
285 kWorkAreaBounds.x() + kWorkAreaBounds.width() / 2,
287 kWorkAreaBounds.width() / 2,
288 kWorkAreaBounds.height());
289 EXPECT_EQ(expected_snapped_bounds.ToString(),
290 window->GetBoundsInScreen().ToString());
292 // The window should still be auto managed despite being right maximized.
293 EXPECT_TRUE(window_state->window_position_managed());
296 // Test that the replacement of a State object works as expected.
297 TEST_F(WindowStateTest, SimpleStateSwap) {
298 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
299 WindowState* window_state = GetWindowState(window.get());
300 EXPECT_FALSE(window_state->IsMaximized());
301 window_state->SetStateObject(
302 scoped_ptr<WindowState::State> (new AlwaysMaximizeTestState(
303 window_state->GetStateType())));
304 EXPECT_TRUE(window_state->IsMaximized());
307 // Test that the replacement of a state object, following a restore with the
308 // original one restores the window to its original state.
309 TEST_F(WindowStateTest, StateSwapRestore) {
310 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
311 WindowState* window_state = GetWindowState(window.get());
312 EXPECT_FALSE(window_state->IsMaximized());
313 scoped_ptr<WindowState::State> old(window_state->SetStateObject(
314 scoped_ptr<WindowState::State> (new AlwaysMaximizeTestState(
315 window_state->GetStateType()))).Pass());
316 EXPECT_TRUE(window_state->IsMaximized());
317 window_state->SetStateObject(old.Pass());
318 EXPECT_FALSE(window_state->IsMaximized());
321 // Tests that a window that had same bounds as the work area shrinks after the
322 // window is maximized and then restored.
323 TEST_F(WindowStateTest, RestoredWindowBoundsShrink) {
324 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
325 WindowState* window_state = GetWindowState(window.get());
326 EXPECT_FALSE(window_state->IsMaximized());
327 gfx::Rect work_area =
328 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
330 window->SetBounds(work_area);
331 window_state->Maximize();
332 EXPECT_TRUE(window_state->IsMaximized());
333 EXPECT_EQ(work_area.ToString(), window->bounds().ToString());
335 window_state->Restore();
336 EXPECT_FALSE(window_state->IsMaximized());
337 EXPECT_NE(work_area.ToString(), window->bounds().ToString());
338 EXPECT_TRUE(work_area.Contains(window->bounds()));
341 // TODO(skuhne): Add more unit test to verify the correctness for the restore