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/test/test_window_delegate.h"
13 #include "ui/aura/window.h"
19 class AlwaysMaximizeTestState : public WindowState::State {
21 explicit AlwaysMaximizeTestState(WindowStateType initial_state_type)
22 : state_type_(initial_state_type) {}
23 virtual ~AlwaysMaximizeTestState() {}
25 // WindowState::State overrides:
26 virtual void OnWMEvent(WindowState* window_state,
27 const WMEvent* event) OVERRIDE {
28 // We don't do anything here.
30 virtual WindowStateType GetType() const OVERRIDE {
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;
42 virtual void DetachState(WindowState* window_state) OVERRIDE {}
45 WindowStateType state_type_;
47 DISALLOW_COPY_AND_ASSIGN(AlwaysMaximizeTestState);
52 typedef test::AshTestBase WindowStateTest;
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())
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();
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());
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());
83 // Move the window to the secondary display.
84 window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100),
85 ScreenUtil::GetSecondaryDisplay());
87 window_state->OnWMEvent(&snap_right);
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());
96 window_state->OnWMEvent(&snap_left);
97 expected.set_x(kSecondaryDisplayWorkAreaBounds.x());
98 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
101 // Test how the minimum and maximum size specified by the aura::WindowDelegate
103 TEST_F(WindowStateTest, SnapWindowMinimumSize) {
104 if (!SupportsHostWindowResize())
107 UpdateDisplay("0+0-600x900");
108 const gfx::Rect kWorkAreaBounds =
109 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
111 aura::test::TestWindowDelegate delegate;
112 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
113 &delegate, -1, gfx::Rect(0, 100, kWorkAreaBounds.width() - 1, 100)));
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,
123 kWorkAreaBounds.width() - 1,
124 kWorkAreaBounds.height());
125 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
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());
133 // Test that setting the bounds of a snapped window keeps its snapped.
134 TEST_F(WindowStateTest, SnapWindowSetBounds) {
135 if (!SupportsHostWindowResize())
138 UpdateDisplay("0+0-900x600");
139 const gfx::Rect kWorkAreaBounds =
140 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
142 scoped_ptr<aura::Window> window(
143 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
144 WindowState* window_state = GetWindowState(window.get());
145 const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
146 window_state->OnWMEvent(&snap_left);
147 EXPECT_EQ(WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType());
148 gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x(),
150 kWorkAreaBounds.width() / 2,
151 kWorkAreaBounds.height());
152 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
154 // Snapped windows can have any width.
155 expected.set_width(500);
156 window->SetBounds(gfx::Rect(10, 10, 500, 300));
157 EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
158 EXPECT_EQ(WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType());
161 // Test that snapping left/right preserves the restore bounds.
162 TEST_F(WindowStateTest, RestoreBounds) {
163 scoped_ptr<aura::Window> window(
164 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
165 WindowState* window_state = GetWindowState(window.get());
167 EXPECT_TRUE(window_state->IsNormalStateType());
169 // 1) Start with restored window with restore bounds set.
170 gfx::Rect restore_bounds = window->GetBoundsInScreen();
171 restore_bounds.set_width(restore_bounds.width() + 1);
172 window_state->SetRestoreBoundsInScreen(restore_bounds);
173 const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
174 window_state->OnWMEvent(&snap_left);
175 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
176 window_state->OnWMEvent(&snap_right);
177 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
178 EXPECT_EQ(restore_bounds.ToString(),
179 window_state->GetRestoreBoundsInScreen().ToString());
180 window_state->Restore();
181 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
183 // 2) Start with restored bounds set as a result of maximizing the window.
184 window_state->Maximize();
185 gfx::Rect maximized_bounds = window->GetBoundsInScreen();
186 EXPECT_NE(maximized_bounds.ToString(), restore_bounds.ToString());
187 EXPECT_EQ(restore_bounds.ToString(),
188 window_state->GetRestoreBoundsInScreen().ToString());
190 window_state->OnWMEvent(&snap_left);
191 EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
192 EXPECT_NE(maximized_bounds.ToString(),
193 window->GetBoundsInScreen().ToString());
194 EXPECT_EQ(restore_bounds.ToString(),
195 window_state->GetRestoreBoundsInScreen().ToString());
197 window_state->Restore();
198 EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
201 // Test that maximizing an auto managed window, then snapping it puts the window
202 // at the snapped bounds and not at the auto-managed (centered) bounds.
203 TEST_F(WindowStateTest, AutoManaged) {
204 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
205 WindowState* window_state = GetWindowState(window.get());
206 window_state->set_window_position_managed(true);
208 window->SetBounds(gfx::Rect(100, 100, 100, 100));
211 window_state->Maximize();
212 const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
213 window_state->OnWMEvent(&snap_right);
215 const gfx::Rect kWorkAreaBounds =
216 ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
217 gfx::Rect expected_snapped_bounds(
218 kWorkAreaBounds.x() + kWorkAreaBounds.width() / 2,
220 kWorkAreaBounds.width() / 2,
221 kWorkAreaBounds.height());
222 EXPECT_EQ(expected_snapped_bounds.ToString(),
223 window->GetBoundsInScreen().ToString());
225 // The window should still be auto managed despite being right maximized.
226 EXPECT_TRUE(window_state->window_position_managed());
229 // Test that the replacement of a State object works as expected.
230 TEST_F(WindowStateTest, SimpleStateSwap) {
231 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
232 WindowState* window_state = GetWindowState(window.get());
233 EXPECT_FALSE(window_state->IsMaximized());
234 window_state->SetStateObject(
235 scoped_ptr<WindowState::State> (new AlwaysMaximizeTestState(
236 window_state->GetStateType())));
237 EXPECT_TRUE(window_state->IsMaximized());
240 // Test that the replacement of a state object, following a restore with the
241 // original one restores the window to its original state.
242 TEST_F(WindowStateTest, StateSwapRestore) {
243 scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
244 WindowState* window_state = GetWindowState(window.get());
245 EXPECT_FALSE(window_state->IsMaximized());
246 scoped_ptr<WindowState::State> old(window_state->SetStateObject(
247 scoped_ptr<WindowState::State> (new AlwaysMaximizeTestState(
248 window_state->GetStateType()))).Pass());
249 EXPECT_TRUE(window_state->IsMaximized());
250 window_state->SetStateObject(old.Pass());
251 EXPECT_FALSE(window_state->IsMaximized());
254 // TODO(skuhne): Add more unit test to verify the correctness for the restore