Cleanup: move AlwaysOnTopController to RootWindowController
[chromium-blink-merge.git] / ash / wm / workspace / workspace_layout_manager.cc
blob15052ee93e05e0c227d29be61bbf72d2c9ad97e3
1 // 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.
5 #include "ash/wm/workspace/workspace_layout_manager.h"
7 #include "ash/ash_switches.h"
8 #include "ash/root_window_controller.h"
9 #include "ash/screen_ash.h"
10 #include "ash/session_state_delegate.h"
11 #include "ash/shell.h"
12 #include "ash/wm/always_on_top_controller.h"
13 #include "ash/wm/base_layout_manager.h"
14 #include "ash/wm/property_util.h"
15 #include "ash/wm/window_animations.h"
16 #include "ash/wm/window_properties.h"
17 #include "ash/wm/window_util.h"
18 #include "ash/wm/workspace/workspace.h"
19 #include "ash/wm/workspace/workspace_manager.h"
20 #include "ash/wm/workspace/workspace_window_resizer.h"
21 #include "base/auto_reset.h"
22 #include "base/command_line.h"
23 #include "ui/aura/client/aura_constants.h"
24 #include "ui/aura/root_window.h"
25 #include "ui/aura/window.h"
26 #include "ui/aura/window_observer.h"
27 #include "ui/base/events/event.h"
28 #include "ui/base/ui_base_types.h"
29 #include "ui/views/corewm/window_util.h"
31 using aura::Window;
33 namespace ash {
35 namespace internal {
37 namespace {
39 // This specifies how much percent (2/3=66%) of a window must be visible when
40 // the window is added to the workspace.
41 const float kMinimumPercentOnScreenArea = 0.66f;
43 typedef std::map<const aura::Window*, gfx::Rect> BoundsMap;
45 // Adds an entry from |window| to its bounds and recursively invokes this for
46 // all children.
47 void BuildWindowBoundsMap(const aura::Window* window, BoundsMap* bounds_map) {
48 (*bounds_map)[window] = window->bounds();
49 for (size_t i = 0; i < window->children().size(); ++i)
50 BuildWindowBoundsMap(window->children()[i], bounds_map);
53 // Resets |window|s bounds from |bounds_map| if currently empty. Recursively
54 // invokes this for all children.
55 void ResetBoundsIfNecessary(const BoundsMap& bounds_map, aura::Window* window) {
56 if (window->bounds().IsEmpty() && window->GetTargetBounds().IsEmpty()) {
57 BoundsMap::const_iterator i = bounds_map.find(window);
58 if (i != bounds_map.end())
59 window->SetBounds(i->second);
61 for (size_t i = 0; i < window->children().size(); ++i)
62 ResetBoundsIfNecessary(bounds_map, window->children()[i]);
65 // Resets |window|s bounds from |bounds_map| if |window| is marked as a
66 // constrained window. Recursively invokes this for all children.
67 // TODO(sky): this should key off window type.
68 void ResetConstrainedWindowBoundsIfNecessary(const BoundsMap& bounds_map,
69 aura::Window* window) {
70 if (window->GetProperty(aura::client::kConstrainedWindowKey)) {
71 BoundsMap::const_iterator i = bounds_map.find(window);
72 if (i != bounds_map.end())
73 window->SetBounds(i->second);
75 for (size_t i = 0; i < window->children().size(); ++i)
76 ResetConstrainedWindowBoundsIfNecessary(bounds_map, window->children()[i]);
79 bool IsMaximizedState(ui::WindowShowState state) {
80 return state == ui::SHOW_STATE_MAXIMIZED ||
81 state == ui::SHOW_STATE_FULLSCREEN;
84 } // namespace
86 WorkspaceLayoutManager::WorkspaceLayoutManager(Workspace* workspace)
87 : root_window_(workspace->window()->GetRootWindow()),
88 workspace_(workspace),
89 work_area_(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
90 workspace->window()->parent())) {
91 Shell::GetInstance()->AddShellObserver(this);
92 root_window_->AddObserver(this);
95 WorkspaceLayoutManager::~WorkspaceLayoutManager() {
96 if (root_window_)
97 root_window_->RemoveObserver(this);
98 for (WindowSet::const_iterator i = windows_.begin(); i != windows_.end(); ++i)
99 (*i)->RemoveObserver(this);
100 Shell::GetInstance()->RemoveShellObserver(this);
103 void WorkspaceLayoutManager::OnWindowAddedToLayout(Window* child) {
104 // Adjust window bounds in case that the new child is out of the workspace.
105 AdjustWindowSizeForScreenChange(child, ADJUST_WINDOW_WINDOW_ADDED);
107 windows_.insert(child);
108 child->AddObserver(this);
110 // Only update the bounds if the window has a show state that depends on the
111 // workspace area.
112 if (wm::IsWindowMaximized(child) || wm::IsWindowFullscreen(child))
113 UpdateBoundsFromShowState(child);
115 workspace_manager()->OnWindowAddedToWorkspace(workspace_, child);
118 void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout(Window* child) {
119 windows_.erase(child);
120 child->RemoveObserver(this);
121 workspace_manager()->OnWillRemoveWindowFromWorkspace(workspace_, child);
124 void WorkspaceLayoutManager::OnWindowRemovedFromLayout(Window* child) {
125 workspace_manager()->OnWindowRemovedFromWorkspace(workspace_, child);
128 void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child,
129 bool visible) {
130 if (visible && wm::IsWindowMinimized(child)) {
131 // Attempting to show a minimized window. Unminimize it.
132 child->SetProperty(aura::client::kShowStateKey,
133 child->GetProperty(aura::client::kRestoreShowStateKey));
134 child->ClearProperty(aura::client::kRestoreShowStateKey);
136 workspace_manager()->OnWorkspaceChildWindowVisibilityChanged(workspace_,
137 child);
140 void WorkspaceLayoutManager::SetChildBounds(
141 Window* child,
142 const gfx::Rect& requested_bounds) {
143 if (!GetTrackedByWorkspace(child)) {
144 SetChildBoundsDirect(child, requested_bounds);
145 return;
147 gfx::Rect child_bounds(requested_bounds);
148 // Some windows rely on this to set their initial bounds.
149 if (!SetMaximizedOrFullscreenBounds(child)) {
150 // Non-maximized/full-screen windows have their size constrained to the
151 // work-area.
152 child_bounds.set_width(std::min(work_area_.width(), child_bounds.width()));
153 child_bounds.set_height(
154 std::min(work_area_.height(), child_bounds.height()));
155 SetChildBoundsDirect(child, child_bounds);
157 workspace_manager()->OnWorkspaceWindowChildBoundsChanged(workspace_, child);
160 void WorkspaceLayoutManager::OnDisplayWorkAreaInsetsChanged() {
161 if (workspace_manager()->active_workspace_ == workspace_) {
162 const gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
163 workspace_->window()->parent()));
164 if (work_area != work_area_)
165 AdjustWindowSizesForScreenChange(ADJUST_WINDOW_DISPLAY_INSETS_CHANGED);
169 void WorkspaceLayoutManager::OnWindowPropertyChanged(Window* window,
170 const void* key,
171 intptr_t old) {
172 if (key == aura::client::kShowStateKey) {
173 ui::WindowShowState old_state = static_cast<ui::WindowShowState>(old);
174 ui::WindowShowState new_state =
175 window->GetProperty(aura::client::kShowStateKey);
176 if (old_state != ui::SHOW_STATE_MINIMIZED &&
177 GetRestoreBoundsInScreen(window) == NULL &&
178 IsMaximizedState(new_state) &&
179 !IsMaximizedState(old_state)) {
180 SetRestoreBoundsInParent(window, window->bounds());
182 // When restoring from a minimized state, we want to restore to the
183 // previous (maybe L/R maximized) state. Since we do also want to keep the
184 // restore rectangle, we set the restore rectangle to the rectangle we want
185 // to restore to and restore it after we switched so that it is preserved.
186 gfx::Rect restore;
187 if (old_state == ui::SHOW_STATE_MINIMIZED &&
188 (new_state == ui::SHOW_STATE_NORMAL ||
189 new_state == ui::SHOW_STATE_DEFAULT) &&
190 GetRestoreBoundsInScreen(window) &&
191 !GetWindowAlwaysRestoresToRestoreBounds(window)) {
192 restore = *GetRestoreBoundsInScreen(window);
193 SetRestoreBoundsInScreen(window, window->GetBoundsInScreen());
196 // If the new state requires |window| to be in a workspace, clone the layer.
197 // WorkspaceManager will use it (and take ownership of it) when animating.
198 // Ideally we could use that of BaseLayoutManager, but that proves
199 // problematic. In particular when restoring we need to animate on top of
200 // the workspace animating in.
201 ui::Layer* cloned_layer = NULL;
202 BoundsMap bounds_map;
203 if (wm::IsActiveWindow(window) &&
204 ((new_state == ui::SHOW_STATE_FULLSCREEN &&
205 wm::IsWindowStateNormal(old_state)) ||
206 (new_state != ui::SHOW_STATE_FULLSCREEN &&
207 old_state == ui::SHOW_STATE_FULLSCREEN &&
208 new_state != ui::SHOW_STATE_MINIMIZED))) {
209 BuildWindowBoundsMap(window, &bounds_map);
210 cloned_layer = views::corewm::RecreateWindowLayers(window, false);
211 // Constrained windows don't get their bounds reset when we update the
212 // window bounds. Leaving them empty is unexpected, so we reset them now.
213 ResetConstrainedWindowBoundsIfNecessary(bounds_map, window);
215 UpdateBoundsFromShowState(window);
217 if (cloned_layer) {
218 // Even though we just set the bounds not all descendants may have valid
219 // bounds. For example, constrained windows don't resize with the parent.
220 // Ensure that all windows that had a bounds before we cloned the layer
221 // have a bounds now.
222 ResetBoundsIfNecessary(bounds_map, window);
225 ShowStateChanged(window, old_state, cloned_layer);
227 // Set the restore rectangle to the previously set restore rectangle.
228 if (!restore.IsEmpty())
229 SetRestoreBoundsInScreen(window, restore);
232 if (key == internal::kWindowTrackedByWorkspaceKey &&
233 GetTrackedByWorkspace(window)) {
234 workspace_manager()->OnTrackedByWorkspaceChanged(workspace_, window);
235 if (wm::IsWindowMaximized(window)) {
236 SetChildBoundsDirect(
237 window, ScreenAsh::GetMaximizedWindowBoundsInParent(
238 window->parent()->parent()));
242 if (key == aura::client::kAlwaysOnTopKey &&
243 window->GetProperty(aura::client::kAlwaysOnTopKey)) {
244 internal::AlwaysOnTopController* controller =
245 GetRootWindowController(window->GetRootWindow())->
246 always_on_top_controller();
247 controller->GetContainer(window)->AddChild(window);
251 void WorkspaceLayoutManager::OnWindowDestroying(aura::Window* window) {
252 if (root_window_ == window) {
253 root_window_->RemoveObserver(this);
254 root_window_ = NULL;
258 void WorkspaceLayoutManager::OnWindowBoundsChanged(
259 aura::Window* window,
260 const gfx::Rect& old_bounds,
261 const gfx::Rect& new_bounds) {
262 if (root_window_ == window)
263 AdjustWindowSizesForScreenChange(ADJUST_WINDOW_SCREEN_SIZE_CHANGED);
266 void WorkspaceLayoutManager::ShowStateChanged(
267 Window* window,
268 ui::WindowShowState last_show_state,
269 ui::Layer* cloned_layer) {
270 if (wm::IsWindowMinimized(window)) {
271 DCHECK(!cloned_layer);
272 // Save the previous show state so that we can correctly restore it.
273 window->SetProperty(aura::client::kRestoreShowStateKey, last_show_state);
274 views::corewm::SetWindowVisibilityAnimationType(
275 window, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
276 workspace_manager()->OnWorkspaceWindowShowStateChanged(
277 workspace_, window, last_show_state, NULL);
278 window->Hide();
279 if (wm::IsActiveWindow(window))
280 wm::DeactivateWindow(window);
281 } else {
282 if ((window->TargetVisibility() ||
283 last_show_state == ui::SHOW_STATE_MINIMIZED) &&
284 !window->layer()->visible()) {
285 // The layer may be hidden if the window was previously minimized. Make
286 // sure it's visible.
287 window->Show();
289 if (last_show_state == ui::SHOW_STATE_MINIMIZED &&
290 !wm::IsWindowMaximized(window) &&
291 !wm::IsWindowFullscreen(window)) {
292 window->ClearProperty(internal::kWindowRestoresToRestoreBounds);
294 workspace_manager()->OnWorkspaceWindowShowStateChanged(
295 workspace_, window, last_show_state, cloned_layer);
299 void WorkspaceLayoutManager::AdjustWindowSizesForScreenChange(
300 AdjustWindowReason reason) {
301 // Don't do any adjustments of the insets while we are in screen locked mode.
302 // This would happen if the launcher was auto hidden before the login screen
303 // was shown and then gets shown when the login screen gets presented.
304 if (reason == ADJUST_WINDOW_DISPLAY_INSETS_CHANGED &&
305 Shell::GetInstance()->session_state_delegate()->IsScreenLocked())
306 return;
307 work_area_ = ScreenAsh::GetDisplayWorkAreaBoundsInParent(
308 workspace_->window()->parent());
309 // If a user plugs an external display into a laptop running Aura the
310 // display size will change. Maximized windows need to resize to match.
311 // We also do this when developers running Aura on a desktop manually resize
312 // the host window.
313 // We also need to do this when the work area insets changes.
314 for (WindowSet::const_iterator it = windows_.begin();
315 it != windows_.end();
316 ++it) {
317 AdjustWindowSizeForScreenChange(*it, reason);
321 void WorkspaceLayoutManager::AdjustWindowSizeForScreenChange(
322 Window* window,
323 AdjustWindowReason reason) {
324 if (GetTrackedByWorkspace(window) &&
325 !SetMaximizedOrFullscreenBounds(window)) {
326 if (reason == ADJUST_WINDOW_SCREEN_SIZE_CHANGED) {
327 // The work area may be smaller than the full screen. Put as much of the
328 // window as possible within the display area.
329 gfx::Rect bounds = window->bounds();
330 bounds.AdjustToFit(work_area_);
331 window->SetBounds(bounds);
332 } else if (reason == ADJUST_WINDOW_DISPLAY_INSETS_CHANGED) {
333 gfx::Rect bounds = window->bounds();
334 ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_, &bounds);
335 if (window->bounds() != bounds)
336 window->SetBounds(bounds);
337 } else if (reason == ADJUST_WINDOW_WINDOW_ADDED) {
338 gfx::Rect bounds = window->bounds();
339 int min_width = bounds.width() * kMinimumPercentOnScreenArea;
340 int min_height = bounds.height() * kMinimumPercentOnScreenArea;
341 ash::wm::AdjustBoundsToEnsureWindowVisibility(
342 work_area_, min_width, min_height, &bounds);
343 if (window->bounds() != bounds)
344 window->SetBounds(bounds);
349 void WorkspaceLayoutManager::UpdateBoundsFromShowState(Window* window) {
350 // See comment in SetMaximizedOrFullscreenBounds() as to why we use parent in
351 // these calculation.
352 switch (window->GetProperty(aura::client::kShowStateKey)) {
353 case ui::SHOW_STATE_DEFAULT:
354 case ui::SHOW_STATE_NORMAL: {
355 const gfx::Rect* restore = GetRestoreBoundsInScreen(window);
356 if (restore) {
357 gfx::Rect bounds_in_parent =
358 ScreenAsh::ConvertRectFromScreen(window->parent()->parent(),
359 *restore);
360 SetChildBoundsDirect(
361 window,
362 BaseLayoutManager::BoundsWithScreenEdgeVisible(
363 window->parent()->parent(),
364 bounds_in_parent));
366 ClearRestoreBounds(window);
367 break;
370 case ui::SHOW_STATE_MAXIMIZED:
371 case ui::SHOW_STATE_FULLSCREEN:
372 SetMaximizedOrFullscreenBounds(window);
373 break;
375 default:
376 break;
380 bool WorkspaceLayoutManager::SetMaximizedOrFullscreenBounds(
381 aura::Window* window) {
382 if (!GetTrackedByWorkspace(window))
383 return false;
385 // During animations there is a transform installed on the workspace
386 // windows. For this reason this code uses the parent so that the transform is
387 // ignored.
388 if (wm::IsWindowMaximized(window)) {
389 SetChildBoundsDirect(
390 window, ScreenAsh::GetMaximizedWindowBoundsInParent(
391 window->parent()->parent()));
392 return true;
394 if (wm::IsWindowFullscreen(window)) {
395 SetChildBoundsDirect(
396 window,
397 ScreenAsh::GetDisplayBoundsInParent(window->parent()->parent()));
398 return true;
400 return false;
403 WorkspaceManager* WorkspaceLayoutManager::workspace_manager() {
404 return workspace_->workspace_manager();
407 } // namespace internal
408 } // namespace ash