• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
AlwaysMaximizeTestState(WindowStateType initial_state_type)21   explicit AlwaysMaximizeTestState(WindowStateType initial_state_type)
22       : state_type_(initial_state_type) {}
~AlwaysMaximizeTestState()23   virtual ~AlwaysMaximizeTestState() {}
24 
25   // WindowState::State overrides:
OnWMEvent(WindowState * window_state,const WMEvent * event)26   virtual void OnWMEvent(WindowState* window_state,
27                          const WMEvent* event) OVERRIDE {
28     // We don't do anything here.
29   }
GetType() const30   virtual WindowStateType GetType() const OVERRIDE {
31     return state_type_;
32   }
AttachState(WindowState * window_state,WindowState::State * previous_state)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   }
DetachState(WindowState * window_state)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.
TEST_F(WindowStateTest,SnapWindowBasic)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.
TEST_F(WindowStateTest,SnapWindowMinimumSize)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.
TEST_F(WindowStateTest,TestRespectMinimumSize)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.
TEST_F(WindowStateTest,TestIgnoreTooBigMinimumSize)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.
TEST_F(WindowStateTest,SnapWindowSetBounds)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.
TEST_F(WindowStateTest,RestoreBounds)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.
TEST_F(WindowStateTest,AutoManaged)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.
TEST_F(WindowStateTest,SimpleStateSwap)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.
TEST_F(WindowStateTest,StateSwapRestore)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