blob: 2fd56af26cbc9c4ba608936d3c21b34485ecf17f [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
Ben Murdocheb525c52013-07-10 11:40:50 +01007#include "ash/root_window_controller.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00008#include "ash/screen_ash.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +01009#include "ash/shelf/shelf_layout_manager.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000010#include "ash/shell.h"
11#include "ash/wm/always_on_top_controller.h"
12#include "ash/wm/base_layout_manager.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010013#include "ash/wm/frame_painter.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010014#include "ash/wm/property_util.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000015#include "ash/wm/window_animations.h"
16#include "ash/wm/window_properties.h"
17#include "ash/wm/window_util.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010018#include "ash/wm/workspace/auto_window_management.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000019#include "ui/aura/client/aura_constants.h"
20#include "ui/aura/root_window.h"
21#include "ui/aura/window.h"
22#include "ui/aura/window_observer.h"
23#include "ui/base/events/event.h"
24#include "ui/base/ui_base_types.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000025#include "ui/views/corewm/window_util.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000026
27using aura::Window;
28
29namespace ash {
30
31namespace internal {
32
33namespace {
34
Ben Murdochbb1529c2013-08-08 10:24:53 +010035// This specifies how much percent 30% of a window rect (width / height)
36// must be visible when the window is added to the workspace.
37const float kMinimumPercentOnScreenArea = 0.3f;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000038
Torne (Richard Coles)5e3f23d2013-06-11 16:24:11 +010039bool IsMaximizedState(ui::WindowShowState state) {
40 return state == ui::SHOW_STATE_MAXIMIZED ||
41 state == ui::SHOW_STATE_FULLSCREEN;
42}
43
Torne (Richard Coles)58218062012-11-14 11:43:16 +000044} // namespace
45
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010046WorkspaceLayoutManager::WorkspaceLayoutManager(aura::Window* window)
47 : BaseLayoutManager(window->GetRootWindow()),
48 shelf_(NULL),
49 window_(window),
Torne (Richard Coles)58218062012-11-14 11:43:16 +000050 work_area_(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010051 window->parent())) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000052}
53
54WorkspaceLayoutManager::~WorkspaceLayoutManager() {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000055}
56
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010057void WorkspaceLayoutManager::SetShelf(internal::ShelfLayoutManager* shelf) {
58 shelf_ = shelf;
59}
60
Torne (Richard Coles)58218062012-11-14 11:43:16 +000061void WorkspaceLayoutManager::OnWindowAddedToLayout(Window* child) {
Ben Murdochbb1529c2013-08-08 10:24:53 +010062 // Adjust window bounds in case that the new child is given the bounds that
63 // is out of the workspace. Exclude the case where bounds is empty
64 // (this happens when a views::Widget is created), or the window
65 // is added with the bounds because a user explicitly moved to
66 // this position (drag and drop for example).
67 if (!child->bounds().IsEmpty() &&
68 !wm::HasUserChangedWindowPositionOrSize(child))
69 AdjustWindowBoundsWhenAdded(child);
Ben Murdoch558790d2013-07-30 15:19:42 +010070 BaseLayoutManager::OnWindowAddedToLayout(child);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010071 UpdateDesktopVisibility();
72 RearrangeVisibleWindowOnShow(child);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000073}
74
75void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout(Window* child) {
Ben Murdoch558790d2013-07-30 15:19:42 +010076 BaseLayoutManager::OnWillRemoveWindowFromLayout(child);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010077 if (child->TargetVisibility())
78 RearrangeVisibleWindowOnHideOrRemove(child);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000079}
80
81void WorkspaceLayoutManager::OnWindowRemovedFromLayout(Window* child) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010082 BaseLayoutManager::OnWindowRemovedFromLayout(child);
83 UpdateDesktopVisibility();
Torne (Richard Coles)58218062012-11-14 11:43:16 +000084}
85
86void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child,
87 bool visible) {
Ben Murdoch558790d2013-07-30 15:19:42 +010088 BaseLayoutManager::OnChildWindowVisibilityChanged(child, visible);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010089 if (child->TargetVisibility())
90 RearrangeVisibleWindowOnShow(child);
91 else
92 RearrangeVisibleWindowOnHideOrRemove(child);
93 UpdateDesktopVisibility();
Torne (Richard Coles)58218062012-11-14 11:43:16 +000094}
95
96void WorkspaceLayoutManager::SetChildBounds(
97 Window* child,
98 const gfx::Rect& requested_bounds) {
99 if (!GetTrackedByWorkspace(child)) {
100 SetChildBoundsDirect(child, requested_bounds);
101 return;
102 }
103 gfx::Rect child_bounds(requested_bounds);
104 // Some windows rely on this to set their initial bounds.
105 if (!SetMaximizedOrFullscreenBounds(child)) {
106 // Non-maximized/full-screen windows have their size constrained to the
107 // work-area.
108 child_bounds.set_width(std::min(work_area_.width(), child_bounds.width()));
109 child_bounds.set_height(
110 std::min(work_area_.height(), child_bounds.height()));
111 SetChildBoundsDirect(child, child_bounds);
112 }
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100113 UpdateDesktopVisibility();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000114}
115
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000116void WorkspaceLayoutManager::OnDisplayWorkAreaInsetsChanged() {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100117 const gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
118 window_->parent()));
Ben Murdochbb1529c2013-08-08 10:24:53 +0100119 if (work_area != work_area_) {
120 AdjustAllWindowsBoundsForWorkAreaChange(
121 ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED);
122 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000123}
124
125void WorkspaceLayoutManager::OnWindowPropertyChanged(Window* window,
126 const void* key,
127 intptr_t old) {
128 if (key == aura::client::kShowStateKey) {
129 ui::WindowShowState old_state = static_cast<ui::WindowShowState>(old);
130 ui::WindowShowState new_state =
131 window->GetProperty(aura::client::kShowStateKey);
132 if (old_state != ui::SHOW_STATE_MINIMIZED &&
133 GetRestoreBoundsInScreen(window) == NULL &&
Torne (Richard Coles)5e3f23d2013-06-11 16:24:11 +0100134 IsMaximizedState(new_state) &&
135 !IsMaximizedState(old_state)) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000136 SetRestoreBoundsInParent(window, window->bounds());
137 }
138 // When restoring from a minimized state, we want to restore to the
139 // previous (maybe L/R maximized) state. Since we do also want to keep the
140 // restore rectangle, we set the restore rectangle to the rectangle we want
141 // to restore to and restore it after we switched so that it is preserved.
142 gfx::Rect restore;
143 if (old_state == ui::SHOW_STATE_MINIMIZED &&
144 (new_state == ui::SHOW_STATE_NORMAL ||
145 new_state == ui::SHOW_STATE_DEFAULT) &&
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000146 GetRestoreBoundsInScreen(window) &&
147 !GetWindowAlwaysRestoresToRestoreBounds(window)) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000148 restore = *GetRestoreBoundsInScreen(window);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100149 SetRestoreBoundsInScreen(window, window->GetBoundsInScreen());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000150 }
151
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000152 UpdateBoundsFromShowState(window);
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100153 ShowStateChanged(window, old_state);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000154
155 // Set the restore rectangle to the previously set restore rectangle.
156 if (!restore.IsEmpty())
157 SetRestoreBoundsInScreen(window, restore);
158 }
159
160 if (key == internal::kWindowTrackedByWorkspaceKey &&
161 GetTrackedByWorkspace(window)) {
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100162 SetMaximizedOrFullscreenBounds(window);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000163 }
164
165 if (key == aura::client::kAlwaysOnTopKey &&
166 window->GetProperty(aura::client::kAlwaysOnTopKey)) {
167 internal::AlwaysOnTopController* controller =
Ben Murdocheb525c52013-07-10 11:40:50 +0100168 GetRootWindowController(window->GetRootWindow())->
169 always_on_top_controller();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000170 controller->GetContainer(window)->AddChild(window);
171 }
172}
173
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000174void WorkspaceLayoutManager::ShowStateChanged(
175 Window* window,
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100176 ui::WindowShowState last_show_state) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100177 BaseLayoutManager::ShowStateChanged(window, last_show_state);
178 UpdateDesktopVisibility();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000179}
180
Ben Murdochbb1529c2013-08-08 10:24:53 +0100181void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange(
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000182 AdjustWindowReason reason) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100183 work_area_ = ScreenAsh::GetDisplayWorkAreaBoundsInParent(window_->parent());
Ben Murdochbb1529c2013-08-08 10:24:53 +0100184 BaseLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange(reason);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000185}
186
Ben Murdochbb1529c2013-08-08 10:24:53 +0100187void WorkspaceLayoutManager::AdjustWindowBoundsForWorkAreaChange(
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000188 Window* window,
189 AdjustWindowReason reason) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100190 if (!GetTrackedByWorkspace(window))
191 return;
192
193 // Use cross fade transition for the maximized window if the adjustment
194 // happens due to the shelf's visibility change. Otherwise the background
195 // can be seen slightly between the bottom edge of resized-window and
196 // the animating shelf.
197 // TODO(mukai): this cause slight blur at the window frame because of the
198 // cross fade. I think this is better, but should reconsider if someone
199 // raises voice for this.
200 if (wm::IsWindowMaximized(window) &&
Ben Murdochbb1529c2013-08-08 10:24:53 +0100201 reason == ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100202 CrossFadeToBounds(window, ScreenAsh::GetMaximizedWindowBoundsInParent(
203 window->parent()->parent()));
204 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000205 }
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100206
207 if (SetMaximizedOrFullscreenBounds(window))
208 return;
209
210 gfx::Rect bounds = window->bounds();
Ben Murdochbb1529c2013-08-08 10:24:53 +0100211 switch (reason) {
212 case ADJUST_WINDOW_DISPLAY_SIZE_CHANGED:
213 // The work area may be smaller than the full screen. Put as much of the
214 // window as possible within the display area.
215 bounds.AdjustToFit(work_area_);
216 break;
217 case ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED:
218 ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_, &bounds);
219 break;
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100220 }
221 if (window->bounds() != bounds)
222 window->SetBounds(bounds);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000223}
224
Ben Murdochbb1529c2013-08-08 10:24:53 +0100225void WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded(
226 Window* window) {
227 if (!GetTrackedByWorkspace(window))
228 return;
229
230 if (SetMaximizedOrFullscreenBounds(window))
231 return;
232
233 gfx::Rect bounds = window->bounds();
234 int min_width = bounds.width() * kMinimumPercentOnScreenArea;
235 int min_height = bounds.height() * kMinimumPercentOnScreenArea;
236 ash::wm::AdjustBoundsToEnsureWindowVisibility(
237 work_area_, min_width, min_height, &bounds);
238
239 if (window->bounds() != bounds)
240 window->SetBounds(bounds);
241}
242
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100243void WorkspaceLayoutManager::UpdateDesktopVisibility() {
244 if (shelf_)
245 shelf_->UpdateVisibilityState();
246 FramePainter::UpdateSoloWindowHeader(window_->GetRootWindow());
247}
248
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000249void WorkspaceLayoutManager::UpdateBoundsFromShowState(Window* window) {
250 // See comment in SetMaximizedOrFullscreenBounds() as to why we use parent in
251 // these calculation.
252 switch (window->GetProperty(aura::client::kShowStateKey)) {
253 case ui::SHOW_STATE_DEFAULT:
254 case ui::SHOW_STATE_NORMAL: {
255 const gfx::Rect* restore = GetRestoreBoundsInScreen(window);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100256 // Make sure that the part of the window is always visible
257 // when restored.
258 gfx::Rect bounds_in_parent;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000259 if (restore) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100260 bounds_in_parent =
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000261 ScreenAsh::ConvertRectFromScreen(window->parent()->parent(),
262 *restore);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100263
264 ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(
265 work_area_, &bounds_in_parent);
266 } else {
267 // Minimized windows have no restore bounds.
268 // Use the current bounds instead.
269 bounds_in_parent = window->bounds();
270 ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(
271 work_area_, &bounds_in_parent);
272 // Don't start animation if the bounds didn't change.
273 if (bounds_in_parent == window->bounds())
274 bounds_in_parent.SetRect(0, 0, 0, 0);
275 }
276 if (!bounds_in_parent.IsEmpty()) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100277 CrossFadeToBounds(
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000278 window,
279 BaseLayoutManager::BoundsWithScreenEdgeVisible(
280 window->parent()->parent(),
281 bounds_in_parent));
282 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000283 ClearRestoreBounds(window);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000284 break;
285 }
286
287 case ui::SHOW_STATE_MAXIMIZED:
Ben Murdocheb525c52013-07-10 11:40:50 +0100288 CrossFadeToBounds(window, ScreenAsh::GetMaximizedWindowBoundsInParent(
289 window->parent()->parent()));
290 break;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000291 case ui::SHOW_STATE_FULLSCREEN:
Ben Murdocha3f7b4e2013-07-24 10:36:34 +0100292 CrossFadeToBounds(window, ScreenAsh::GetDisplayBoundsInParent(
293 window->parent()->parent()));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000294 break;
295
296 default:
297 break;
298 }
299}
300
301bool WorkspaceLayoutManager::SetMaximizedOrFullscreenBounds(
302 aura::Window* window) {
303 if (!GetTrackedByWorkspace(window))
304 return false;
305
306 // During animations there is a transform installed on the workspace
307 // windows. For this reason this code uses the parent so that the transform is
308 // ignored.
309 if (wm::IsWindowMaximized(window)) {
310 SetChildBoundsDirect(
311 window, ScreenAsh::GetMaximizedWindowBoundsInParent(
312 window->parent()->parent()));
313 return true;
314 }
315 if (wm::IsWindowFullscreen(window)) {
316 SetChildBoundsDirect(
317 window,
318 ScreenAsh::GetDisplayBoundsInParent(window->parent()->parent()));
319 return true;
320 }
321 return false;
322}
323
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000324} // namespace internal
325} // namespace ash