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_ash.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 ScreenAsh::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 ScreenAsh::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 repeadedly calling SnapSizer::SnapWindow() steps through the ideal
105 // widths in descending order as well as 90% and 50% of the work area's width.
106 TEST_F(SnapSizerTest, StepThroughSizes) {
107 if (!SupportsHostWindowResize())
110 UpdateDisplay("0+0-1024x800");
111 const gfx::Rect kWorkAreaBounds =
112 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
114 scoped_ptr<aura::Window> window(
115 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
116 wm::WindowState* window_state = wm::GetWindowState(window.get());
118 // Make sure that the work area is the size we expect it to be.
119 EXPECT_GT(kWorkAreaBounds.width() * 0.9, 768);
121 // The first width should be 1024 * 0.9 because the larger ideal widths
122 // (1280, 1024) > 1024 * 0.9.
123 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
124 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x(),
126 kWorkAreaBounds.width() * 0.9,
127 kWorkAreaBounds.height());
128 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
130 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
131 expected.set_width(768);
132 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
134 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
135 expected.set_width(640);
136 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
138 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
139 expected.set_width(kWorkAreaBounds.width() * 0.5);
140 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
143 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
144 expected.set_width(kWorkAreaBounds.width() * 0.9);
145 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
148 // Tests the SnapSizer's target bounds when resizing is disabled.
149 TEST_F(SnapSizerTest, Default) {
150 if (!SupportsHostWindowResize())
153 scoped_ptr<aura::Window> window(
154 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
155 SnapSizer sizer(wm::GetWindowState(window.get()), gfx::Point(),
156 SnapSizer::LEFT_EDGE, SnapSizer::OTHER_INPUT);
158 // For small workspace widths, we should snap to 90% of the workspace width
159 // because it is the largest width the window can snap to.
160 UpdateDisplay("0+0-800x600");
161 sizer.SelectDefaultSizeAndDisableResize();
163 gfx::Rect work_area =
164 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
165 gfx::Rect expected(work_area);
166 expected.set_width(work_area.width() * 0.9);
167 EXPECT_EQ(expected.ToString(),
168 ScreenAsh::ConvertRectToScreen(window->parent(),
169 sizer.target_bounds()).ToString());
171 // If the largest width the window can snap to is between 1024 and 1280, we
172 // should snap to 1024.
173 UpdateDisplay("0+0-1280x800");
174 sizer.SelectDefaultSizeAndDisableResize();
175 sizer.SnapWindowToTargetBounds();
176 EXPECT_EQ(1024, window->bounds().width());
178 // We should snap to a width of 50% of the work area it is the largest width
179 // the window can snap to.
180 UpdateDisplay("0+0-2560x1080");
181 work_area = ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
182 sizer.SelectDefaultSizeAndDisableResize();
183 sizer.SnapWindowToTargetBounds();
184 EXPECT_EQ(work_area.width() / 2, window->bounds().width());
187 // Test that the window only snaps to 50% of the work area width when using the
188 // alternate caption button style.
189 TEST_F(SnapSizerTest, AlternateFrameCaptionButtonStyle) {
190 if (!SupportsHostWindowResize())
193 CommandLine::ForCurrentProcess()->AppendSwitch(
194 ash::switches::kAshEnableAlternateFrameCaptionButtonStyle);
195 ASSERT_TRUE(ash::switches::UseAlternateFrameCaptionButtonStyle());
197 UpdateDisplay("0+0-800x600");
198 const gfx::Rect kWorkAreaBounds =
199 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
201 scoped_ptr<aura::Window> window(
202 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
203 wm::WindowState* window_state = wm::GetWindowState(window.get());
204 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
205 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x(),
207 kWorkAreaBounds.width() / 2,
208 kWorkAreaBounds.height());
209 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
211 // Because a window can only be snapped to one size when using the alternate
212 // caption button style, a second call to SnapSizer::SnapWindow() should have
214 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
215 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
217 // It should still be possible to switch a window from being snapped to the
218 // left edge to being snapped to the right edge.
219 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
220 expected.set_x(kWorkAreaBounds.right() - expected.width());
221 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
223 // If resizing is disabled, the window should be snapped to 50% too.
224 SnapSizer sizer(window_state, gfx::Point(), SnapSizer::RIGHT_EDGE,
225 SnapSizer::OTHER_INPUT);
226 sizer.SelectDefaultSizeAndDisableResize();
227 sizer.SnapWindowToTargetBounds();
228 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
231 // Test that snapping left/right preserves the restore bounds.
232 TEST_F(SnapSizerTest, RestoreBounds) {
233 scoped_ptr<aura::Window> window(
234 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
235 wm::WindowState* window_state = wm::GetWindowState(window.get());
237 EXPECT_TRUE(window_state->IsNormalShowState());
239 // 1) Start with restored window with restore bounds set.
240 gfx::Rect restore_bounds = window->GetBoundsInScreen();
241 restore_bounds.set_width(restore_bounds.width() + 1);
242 window_state->SetRestoreBoundsInScreen(restore_bounds);
243 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
244 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
245 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
246 EXPECT_EQ(restore_bounds.ToString(),
247 window_state->GetRestoreBoundsInScreen().ToString());
248 window_state->Restore();
249 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
251 // 2) Start with restored bounds set as a result of maximizing the window.
252 window_state->Maximize();
253 gfx::Rect maximized_bounds = window->GetBoundsInScreen();
254 EXPECT_NE(maximized_bounds.ToString(), restore_bounds.ToString());
255 EXPECT_EQ(restore_bounds.ToString(),
256 window_state->GetRestoreBoundsInScreen().ToString());
258 SnapSizer::SnapWindow(window_state, SnapSizer::LEFT_EDGE);
259 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
260 EXPECT_NE(maximized_bounds.ToString(),
261 window->GetBoundsInScreen().ToString());
262 EXPECT_EQ(restore_bounds.ToString(),
263 window_state->GetRestoreBoundsInScreen().ToString());
265 window_state->Restore();
266 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
269 // Test that maximizing an auto managed window, then snapping it puts the window
270 // at the snapped bounds and not at the auto-managed (centered) bounds.
271 TEST_F(SnapSizerTest, AutoManaged) {
272 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
273 wm::WindowState* window_state = wm::GetWindowState(window.get());
274 window_state->set_window_position_managed(true);
276 window->SetBounds(gfx::Rect(100, 100, 100, 100));
279 window_state->Maximize();
280 SnapSizer::SnapWindow(window_state, SnapSizer::RIGHT_EDGE);
282 const gfx::Rect kWorkAreaBounds =
283 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
284 gfx::Rect expected_snapped_bounds(
285 kWorkAreaBounds.right() - window->bounds().width(),
287 window->bounds().width(), // No expectation for the width.
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());