OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "ash/wm/workspace/workspace_layout_manager2.h" | |
6 | |
7 #include "ash/ash_switches.h" | |
8 #include "ash/screen_ash.h" | |
9 #include "ash/shell.h" | |
10 #include "ash/wm/always_on_top_controller.h" | |
11 #include "ash/wm/base_layout_manager.h" | |
12 #include "ash/wm/window_animations.h" | |
13 #include "ash/wm/window_properties.h" | |
14 #include "ash/wm/window_util.h" | |
15 #include "ash/wm/workspace/workspace2.h" | |
16 #include "ash/wm/workspace/workspace_manager2.h" | |
17 #include "ash/wm/workspace/workspace_window_resizer.h" | |
18 #include "base/auto_reset.h" | |
19 #include "base/command_line.h" | |
20 #include "ui/aura/client/aura_constants.h" | |
21 #include "ui/aura/root_window.h" | |
22 #include "ui/aura/window.h" | |
23 #include "ui/aura/window_observer.h" | |
24 #include "ui/base/events/event.h" | |
25 #include "ui/base/ui_base_types.h" | |
26 | |
27 using aura::Window; | |
28 | |
29 namespace ash { | |
30 | |
31 namespace internal { | |
32 | |
33 namespace { | |
34 | |
35 typedef std::map<const aura::Window*, gfx::Rect> BoundsMap; | |
36 | |
37 // Adds an entry from |window| to its bounds and recursively invokes this for | |
38 // all children. | |
39 void BuildWindowBoundsMap(const aura::Window* window, BoundsMap* bounds_map) { | |
40 (*bounds_map)[window] = window->bounds(); | |
41 for (size_t i = 0; i < window->children().size(); ++i) | |
42 BuildWindowBoundsMap(window->children()[i], bounds_map); | |
43 } | |
44 | |
45 // Resets |window|s bounds from |bounds_map| if currently empty. Recusively | |
46 // invokes this for all children. | |
47 void ResetBoundsIfNecessary(const BoundsMap& bounds_map, aura::Window* window) { | |
48 if (window->bounds().IsEmpty() && window->GetTargetBounds().IsEmpty()) { | |
49 BoundsMap::const_iterator i = bounds_map.find(window); | |
50 if (i != bounds_map.end()) | |
51 window->SetBounds(i->second); | |
52 } | |
53 for (size_t i = 0; i < window->children().size(); ++i) | |
54 ResetBoundsIfNecessary(bounds_map, window->children()[i]); | |
55 } | |
56 | |
57 // Resets |window|s bounds from |bounds_map| if |window| is marked as a | |
58 // constrained window. Recusively invokes this for all children. | |
59 // TODO(sky): this should key off window type. | |
60 void ResetConstrainedWindowBoundsIfNecessary(const BoundsMap& bounds_map, | |
61 aura::Window* window) { | |
62 if (window->GetProperty(aura::client::kConstrainedWindowKey)) { | |
63 BoundsMap::const_iterator i = bounds_map.find(window); | |
64 if (i != bounds_map.end()) | |
65 window->SetBounds(i->second); | |
66 } | |
67 for (size_t i = 0; i < window->children().size(); ++i) | |
68 ResetConstrainedWindowBoundsIfNecessary(bounds_map, window->children()[i]); | |
69 } | |
70 | |
71 } // namespace | |
72 | |
73 WorkspaceLayoutManager2::WorkspaceLayoutManager2(Workspace2* workspace) | |
74 : root_window_(workspace->window()->GetRootWindow()), | |
75 workspace_(workspace), | |
76 work_area_(ScreenAsh::GetDisplayWorkAreaBoundsInParent( | |
77 workspace->window()->parent())) { | |
78 Shell::GetInstance()->AddShellObserver(this); | |
79 root_window_->AddRootWindowObserver(this); | |
80 root_window_->AddObserver(this); | |
81 } | |
82 | |
83 WorkspaceLayoutManager2::~WorkspaceLayoutManager2() { | |
84 if (root_window_) { | |
85 root_window_->RemoveObserver(this); | |
86 root_window_->RemoveRootWindowObserver(this); | |
87 } | |
88 for (WindowSet::const_iterator i = windows_.begin(); i != windows_.end(); ++i) | |
89 (*i)->RemoveObserver(this); | |
90 Shell::GetInstance()->RemoveShellObserver(this); | |
91 } | |
92 | |
93 void WorkspaceLayoutManager2::OnWindowAddedToLayout(Window* child) { | |
94 // Adjust window bounds in case that the new child is out of the workspace. | |
95 AdjustWindowSizeForScreenChange(child, ADJUST_WINDOW_DISPLAY_INSETS_CHANGED); | |
96 | |
97 windows_.insert(child); | |
98 child->AddObserver(this); | |
99 | |
100 // Only update the bounds if the window has a show state that depends on the | |
101 // workspace area. | |
102 if (wm::IsWindowMaximized(child) || wm::IsWindowFullscreen(child)) | |
103 UpdateBoundsFromShowState(child); | |
104 | |
105 workspace_manager()->OnWindowAddedToWorkspace(workspace_, child); | |
106 } | |
107 | |
108 void WorkspaceLayoutManager2::OnWillRemoveWindowFromLayout(Window* child) { | |
109 windows_.erase(child); | |
110 child->RemoveObserver(this); | |
111 workspace_manager()->OnWillRemoveWindowFromWorkspace(workspace_, child); | |
112 } | |
113 | |
114 void WorkspaceLayoutManager2::OnWindowRemovedFromLayout(Window* child) { | |
115 workspace_manager()->OnWindowRemovedFromWorkspace(workspace_, child); | |
116 } | |
117 | |
118 void WorkspaceLayoutManager2::OnChildWindowVisibilityChanged(Window* child, | |
119 bool visible) { | |
120 if (visible && wm::IsWindowMinimized(child)) { | |
121 // Attempting to show a minimized window. Unminimize it. | |
122 child->SetProperty(aura::client::kShowStateKey, | |
123 child->GetProperty(internal::kRestoreShowStateKey)); | |
124 child->ClearProperty(internal::kRestoreShowStateKey); | |
125 } | |
126 workspace_manager()->OnWorkspaceChildWindowVisibilityChanged(workspace_, | |
127 child); | |
128 } | |
129 | |
130 void WorkspaceLayoutManager2::SetChildBounds( | |
131 Window* child, | |
132 const gfx::Rect& requested_bounds) { | |
133 if (!GetTrackedByWorkspace(child)) { | |
134 SetChildBoundsDirect(child, requested_bounds); | |
135 return; | |
136 } | |
137 gfx::Rect child_bounds(requested_bounds); | |
138 // Some windows rely on this to set their initial bounds. | |
139 if (!SetMaximizedOrFullscreenBounds(child)) { | |
140 // Non-maximized/full-screen windows have their size constrained to the | |
141 // work-area. | |
142 child_bounds.set_width(std::min(work_area_.width(), child_bounds.width())); | |
143 child_bounds.set_height( | |
144 std::min(work_area_.height(), child_bounds.height())); | |
145 SetChildBoundsDirect(child, child_bounds); | |
146 } | |
147 workspace_manager()->OnWorkspaceWindowChildBoundsChanged(workspace_, child); | |
148 } | |
149 | |
150 void WorkspaceLayoutManager2::OnRootWindowResized(const aura::RootWindow* root, | |
151 const gfx::Size& old_size) { | |
152 AdjustWindowSizesForScreenChange(ADJUST_WINDOW_SCREEN_SIZE_CHANGED); | |
153 } | |
154 | |
155 void WorkspaceLayoutManager2::OnDisplayWorkAreaInsetsChanged() { | |
156 if (workspace_manager()->active_workspace_ == workspace_) { | |
157 const gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent( | |
158 workspace_->window()->parent())); | |
159 if (work_area != work_area_) | |
160 AdjustWindowSizesForScreenChange(ADJUST_WINDOW_DISPLAY_INSETS_CHANGED); | |
161 } | |
162 } | |
163 | |
164 void WorkspaceLayoutManager2::OnWindowPropertyChanged(Window* window, | |
165 const void* key, | |
166 intptr_t old) { | |
167 if (key == aura::client::kShowStateKey) { | |
168 ui::WindowShowState old_state = static_cast<ui::WindowShowState>(old); | |
169 ui::WindowShowState new_state = | |
170 window->GetProperty(aura::client::kShowStateKey); | |
171 if (old_state != ui::SHOW_STATE_MINIMIZED && | |
172 GetRestoreBoundsInScreen(window) == NULL && | |
173 WorkspaceManager2::IsMaximizedState(new_state) && | |
174 !WorkspaceManager2::IsMaximizedState(old_state)) { | |
175 SetRestoreBoundsInParent(window, window->bounds()); | |
176 } | |
177 // When restoring from a minimized state, we want to restore to the | |
178 // previous (maybe L/R maximized) state. Since we do also want to keep the | |
179 // restore rectangle, we set the restore rectangle to the rectangle we want | |
180 // to restore to and restore it after we switched so that it is preserved. | |
181 gfx::Rect restore; | |
182 if (old_state == ui::SHOW_STATE_MINIMIZED && | |
183 (new_state == ui::SHOW_STATE_NORMAL || | |
184 new_state == ui::SHOW_STATE_DEFAULT) && | |
185 GetRestoreBoundsInScreen(window)) { | |
186 restore = *GetRestoreBoundsInScreen(window); | |
187 SetRestoreBoundsInScreen(window, window->bounds()); | |
188 } | |
189 | |
190 // If maximizing or restoring, clone the layer. WorkspaceManager will use it | |
191 // (and take ownership of it) when animating. Ideally we could use that of | |
192 // BaseLayoutManager, but that proves problematic. In particular when | |
193 // restoring we need to animate on top of the workspace animating in. | |
194 ui::Layer* cloned_layer = NULL; | |
195 BoundsMap bounds_map; | |
196 if (wm::IsActiveWindow(window) && | |
197 ((WorkspaceManager2::IsMaximizedState(new_state) && | |
198 wm::IsWindowStateNormal(old_state)) || | |
199 (!WorkspaceManager2::IsMaximizedState(new_state) && | |
200 WorkspaceManager2::IsMaximizedState(old_state) && | |
201 new_state != ui::SHOW_STATE_MINIMIZED))) { | |
202 BuildWindowBoundsMap(window, &bounds_map); | |
203 cloned_layer = wm::RecreateWindowLayers(window, false); | |
204 // Constrained windows don't get their bounds reset when we update the | |
205 // window bounds. Leaving them empty is unexpected, so we reset them now. | |
206 ResetConstrainedWindowBoundsIfNecessary(bounds_map, window); | |
207 } | |
208 UpdateBoundsFromShowState(window); | |
209 | |
210 if (cloned_layer) { | |
211 // Even though we just set the bounds not all descendants may have valid | |
212 // bounds. For example, constrained windows don't resize with the parent. | |
213 // Ensure that all windows that had a bounds before we cloned the layer | |
214 // have a bounds now. | |
215 ResetBoundsIfNecessary(bounds_map, window); | |
216 } | |
217 | |
218 ShowStateChanged(window, old_state, cloned_layer); | |
219 | |
220 // Set the restore rectangle to the previously set restore rectangle. | |
221 if (!restore.IsEmpty()) | |
222 SetRestoreBoundsInScreen(window, restore); | |
223 } | |
224 | |
225 if (key == internal::kWindowTrackedByWorkspaceKey && | |
226 GetTrackedByWorkspace(window)) { | |
227 workspace_manager()->OnTrackedByWorkspaceChanged(workspace_, window); | |
228 } | |
229 | |
230 if (key == aura::client::kAlwaysOnTopKey && | |
231 window->GetProperty(aura::client::kAlwaysOnTopKey)) { | |
232 internal::AlwaysOnTopController* controller = | |
233 window->GetRootWindow()->GetProperty( | |
234 internal::kAlwaysOnTopControllerKey); | |
235 controller->GetContainer(window)->AddChild(window); | |
236 } | |
237 } | |
238 | |
239 void WorkspaceLayoutManager2::OnWindowDestroying(aura::Window* window) { | |
240 if (root_window_ == window) { | |
241 root_window_->RemoveObserver(this); | |
242 root_window_ = NULL; | |
243 } | |
244 } | |
245 | |
246 void WorkspaceLayoutManager2::ShowStateChanged( | |
247 Window* window, | |
248 ui::WindowShowState last_show_state, | |
249 ui::Layer* cloned_layer) { | |
250 if (wm::IsWindowMinimized(window)) { | |
251 DCHECK(!cloned_layer); | |
252 // Save the previous show state so that we can correctly restore it. | |
253 window->SetProperty(internal::kRestoreShowStateKey, last_show_state); | |
254 SetWindowVisibilityAnimationType( | |
255 window, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); | |
256 workspace_manager()->OnWorkspaceWindowShowStateChanged( | |
257 workspace_, window, last_show_state, NULL); | |
258 window->Hide(); | |
259 if (wm::IsActiveWindow(window)) | |
260 wm::DeactivateWindow(window); | |
261 } else { | |
262 if ((window->TargetVisibility() || | |
263 last_show_state == ui::SHOW_STATE_MINIMIZED) && | |
264 !window->layer()->visible()) { | |
265 // The layer may be hidden if the window was previously minimized. Make | |
266 // sure it's visible. | |
267 window->Show(); | |
268 } | |
269 workspace_manager()->OnWorkspaceWindowShowStateChanged( | |
270 workspace_, window, last_show_state, cloned_layer); | |
271 } | |
272 } | |
273 | |
274 void WorkspaceLayoutManager2::AdjustWindowSizesForScreenChange( | |
275 AdjustWindowReason reason) { | |
276 work_area_ = ScreenAsh::GetDisplayWorkAreaBoundsInParent( | |
277 workspace_->window()->parent()); | |
278 // If a user plugs an external display into a laptop running Aura the | |
279 // display size will change. Maximized windows need to resize to match. | |
280 // We also do this when developers running Aura on a desktop manually resize | |
281 // the host window. | |
282 // We also need to do this when the work area insets changes. | |
283 for (WindowSet::const_iterator it = windows_.begin(); | |
284 it != windows_.end(); | |
285 ++it) { | |
286 AdjustWindowSizeForScreenChange(*it, reason); | |
287 } | |
288 } | |
289 | |
290 void WorkspaceLayoutManager2::AdjustWindowSizeForScreenChange( | |
291 Window* window, | |
292 AdjustWindowReason reason) { | |
293 if (GetTrackedByWorkspace(window) && | |
294 !SetMaximizedOrFullscreenBounds(window)) { | |
295 if (reason == ADJUST_WINDOW_SCREEN_SIZE_CHANGED) { | |
296 // The work area may be smaller than the full screen. Put as much of the | |
297 // window as possible within the display area. | |
298 gfx::Rect bounds = window->bounds(); | |
299 bounds.AdjustToFit(work_area_); | |
300 window->SetBounds(bounds); | |
301 } else if (reason == ADJUST_WINDOW_DISPLAY_INSETS_CHANGED) { | |
302 // Make sure the window isn't bigger than the display work area and that | |
303 // at least a portion of it is visible. | |
304 gfx::Rect bounds = window->bounds(); | |
305 bounds.set_width(std::min(bounds.width(), work_area_.width())); | |
306 bounds.set_height(std::min(bounds.height(), work_area_.height())); | |
307 if (!work_area_.Intersects(bounds)) { | |
308 int y_offset = 0; | |
309 if (work_area_.bottom() < bounds.y()) { | |
310 y_offset = work_area_.bottom() - bounds.y() - kMinimumOnScreenArea; | |
311 } else if (bounds.bottom() < work_area_.y()) { | |
312 y_offset = work_area_.y() - bounds.bottom() + kMinimumOnScreenArea; | |
313 } | |
314 | |
315 int x_offset = 0; | |
316 if (work_area_.right() < bounds.x()) { | |
317 x_offset = work_area_.right() - bounds.x() - kMinimumOnScreenArea; | |
318 } else if (bounds.right() < work_area_.x()) { | |
319 x_offset = work_area_.x() - bounds.right() + kMinimumOnScreenArea; | |
320 } | |
321 bounds.Offset(x_offset, y_offset); | |
322 } | |
323 if (window->bounds() != bounds) | |
324 window->SetBounds(bounds); | |
325 } | |
326 } | |
327 } | |
328 | |
329 void WorkspaceLayoutManager2::UpdateBoundsFromShowState(Window* window) { | |
330 // See comment in SetMaximizedOrFullscreenBounds() as to why we use parent in | |
331 // these calculation. | |
332 switch (window->GetProperty(aura::client::kShowStateKey)) { | |
333 case ui::SHOW_STATE_DEFAULT: | |
334 case ui::SHOW_STATE_NORMAL: { | |
335 const gfx::Rect* restore = GetRestoreBoundsInScreen(window); | |
336 if (restore) { | |
337 gfx::Rect bounds_in_parent = | |
338 ScreenAsh::ConvertRectFromScreen(window->parent()->parent(), | |
339 *restore); | |
340 SetChildBoundsDirect( | |
341 window, | |
342 BaseLayoutManager::BoundsWithScreenEdgeVisible( | |
343 window->parent()->parent(), | |
344 bounds_in_parent)); | |
345 } | |
346 window->ClearProperty(aura::client::kRestoreBoundsKey); | |
347 break; | |
348 } | |
349 | |
350 case ui::SHOW_STATE_MAXIMIZED: | |
351 case ui::SHOW_STATE_FULLSCREEN: | |
352 SetMaximizedOrFullscreenBounds(window); | |
353 break; | |
354 | |
355 default: | |
356 break; | |
357 } | |
358 } | |
359 | |
360 bool WorkspaceLayoutManager2::SetMaximizedOrFullscreenBounds( | |
361 aura::Window* window) { | |
362 if (!GetTrackedByWorkspace(window)) | |
363 return false; | |
364 | |
365 // During animations there is a transform installed on the workspace | |
366 // windows. For this reason this code uses the parent so that the transform is | |
367 // ignored. | |
368 if (wm::IsWindowMaximized(window)) { | |
369 SetChildBoundsDirect( | |
370 window, ScreenAsh::GetMaximizedWindowBoundsInParent( | |
371 window->parent()->parent())); | |
372 return true; | |
373 } | |
374 if (wm::IsWindowFullscreen(window)) { | |
375 SetChildBoundsDirect( | |
376 window, | |
377 ScreenAsh::GetDisplayBoundsInParent(window->parent()->parent())); | |
378 return true; | |
379 } | |
380 return false; | |
381 } | |
382 | |
383 WorkspaceManager2* WorkspaceLayoutManager2::workspace_manager() { | |
384 return workspace_->workspace_manager(); | |
385 } | |
386 | |
387 } // namespace internal | |
388 } // namespace ash | |
OLD | NEW |