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/common/workspace/workspace_layout_manager.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "ash/wm/common/always_on_top_controller.h" | |
10 #include "ash/wm/common/fullscreen_window_finder.h" | |
11 #include "ash/wm/common/window_positioner.h" | |
12 #include "ash/wm/common/window_state.h" | |
13 #include "ash/wm/common/wm_event.h" | |
14 #include "ash/wm/common/wm_globals.h" | |
15 #include "ash/wm/common/wm_root_window_controller.h" | |
16 #include "ash/wm/common/wm_screen_util.h" | |
17 #include "ash/wm/common/wm_window.h" | |
18 #include "ash/wm/common/wm_window_property.h" | |
19 #include "ash/wm/common/workspace/workspace_layout_manager_backdrop_delegate.h" | |
20 #include "ash/wm/common/workspace/workspace_layout_manager_delegate.h" | |
21 #include "ui/compositor/layer.h" | |
22 #include "ui/keyboard/keyboard_controller_observer.h" | |
23 | |
24 namespace ash { | |
25 | |
26 WorkspaceLayoutManager::WorkspaceLayoutManager( | |
27 wm::WmWindow* window, | |
28 std::unique_ptr<wm::WorkspaceLayoutManagerDelegate> delegate) | |
29 : window_(window), | |
30 root_window_(window->GetRootWindow()), | |
31 root_window_controller_(root_window_->GetRootWindowController()), | |
32 globals_(window_->GetGlobals()), | |
33 delegate_(std::move(delegate)), | |
34 work_area_in_parent_(wm::GetDisplayWorkAreaBounds(window_)), | |
35 is_fullscreen_(wm::GetWindowForFullscreenMode(window) != nullptr) { | |
36 globals_->AddActivationObserver(this); | |
37 root_window_->AddObserver(this); | |
38 root_window_controller_->AddObserver(this); | |
39 DCHECK(window->GetBoolProperty( | |
40 wm::WmWindowProperty::SNAP_CHILDREN_TO_PIXEL_BOUNDARY)); | |
41 } | |
42 | |
43 WorkspaceLayoutManager::~WorkspaceLayoutManager() { | |
44 if (root_window_) | |
45 root_window_->RemoveObserver(this); | |
46 for (wm::WmWindow* window : windows_) | |
47 window->RemoveObserver(this); | |
48 root_window_->GetRootWindowController()->RemoveObserver(this); | |
49 globals_->RemoveActivationObserver(this); | |
50 } | |
51 | |
52 void WorkspaceLayoutManager::DeleteDelegate() { | |
53 delegate_.reset(); | |
54 } | |
55 | |
56 void WorkspaceLayoutManager::SetMaximizeBackdropDelegate( | |
57 std::unique_ptr<WorkspaceLayoutManagerBackdropDelegate> delegate) { | |
58 backdrop_delegate_.reset(delegate.release()); | |
59 } | |
60 | |
61 ////////////////////////////////////////////////////////////////////////////// | |
62 // WorkspaceLayoutManager, aura::LayoutManager implementation: | |
63 | |
64 void WorkspaceLayoutManager::OnWindowResized() {} | |
65 | |
66 void WorkspaceLayoutManager::OnWindowAddedToLayout(wm::WmWindow* child) { | |
67 wm::WindowState* window_state = child->GetWindowState(); | |
68 wm::WMEvent event(wm::WM_EVENT_ADDED_TO_WORKSPACE); | |
69 window_state->OnWMEvent(&event); | |
70 windows_.insert(child); | |
71 child->AddObserver(this); | |
72 window_state->AddObserver(this); | |
73 UpdateShelfVisibility(); | |
74 UpdateFullscreenState(); | |
75 if (backdrop_delegate_) | |
76 backdrop_delegate_->OnWindowAddedToLayout(child); | |
77 WindowPositioner::RearrangeVisibleWindowOnShow(child); | |
78 } | |
79 | |
80 void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout(wm::WmWindow* child) { | |
81 windows_.erase(child); | |
82 child->RemoveObserver(this); | |
83 child->GetWindowState()->RemoveObserver(this); | |
84 | |
85 if (child->GetTargetVisibility()) | |
86 WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child); | |
87 } | |
88 | |
89 void WorkspaceLayoutManager::OnWindowRemovedFromLayout(wm::WmWindow* child) { | |
90 UpdateShelfVisibility(); | |
91 UpdateFullscreenState(); | |
92 if (backdrop_delegate_) | |
93 backdrop_delegate_->OnWindowRemovedFromLayout(child); | |
94 } | |
95 | |
96 void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(wm::WmWindow* child, | |
97 bool visible) { | |
98 wm::WindowState* window_state = child->GetWindowState(); | |
99 // Attempting to show a minimized window. Unminimize it. | |
100 if (visible && window_state->IsMinimized()) | |
101 window_state->Unminimize(); | |
102 | |
103 if (child->GetTargetVisibility()) | |
104 WindowPositioner::RearrangeVisibleWindowOnShow(child); | |
105 else | |
106 WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child); | |
107 UpdateFullscreenState(); | |
108 UpdateShelfVisibility(); | |
109 if (backdrop_delegate_) | |
110 backdrop_delegate_->OnChildWindowVisibilityChanged(child, visible); | |
111 } | |
112 | |
113 void WorkspaceLayoutManager::SetChildBounds(wm::WmWindow* child, | |
114 const gfx::Rect& requested_bounds) { | |
115 wm::SetBoundsEvent event(wm::WM_EVENT_SET_BOUNDS, requested_bounds); | |
116 child->GetWindowState()->OnWMEvent(&event); | |
117 UpdateShelfVisibility(); | |
118 } | |
119 | |
120 ////////////////////////////////////////////////////////////////////////////// | |
121 // WorkspaceLayoutManager, keyboard::KeyboardControllerObserver implementation: | |
122 | |
123 void WorkspaceLayoutManager::OnKeyboardBoundsChanging( | |
124 const gfx::Rect& new_bounds) { | |
125 wm::WmWindow* window = globals_->GetActiveWindow(); | |
126 if (!window) | |
127 return; | |
128 | |
129 window = window->GetToplevelWindow(); | |
130 if (!window_->Contains(window)) | |
131 return; | |
132 wm::WindowState* window_state = window->GetWindowState(); | |
133 if (!new_bounds.IsEmpty()) { | |
134 // Store existing bounds to be restored before resizing for keyboard if it | |
135 // is not already stored. | |
136 if (!window_state->HasRestoreBounds()) | |
137 window_state->SaveCurrentBoundsForRestore(); | |
138 | |
139 gfx::Rect window_bounds = | |
140 window_->ConvertRectToScreen(window->GetTargetBounds()); | |
141 int vertical_displacement = | |
142 std::max(0, window_bounds.bottom() - new_bounds.y()); | |
143 int shift = std::min(vertical_displacement, | |
144 window_bounds.y() - work_area_in_parent_.y()); | |
145 if (shift > 0) { | |
146 gfx::Point origin(window_bounds.x(), window_bounds.y() - shift); | |
147 SetChildBounds(window, gfx::Rect(origin, window_bounds.size())); | |
148 } | |
149 } else if (window_state->HasRestoreBounds()) { | |
150 // Keyboard hidden, restore original bounds if they exist. If the user has | |
151 // resized or dragged the window in the meantime, WorkspaceWindowResizer | |
152 // will have cleared the restore bounds and this code will not accidentally | |
153 // override user intent. | |
154 window_state->SetAndClearRestoreBounds(); | |
155 } | |
156 } | |
157 | |
158 ////////////////////////////////////////////////////////////////////////////// | |
159 // WorkspaceLayoutManager, wm::WmRootWindowControllerObserver implementation: | |
160 | |
161 void WorkspaceLayoutManager::OnWorkAreaChanged() { | |
162 const gfx::Rect work_area(wm::GetDisplayWorkAreaBounds(window_)); | |
163 if (work_area != work_area_in_parent_) { | |
164 const wm::WMEvent event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED); | |
165 AdjustAllWindowsBoundsForWorkAreaChange(&event); | |
166 } | |
167 if (backdrop_delegate_) | |
168 backdrop_delegate_->OnDisplayWorkAreaInsetsChanged(); | |
169 } | |
170 | |
171 void WorkspaceLayoutManager::OnFullscreenStateChanged(bool is_fullscreen) { | |
172 if (is_fullscreen_ == is_fullscreen) | |
173 return; | |
174 | |
175 is_fullscreen_ = is_fullscreen; | |
176 wm::WmWindow* fullscreen_window = | |
177 is_fullscreen ? GetWindowForFullscreenMode(window_) : nullptr; | |
178 // Changing always on top state may change window's parent. Iterate on a copy | |
179 // of |windows_| to avoid invalidating an iterator. Since both workspace and | |
180 // always_on_top containers' layouts are managed by this class all the | |
181 // appropriate windows will be included in the iteration. | |
182 WindowSet windows(windows_); | |
183 for (auto window : windows) { | |
184 wm::WindowState* window_state = window->GetWindowState(); | |
185 if (is_fullscreen) | |
186 window_state->DisableAlwaysOnTop(fullscreen_window); | |
187 else | |
188 window_state->RestoreAlwaysOnTop(); | |
189 } | |
190 } | |
191 | |
192 ////////////////////////////////////////////////////////////////////////////// | |
193 // WorkspaceLayoutManager, aura::WindowObserver implementation: | |
194 | |
195 void WorkspaceLayoutManager::OnWindowTreeChanged( | |
196 wm::WmWindow* window, | |
197 const wm::WmWindowObserver::TreeChangeParams& params) { | |
198 if (!params.target->GetWindowState()->IsActive()) | |
199 return; | |
200 // If the window is already tracked by the workspace this update would be | |
201 // redundant as the fullscreen and shelf state would have been handled in | |
202 // OnWindowAddedToLayout. | |
203 if (windows_.find(params.target) != windows_.end()) | |
204 return; | |
205 | |
206 // If the active window has moved to this root window then update the | |
207 // fullscreen state. | |
208 // TODO(flackr): Track the active window leaving this root window and update | |
209 // the fullscreen state accordingly. | |
210 if (params.new_parent && params.new_parent->GetRootWindow() == root_window_) { | |
211 UpdateFullscreenState(); | |
212 UpdateShelfVisibility(); | |
213 } | |
214 } | |
215 | |
216 void WorkspaceLayoutManager::OnWindowPropertyChanged( | |
217 wm::WmWindow* window, | |
218 wm::WmWindowProperty property) { | |
219 if (property == wm::WmWindowProperty::ALWAYS_ON_TOP && | |
220 window->GetBoolProperty(wm::WmWindowProperty::ALWAYS_ON_TOP)) { | |
221 root_window_controller_->GetAlwaysOnTopController() | |
222 ->GetContainer(window) | |
223 ->AddChild(window); | |
224 } | |
225 } | |
226 | |
227 void WorkspaceLayoutManager::OnWindowStackingChanged(wm::WmWindow* window) { | |
228 UpdateShelfVisibility(); | |
229 UpdateFullscreenState(); | |
230 if (backdrop_delegate_) | |
231 backdrop_delegate_->OnWindowStackingChanged(window); | |
232 } | |
233 | |
234 void WorkspaceLayoutManager::OnWindowDestroying(wm::WmWindow* window) { | |
235 if (root_window_ == window) { | |
236 root_window_->RemoveObserver(this); | |
237 root_window_ = nullptr; | |
238 } | |
239 } | |
240 | |
241 void WorkspaceLayoutManager::OnWindowBoundsChanged( | |
242 wm::WmWindow* window, | |
243 const gfx::Rect& old_bounds, | |
244 const gfx::Rect& new_bounds) { | |
245 if (root_window_ == window) { | |
246 const wm::WMEvent wm_event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED); | |
247 AdjustAllWindowsBoundsForWorkAreaChange(&wm_event); | |
248 } | |
249 } | |
250 | |
251 ////////////////////////////////////////////////////////////////////////////// | |
252 // WorkspaceLayoutManager, | |
253 // aura::client::ActivationChangeObserver implementation: | |
254 | |
255 void WorkspaceLayoutManager::OnWindowActivated(wm::WmWindow* gained_active, | |
256 wm::WmWindow* lost_active) { | |
257 wm::WindowState* window_state = | |
258 gained_active ? gained_active->GetWindowState() : nullptr; | |
259 if (window_state && window_state->IsMinimized() && | |
260 !gained_active->IsVisible()) { | |
261 window_state->Unminimize(); | |
262 DCHECK(!window_state->IsMinimized()); | |
263 } | |
264 UpdateFullscreenState(); | |
265 UpdateShelfVisibility(); | |
266 } | |
267 | |
268 ////////////////////////////////////////////////////////////////////////////// | |
269 // WorkspaceLayoutManager, wm::WindowStateObserver implementation: | |
270 | |
271 void WorkspaceLayoutManager::OnPostWindowStateTypeChange( | |
272 wm::WindowState* window_state, | |
273 wm::WindowStateType old_type) { | |
274 // Notify observers that fullscreen state may be changing. | |
275 if (window_state->IsFullscreen() || | |
276 old_type == wm::WINDOW_STATE_TYPE_FULLSCREEN) { | |
277 UpdateFullscreenState(); | |
278 } | |
279 | |
280 UpdateShelfVisibility(); | |
281 if (backdrop_delegate_) | |
282 backdrop_delegate_->OnPostWindowStateTypeChange(window_state, old_type); | |
283 } | |
284 | |
285 ////////////////////////////////////////////////////////////////////////////// | |
286 // WorkspaceLayoutManager, private: | |
287 | |
288 void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange( | |
289 const wm::WMEvent* event) { | |
290 DCHECK(event->type() == wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED || | |
291 event->type() == wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED); | |
292 | |
293 work_area_in_parent_ = wm::GetDisplayWorkAreaBounds(window_); | |
294 | |
295 // Don't do any adjustments of the insets while we are in screen locked mode. | |
296 // This would happen if the launcher was auto hidden before the login screen | |
297 // was shown and then gets shown when the login screen gets presented. | |
298 if (event->type() == wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED && | |
299 globals_->IsScreenLocked()) | |
300 return; | |
301 | |
302 // If a user plugs an external display into a laptop running Aura the | |
303 // display size will change. Maximized windows need to resize to match. | |
304 // We also do this when developers running Aura on a desktop manually resize | |
305 // the host window. | |
306 // We also need to do this when the work area insets changes. | |
307 for (wm::WmWindow* window : windows_) | |
308 window->GetWindowState()->OnWMEvent(event); | |
309 } | |
310 | |
311 void WorkspaceLayoutManager::UpdateShelfVisibility() { | |
312 if (delegate_) | |
313 delegate_->UpdateShelfVisibility(); | |
314 } | |
315 | |
316 void WorkspaceLayoutManager::UpdateFullscreenState() { | |
317 // TODO(flackr): The fullscreen state is currently tracked per workspace | |
318 // but the shell notification implies a per root window state. Currently | |
319 // only windows in the default workspace container will go fullscreen but | |
320 // this should really be tracked by the RootWindowController since | |
321 // technically any container could get a fullscreen window. | |
322 if (!delegate_) | |
323 return; | |
324 bool is_fullscreen = GetWindowForFullscreenMode(window_) != nullptr; | |
325 if (is_fullscreen != is_fullscreen_) { | |
326 delegate_->OnFullscreenStateChanged(is_fullscreen); | |
327 is_fullscreen_ = is_fullscreen; | |
328 } | |
329 } | |
330 | |
331 } // namespace ash | |
OLD | NEW |