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.
5 #include "ash/wm/workspace/snap_sizer.h"
7 #include "ash/ash_switches.h"
8 #include "ash/screen_util.h"
10 #include "ash/test/ash_test_base.h"
11 #include "ash/wm/window_state.h"
12 #include "ash/wm/window_util.h"
13 #include "base/command_line.h"
14 #include "ui/aura/root_window.h"
15 #include "ui/aura/test/test_window_delegate.h"
16 #include "ui/aura/window.h"
17 #include "ui/gfx/screen.h"
21 typedef test::AshTestBase SnapSizerTest;
23 using internal::SnapSizer;
25 // Test that a window gets properly snapped to the display's edges in a
26 // multi monitor environment.
27 TEST_F(SnapSizerTest, MultipleDisplays) {
28 if (!SupportsMultipleDisplays())
31 UpdateDisplay("0+0-500x400, 0+500-600x400");
32 const gfx::Rect kPrimaryDisplayWorkAreaBounds =
33 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
34 const gfx::Rect kSecondaryDisplayWorkAreaBounds =
35 ScreenUtil::GetSecondaryDisplay().work_area();
37 scoped_ptr<aura::Window> window(
38 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
39 wm::WindowState* window_state = wm::GetWindowState(window.get());
40 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
41 gfx::Rect expected = gfx::Rect(
42 kPrimaryDisplayWorkAreaBounds.x(),
43 kPrimaryDisplayWorkAreaBounds.y(),
44 window->bounds().width(), // No expectation for the width.
45 kPrimaryDisplayWorkAreaBounds.height());
46 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
48 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
49 // The width should not change when a window switches from being snapped to
50 // the left edge to being snapped to the right edge.
51 expected.set_x(kPrimaryDisplayWorkAreaBounds.right() - expected.width());
52 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
54 // Move the window to the secondary display.
55 window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100),
56 ScreenUtil::GetSecondaryDisplay());
58 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
60 kSecondaryDisplayWorkAreaBounds.right() - window->bounds().width(),
61 kSecondaryDisplayWorkAreaBounds.y(),
62 window->bounds().width(), // No expectation for the width.
63 kSecondaryDisplayWorkAreaBounds.height());
64 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
66 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
67 // The width should not change when a window switches from being snapped to
68 // the right edge to being snapped to the left edge.
69 expected.set_x(kSecondaryDisplayWorkAreaBounds.x());
70 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
73 // Test how the minimum and maximum size specified by the aura::WindowDelegate
75 TEST_F(SnapSizerTest, MinimumSize) {
76 if (!SupportsHostWindowResize())
79 UpdateDisplay("0+0-600x800");
80 const gfx::Rect kWorkAreaBounds =
81 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
83 aura::test::TestWindowDelegate delegate;
84 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
85 &delegate, -1, gfx::Rect(0, 100, kWorkAreaBounds.width() - 1, 100)));
87 // It should be possible to snap a window with a minimum size.
88 delegate.set_minimum_size(gfx::Size(kWorkAreaBounds.width() - 1, 0));
89 wm::WindowState* window_state = wm::GetWindowState(window.get());
90 EXPECT_TRUE(window_state->CanSnap());
91 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
92 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x() + 1,
94 kWorkAreaBounds.width() - 1,
95 kWorkAreaBounds.height());
96 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
98 // It should not be possible to snap a window with a maximum size.
99 delegate.set_minimum_size(gfx::Size());
100 delegate.set_maximum_size(gfx::Size(kWorkAreaBounds.width() - 1, INT_MAX));
101 EXPECT_FALSE(window_state->CanSnap());
104 // Test that repeatedly calling SnapSizer::SnapWindow() when the
105 // --ash-multiple-snap-window-widths flag is set steps through the ideal widths
106 // in descending order as well as 90% and 50% of the work area's width.
107 TEST_F(SnapSizerTest, StepThroughSizes) {
108 if (!SupportsHostWindowResize())
110 CommandLine::ForCurrentProcess()->AppendSwitch(
111 switches::kAshMultipleSnapWindowWidths);
113 UpdateDisplay("1024x800");
114 const gfx::Rect kWorkAreaBounds =
115 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
117 scoped_ptr<aura::Window> window(
118 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
119 wm::WindowState* window_state = wm::GetWindowState(window.get());
121 // Make sure that the work area is the size we expect it to be.
122 EXPECT_GT(kWorkAreaBounds.width() * 0.9, 768);
124 // The first width should be 1024 * 0.9 because the larger ideal widths
125 // (1280, 1024) > 1024 * 0.9.
126 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
127 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x(),
129 kWorkAreaBounds.width() * 0.9,
130 kWorkAreaBounds.height());
131 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
133 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
134 expected.set_width(768);
135 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
137 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
138 expected.set_width(640);
139 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
141 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
142 expected.set_width(kWorkAreaBounds.width() * 0.5);
143 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
146 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
147 expected.set_width(kWorkAreaBounds.width() * 0.9);
148 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
150 // Repeat the test snapping right.
151 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
152 expected.set_width(kWorkAreaBounds.width() * 0.9);
153 expected.set_x(kWorkAreaBounds.right() - expected.width());
154 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
156 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
157 expected.set_width(768);
158 expected.set_x(kWorkAreaBounds.right() - expected.width());
159 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
161 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
162 expected.set_width(640);
163 expected.set_x(kWorkAreaBounds.right() - expected.width());
164 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
166 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
167 expected.set_width(kWorkAreaBounds.width() * 0.5);
168 expected.set_x(kWorkAreaBounds.right() - expected.width());
169 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
172 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
173 expected.set_width(kWorkAreaBounds.width() * 0.9);
174 expected.set_x(kWorkAreaBounds.right() - expected.width());
175 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
178 // Test that a SnapSizer's target bounds always start from the widest possible
179 // bounds and that calling Update() steps through the ideal widths in descending
180 // order as well as 90% and 50% of the work area's width.
181 TEST_F(SnapSizerTest, Update) {
182 if (!SupportsHostWindowResize())
184 CommandLine::ForCurrentProcess()->AppendSwitch(
185 switches::kAshMultipleSnapWindowWidths);
186 UpdateDisplay("1024x800");
187 const gfx::Rect kWorkAreaBounds =
188 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
190 // Make sure that the work area is the size we expect it to be.
191 EXPECT_GT(kWorkAreaBounds.width() * 0.9, 768);
193 scoped_ptr<aura::Window> window(
194 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
195 wm::WindowState* window_state = wm::GetWindowState(window.get());
197 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
198 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
199 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x(),
202 kWorkAreaBounds.height());
204 // The SnapSizer's target bounds should always start from the widest bounds
205 // (instead of the bounds with the next ideal width).
206 SnapSizer sizer(window_state, gfx::Point(800, 0), SnapSizer::LEFT_EDGE,
207 SnapSizer::OTHER_INPUT);
208 sizer.SnapWindowToTargetBounds();
209 expected.set_width(kWorkAreaBounds.width() * .9);
210 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
212 sizer.Update(gfx::Point(600, 0));
213 sizer.SnapWindowToTargetBounds();
214 expected.set_width(768);
215 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
217 sizer.Update(gfx::Point(400, 0));
218 sizer.SnapWindowToTargetBounds();
219 expected.set_width(640);
220 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
222 sizer.Update(gfx::Point(200, 0));
223 sizer.SnapWindowToTargetBounds();
224 expected.set_width(kWorkAreaBounds.width() * 0.5);
225 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
227 // Do not wrap around.
228 sizer.Update(gfx::Point(0, 0));
229 sizer.SnapWindowToTargetBounds();
230 expected.set_width(kWorkAreaBounds.width() * 0.5);
231 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
234 // Tests the SnapSizer's target bounds when resizing is disabled and the
235 // --ash-multiple-snap-window-widths flag is set.
236 TEST_F(SnapSizerTest, Default) {
237 if (!SupportsHostWindowResize())
239 CommandLine::ForCurrentProcess()->AppendSwitch(
240 switches::kAshMultipleSnapWindowWidths);
242 scoped_ptr<aura::Window> window(
243 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
244 SnapSizer sizer(wm::GetWindowState(window.get()), gfx::Point(),
245 SnapSizer::LEFT_EDGE, SnapSizer::OTHER_INPUT);
247 // For small workspace widths, we should snap to 90% of the workspace width
248 // because it is the largest width the window can snap to.
249 UpdateDisplay("0+0-800x600");
250 sizer.SelectDefaultSizeAndDisableResize();
252 gfx::Rect work_area =
253 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
254 gfx::Rect expected(work_area);
255 expected.set_width(work_area.width() * 0.9);
256 EXPECT_EQ(expected.ToString(),
257 ScreenUtil::ConvertRectToScreen(window->parent(),
258 sizer.target_bounds()).ToString());
260 // If the largest width the window can snap to is between 1024 and 1280, we
261 // should snap to 1024.
262 UpdateDisplay("0+0-1280x800");
263 sizer.SelectDefaultSizeAndDisableResize();
264 sizer.SnapWindowToTargetBounds();
265 EXPECT_EQ(1024, window->bounds().width());
267 // We should snap to a width of 50% of the work area if it is the largest
268 // width the window can snap to.
269 UpdateDisplay("0+0-2560x1080");
270 work_area = ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
271 sizer.SelectDefaultSizeAndDisableResize();
272 sizer.SnapWindowToTargetBounds();
273 EXPECT_EQ(work_area.width() / 2, window->bounds().width());
276 // Test that the window only snaps to 50% of the work area width when the
277 // --ash-multiple-snap-window-widths flag is not set.
278 TEST_F(SnapSizerTest, SingleSnapWindowWidth) {
279 if (!SupportsHostWindowResize())
282 if (CommandLine::ForCurrentProcess()->HasSwitch(
283 switches::kAshMultipleSnapWindowWidths)) {
287 UpdateDisplay("0+0-800x600");
288 const gfx::Rect kWorkAreaBounds =
289 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
291 scoped_ptr<aura::Window> window(
292 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
293 wm::WindowState* window_state = wm::GetWindowState(window.get());
294 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
295 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x(),
297 kWorkAreaBounds.width() / 2,
298 kWorkAreaBounds.height());
299 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
301 // Because a window can only be snapped to one size when using the alternate
302 // caption button style, a second call to SnapSizer::SnapWindow() should have
304 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
305 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
307 // It should still be possible to switch a window from being snapped to the
308 // left edge to being snapped to the right edge.
309 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
310 expected.set_x(kWorkAreaBounds.right() - expected.width());
311 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
313 // If resizing is disabled, the window should be snapped to 50% too.
314 SnapSizer sizer1(window_state, gfx::Point(), SnapSizer::RIGHT_EDGE,
315 SnapSizer::OTHER_INPUT);
316 sizer1.SelectDefaultSizeAndDisableResize();
317 sizer1.SnapWindowToTargetBounds();
318 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
320 // Snapping to a SnapSizer's initial bounds snaps to 50% too.
321 SnapSizer sizer2(window_state, gfx::Point(), SnapSizer::LEFT_EDGE,
322 SnapSizer::OTHER_INPUT);
323 sizer2.SnapWindowToTargetBounds();
324 expected.set_x(kWorkAreaBounds.x());
325 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
326 EXPECT_EQ(wm::SHOW_TYPE_LEFT_SNAPPED, window_state->window_show_type());
328 // Setting bounds should keep the window snapped.
329 expected = kWorkAreaBounds;
330 expected.set_width(500);
331 window->SetBounds(gfx::Rect(10, 10, 500, 300));
332 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
335 // Test that snapping left/right preserves the restore bounds.
336 TEST_F(SnapSizerTest, RestoreBounds) {
337 scoped_ptr<aura::Window> window(
338 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
339 wm::WindowState* window_state = wm::GetWindowState(window.get());
341 EXPECT_TRUE(window_state->IsNormalShowState());
343 // 1) Start with restored window with restore bounds set.
344 gfx::Rect restore_bounds = window->GetBoundsInScreen();
345 restore_bounds.set_width(restore_bounds.width() + 1);
346 window_state->SetRestoreBoundsInScreen(restore_bounds);
347 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
348 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
349 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
350 EXPECT_EQ(restore_bounds.ToString(),
351 window_state->GetRestoreBoundsInScreen().ToString());
352 window_state->Restore();
353 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
355 // 2) Start with restored bounds set as a result of maximizing the window.
356 window_state->Maximize();
357 gfx::Rect maximized_bounds = window->GetBoundsInScreen();
358 EXPECT_NE(maximized_bounds.ToString(), restore_bounds.ToString());
359 EXPECT_EQ(restore_bounds.ToString(),
360 window_state->GetRestoreBoundsInScreen().ToString());
362 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
363 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
364 EXPECT_NE(maximized_bounds.ToString(),
365 window->GetBoundsInScreen().ToString());
366 EXPECT_EQ(restore_bounds.ToString(),
367 window_state->GetRestoreBoundsInScreen().ToString());
369 window_state->Restore();
370 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
373 // Test that maximizing an auto managed window, then snapping it puts the window
374 // at the snapped bounds and not at the auto-managed (centered) bounds.
375 TEST_F(SnapSizerTest, AutoManaged) {
376 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
377 wm::WindowState* window_state = wm::GetWindowState(window.get());
378 window_state->set_window_position_managed(true);
380 window->SetBounds(gfx::Rect(100, 100, 100, 100));
383 window_state->Maximize();
384 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
386 const gfx::Rect kWorkAreaBounds =
387 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
388 gfx::Rect expected_snapped_bounds(
389 kWorkAreaBounds.right() - window->bounds().width(),
391 window->bounds().width(), // No expectation for the width.
392 kWorkAreaBounds.height());
393 EXPECT_EQ(expected_snapped_bounds.ToString(),
394 window->GetBoundsInScreen().ToString());
396 // The window should still be auto managed despite being right maximized.
397 EXPECT_TRUE(window_state->window_position_managed());