blob: 3e3a1c7d655f35a518077b081b335d8c0a47e5d8 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 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/workspace/workspace_layout_manager.h"
6
7#include "ash/root_window_controller.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01008#include "ash/screen_ash.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00009#include "ash/shelf/shelf_layout_manager.h"
10#include "ash/shelf/shelf_widget.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000011#include "ash/shell.h"
12#include "ash/test/ash_test_base.h"
13#include "ash/wm/property_util.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000014#include "ash/wm/window_util.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000015#include "ui/aura/client/aura_constants.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000016#include "ui/aura/root_window.h"
17#include "ui/aura/test/test_windows.h"
18#include "ui/aura/window.h"
19#include "ui/gfx/insets.h"
20
21namespace ash {
22
23namespace {
24
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000025typedef test::AshTestBase WorkspaceLayoutManagerTest;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000026
27// Verifies that a window containing a restore coordinate will be restored to
28// to the size prior to minimize, keeping the restore rectangle in tact (if
29// there is one).
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010030TEST_F(WorkspaceLayoutManagerTest, RestoreFromMinimizeKeepsRestore) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000031 scoped_ptr<aura::Window> window(
32 CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 3, 4)));
Torne (Richard Coles)58218062012-11-14 11:43:16 +000033 gfx::Rect bounds(10, 15, 25, 35);
34 window->SetBounds(bounds);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010035 // This will not be used for un-minimizing window.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000036 SetRestoreBoundsInScreen(window.get(), gfx::Rect(0, 0, 100, 100));
37 wm::MinimizeWindow(window.get());
38 wm::RestoreWindow(window.get());
39 EXPECT_EQ("0,0 100x100", GetRestoreBoundsInScreen(window.get())->ToString());
40 EXPECT_EQ("10,15 25x35", window.get()->bounds().ToString());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010041
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010042 if (!SupportsMultipleDisplays())
43 return;
44
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010045 UpdateDisplay("400x300,500x400");
46 window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100),
47 ScreenAsh::GetSecondaryDisplay());
48 EXPECT_EQ(Shell::GetAllRootWindows()[1], window->GetRootWindow());
49 wm::MinimizeWindow(window.get());
50 // This will not be used for un-minimizing window.
51 SetRestoreBoundsInScreen(window.get(), gfx::Rect(0, 0, 100, 100));
52 wm::RestoreWindow(window.get());
53 EXPECT_EQ("600,0 100x100", window->GetBoundsInScreen().ToString());
54
55 // Make sure the unminimized window moves inside the display when
56 // 2nd display is disconnected.
57 wm::MinimizeWindow(window.get());
58 UpdateDisplay("400x300");
59 wm::RestoreWindow(window.get());
60 EXPECT_EQ(Shell::GetPrimaryRootWindow(), window->GetRootWindow());
61 EXPECT_TRUE(
62 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
Torne (Richard Coles)58218062012-11-14 11:43:16 +000063}
64
Ben Murdochbb1529c2013-08-08 10:24:53 +010065TEST_F(WorkspaceLayoutManagerTest, KeepRestoredWindowInDisplay) {
66 if (!SupportsHostWindowResize())
67 return;
68 scoped_ptr<aura::Window> window(
69 CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
70 // Maximized -> Normal transition.
71 wm::MaximizeWindow(window.get());
72 SetRestoreBoundsInScreen(window.get(), gfx::Rect(-100, -100, 30, 40));
73 wm::RestoreWindow(window.get());
74 EXPECT_TRUE(
75 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
76 EXPECT_EQ("-20,-30 30x40", window->bounds().ToString());
77
78 // Minimized -> Normal transition.
79 window->SetBounds(gfx::Rect(-100, -100, 30, 40));
80 wm::MinimizeWindow(window.get());
81 EXPECT_FALSE(
82 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
83 EXPECT_EQ("-100,-100 30x40", window->bounds().ToString());
84 window->Show();
85 EXPECT_TRUE(
86 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
87 EXPECT_EQ("-20,-30 30x40", window->bounds().ToString());
88
89 // Fullscreen -> Normal transition.
90 window->SetBounds(gfx::Rect(0, 0, 30, 40)); // reset bounds.
91 ASSERT_EQ("0,0 30x40", window->bounds().ToString());
92 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
93 EXPECT_EQ(window->bounds(), window->GetRootWindow()->bounds());
94 SetRestoreBoundsInScreen(window.get(), gfx::Rect(-100, -100, 30, 40));
95 wm::RestoreWindow(window.get());
96 EXPECT_TRUE(
97 Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
98 EXPECT_EQ("-20,-30 30x40", window->bounds().ToString());
99}
100
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000101// WindowObserver implementation used by DontClobberRestoreBoundsWindowObserver.
102// This code mirrors what BrowserFrameAura does. In particular when this code
103// sees the window was maximized it changes the bounds of a secondary
104// window. The secondary window mirrors the status window.
105class DontClobberRestoreBoundsWindowObserver : public aura::WindowObserver {
106 public:
107 DontClobberRestoreBoundsWindowObserver() : window_(NULL) {}
108
109 void set_window(aura::Window* window) { window_ = window; }
110
111 virtual void OnWindowPropertyChanged(aura::Window* window,
112 const void* key,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000113 intptr_t old) OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000114 if (!window_)
115 return;
116
117 if (wm::IsWindowMaximized(window)) {
118 aura::Window* w = window_;
119 window_ = NULL;
120
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000121 gfx::Rect shelf_bounds(Shell::GetPrimaryRootWindowController()->
122 GetShelfLayoutManager()->GetIdealBounds());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000123 const gfx::Rect& window_bounds(w->bounds());
124 w->SetBounds(gfx::Rect(window_bounds.x(), shelf_bounds.y() - 1,
125 window_bounds.width(), window_bounds.height()));
126 }
127 }
128
129 private:
130 aura::Window* window_;
131
132 DISALLOW_COPY_AND_ASSIGN(DontClobberRestoreBoundsWindowObserver);
133};
134
135// Creates a window, maximized the window and from within the maximized
136// notification sets the bounds of a window to overlap the shelf. Verifies this
137// doesn't effect the restore bounds.
138TEST_F(WorkspaceLayoutManagerTest, DontClobberRestoreBounds) {
139 DontClobberRestoreBoundsWindowObserver window_observer;
140 scoped_ptr<aura::Window> window(new aura::Window(NULL));
141 window->SetType(aura::client::WINDOW_TYPE_NORMAL);
142 window->Init(ui::LAYER_TEXTURED);
143 window->SetBounds(gfx::Rect(10, 20, 30, 40));
144 // NOTE: for this test to exercise the failure the observer needs to be added
145 // before the parent set. This mimics what BrowserFrameAura does.
146 window->AddObserver(&window_observer);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000147 SetDefaultParentByPrimaryRootWindow(window.get());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000148 window->Show();
Ben Murdochbb1529c2013-08-08 10:24:53 +0100149 wm::ActivateWindow(window.get());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000150
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000151 scoped_ptr<aura::Window> window2(
152 CreateTestWindowInShellWithBounds(gfx::Rect(12, 20, 30, 40)));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000153 window->AddTransientChild(window2.get());
154 window2->Show();
155
156 window_observer.set_window(window2.get());
157 wm::MaximizeWindow(window.get());
158 EXPECT_EQ("10,20 30x40", GetRestoreBoundsInScreen(window.get())->ToString());
159 window->RemoveObserver(&window_observer);
160}
161
162// Verifies when a window is maximized all descendant windows have a size.
163TEST_F(WorkspaceLayoutManagerTest, ChildBoundsResetOnMaximize) {
164 scoped_ptr<aura::Window> window(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000165 CreateTestWindowInShellWithBounds(gfx::Rect(10, 20, 30, 40)));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000166 window->Show();
Ben Murdochbb1529c2013-08-08 10:24:53 +0100167 wm::ActivateWindow(window.get());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000168 scoped_ptr<aura::Window> child_window(
169 aura::test::CreateTestWindowWithBounds(gfx::Rect(5, 6, 7, 8),
170 window.get()));
171 child_window->Show();
Ben Murdochbb1529c2013-08-08 10:24:53 +0100172 wm::MaximizeWindow(window.get());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000173 EXPECT_EQ("5,6 7x8", child_window->bounds().ToString());
174}
175
176TEST_F(WorkspaceLayoutManagerTest, WindowShouldBeOnScreenWhenAdded) {
177 // Normal window bounds shouldn't be changed.
178 gfx::Rect window_bounds(100, 100, 200, 200);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000179 scoped_ptr<aura::Window> window(
180 CreateTestWindowInShellWithBounds(window_bounds));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000181 EXPECT_EQ(window_bounds, window->bounds());
182
183 // If the window is out of the workspace, it would be moved on screen.
184 gfx::Rect root_window_bounds =
Ben Murdochbb1529c2013-08-08 10:24:53 +0100185 Shell::GetInstance()->GetPrimaryRootWindow()->bounds();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000186 window_bounds.Offset(root_window_bounds.width(), root_window_bounds.height());
187 ASSERT_FALSE(window_bounds.Intersects(root_window_bounds));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000188 scoped_ptr<aura::Window> out_window(
189 CreateTestWindowInShellWithBounds(window_bounds));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000190 EXPECT_EQ(window_bounds.size(), out_window->bounds().size());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000191 gfx::Rect bounds = out_window->bounds();
192 bounds.Intersect(root_window_bounds);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100193
194 // 30% of the window edge must be visible.
195 EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29);
196 EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29);
197
198 // Make sure we always make more than 1/3 of the window edge visible even
199 // if the initial bounds intersects with display.
200 window_bounds.SetRect(-150, -150, 200, 200);
201 bounds = window_bounds;
202 bounds.Intersect(root_window_bounds);
203
204 // Make sure that the initial bounds' visible area is less than 26%
205 // so that the auto adjustment logic kicks in.
206 ASSERT_LT(bounds.width(), out_window->bounds().width() * 0.26);
207 ASSERT_LT(bounds.height(), out_window->bounds().height() * 0.26);
208 ASSERT_TRUE(window_bounds.Intersects(root_window_bounds));
209
210 scoped_ptr<aura::Window> partially_out_window(
211 CreateTestWindowInShellWithBounds(window_bounds));
212 EXPECT_EQ(window_bounds.size(), partially_out_window->bounds().size());
213 bounds = partially_out_window->bounds();
214 bounds.Intersect(root_window_bounds);
215 EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29);
216 EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29);
217
218 // Make sure the window whose 30% width/height is bigger than display
219 // will be placed correctly.
220 window_bounds.SetRect(-1900, -1900, 3000, 3000);
221 scoped_ptr<aura::Window> window_bigger_than_display(
222 CreateTestWindowInShellWithBounds(window_bounds));
223 EXPECT_GE(root_window_bounds.width(),
224 window_bigger_than_display->bounds().width());
225 EXPECT_GE(root_window_bounds.height(),
226 window_bigger_than_display->bounds().height());
227
228 bounds = window_bigger_than_display->bounds();
229 bounds.Intersect(root_window_bounds);
230 EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29);
231 EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000232}
233
234// Verifies the size of a window is enforced to be smaller than the work area.
235TEST_F(WorkspaceLayoutManagerTest, SizeToWorkArea) {
236 // Normal window bounds shouldn't be changed.
237 gfx::Size work_area(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000238 Shell::GetScreen()->GetPrimaryDisplay().work_area().size());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000239 const gfx::Rect window_bounds(
240 100, 101, work_area.width() + 1, work_area.height() + 2);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000241 scoped_ptr<aura::Window> window(
242 CreateTestWindowInShellWithBounds(window_bounds));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000243 EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(),
244 window->bounds().ToString());
245
246 // Directly setting the bounds triggers a slightly different code path. Verify
247 // that too.
248 window->SetBounds(window_bounds);
249 EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(),
250 window->bounds().ToString());
251}
252
253} // namespace
254} // namespace ash