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/common/wm/workspace/workspace_layout_manager.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "ash/common/session/session_state_delegate.h" | |
10 #include "ash/common/shelf/wm_shelf.h" | |
11 #include "ash/common/wm/always_on_top_controller.h" | |
12 #include "ash/common/wm/fullscreen_window_finder.h" | |
13 #include "ash/common/wm/window_positioner.h" | |
14 #include "ash/common/wm/window_state.h" | |
15 #include "ash/common/wm/wm_event.h" | |
16 #include "ash/common/wm/wm_screen_util.h" | |
17 #include "ash/common/wm/workspace/workspace_layout_manager_backdrop_delegate.h" | |
18 #include "ash/common/wm_shell.h" | |
19 #include "ash/common/wm_window.h" | |
20 #include "ash/public/cpp/shell_window_ids.h" | |
21 #include "ash/root_window_controller.h" | |
22 #include "ash/wm/window_properties.h" | |
23 #include "ash/wm/window_state_aura.h" | |
24 #include "base/command_line.h" | |
25 #include "ui/aura/client/aura_constants.h" | |
26 #include "ui/base/ui_base_switches.h" | |
27 #include "ui/compositor/layer.h" | |
28 #include "ui/display/display.h" | |
29 #include "ui/display/screen.h" | |
30 #include "ui/keyboard/keyboard_controller.h" | |
31 #include "ui/keyboard/keyboard_controller_observer.h" | |
32 | |
33 namespace ash { | |
34 | |
35 WorkspaceLayoutManager::WorkspaceLayoutManager(WmWindow* window) | |
36 : window_(window), | |
37 root_window_(window->GetRootWindow()), | |
38 root_window_controller_(root_window_->GetRootWindowController()), | |
39 shell_(window_->GetShell()), | |
40 work_area_in_parent_(wm::GetDisplayWorkAreaBoundsInParent(window_)), | |
41 is_fullscreen_(wm::GetWindowForFullscreenMode(window) != nullptr) { | |
42 shell_->AddShellObserver(this); | |
43 shell_->AddActivationObserver(this); | |
44 root_window_->aura_window()->AddObserver(this); | |
45 display::Screen::GetScreen()->AddObserver(this); | |
46 DCHECK(window->aura_window()->GetProperty(kSnapChildrenToPixelBoundary)); | |
47 } | |
48 | |
49 WorkspaceLayoutManager::~WorkspaceLayoutManager() { | |
50 if (root_window_) | |
51 root_window_->aura_window()->RemoveObserver(this); | |
52 for (WmWindow* window : windows_) { | |
53 wm::WindowState* window_state = window->GetWindowState(); | |
54 window_state->RemoveObserver(this); | |
55 window->aura_window()->RemoveObserver(this); | |
56 } | |
57 display::Screen::GetScreen()->RemoveObserver(this); | |
58 shell_->RemoveActivationObserver(this); | |
59 shell_->RemoveShellObserver(this); | |
60 } | |
61 | |
62 void WorkspaceLayoutManager::SetMaximizeBackdropDelegate( | |
63 std::unique_ptr<WorkspaceLayoutManagerBackdropDelegate> delegate) { | |
64 backdrop_delegate_ = std::move(delegate); | |
65 } | |
66 | |
67 ////////////////////////////////////////////////////////////////////////////// | |
68 // WorkspaceLayoutManager, aura::LayoutManager implementation: | |
69 | |
70 void WorkspaceLayoutManager::OnWindowResized() {} | |
71 | |
72 void WorkspaceLayoutManager::OnWindowAddedToLayout(WmWindow* child) { | |
73 wm::WindowState* window_state = child->GetWindowState(); | |
74 wm::WMEvent event(wm::WM_EVENT_ADDED_TO_WORKSPACE); | |
75 window_state->OnWMEvent(&event); | |
76 windows_.insert(child); | |
77 child->aura_window()->AddObserver(this); | |
78 window_state->AddObserver(this); | |
79 UpdateShelfVisibility(); | |
80 UpdateFullscreenState(); | |
81 if (backdrop_delegate_) | |
82 backdrop_delegate_->OnWindowAddedToLayout(child); | |
83 WindowPositioner::RearrangeVisibleWindowOnShow(child); | |
84 if (WmShell::Get()->IsPinned()) | |
85 child->GetWindowState()->DisableAlwaysOnTop(nullptr); | |
86 } | |
87 | |
88 void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout(WmWindow* child) { | |
89 windows_.erase(child); | |
90 child->aura_window()->RemoveObserver(this); | |
91 child->GetWindowState()->RemoveObserver(this); | |
92 | |
93 if (child->GetTargetVisibility()) | |
94 WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child); | |
95 } | |
96 | |
97 void WorkspaceLayoutManager::OnWindowRemovedFromLayout(WmWindow* child) { | |
98 UpdateShelfVisibility(); | |
99 UpdateFullscreenState(); | |
100 if (backdrop_delegate_) | |
101 backdrop_delegate_->OnWindowRemovedFromLayout(child); | |
102 } | |
103 | |
104 void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(WmWindow* child, | |
105 bool visible) { | |
106 wm::WindowState* window_state = child->GetWindowState(); | |
107 // Attempting to show a minimized window. Unminimize it. | |
108 if (visible && window_state->IsMinimized()) | |
109 window_state->Unminimize(); | |
110 | |
111 if (child->GetTargetVisibility()) | |
112 WindowPositioner::RearrangeVisibleWindowOnShow(child); | |
113 else | |
114 WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child); | |
115 UpdateFullscreenState(); | |
116 UpdateShelfVisibility(); | |
117 if (backdrop_delegate_) | |
118 backdrop_delegate_->OnChildWindowVisibilityChanged(child, visible); | |
119 } | |
120 | |
121 void WorkspaceLayoutManager::SetChildBounds(WmWindow* child, | |
122 const gfx::Rect& requested_bounds) { | |
123 wm::SetBoundsEvent event(wm::WM_EVENT_SET_BOUNDS, requested_bounds); | |
124 child->GetWindowState()->OnWMEvent(&event); | |
125 UpdateShelfVisibility(); | |
126 } | |
127 | |
128 ////////////////////////////////////////////////////////////////////////////// | |
129 // WorkspaceLayoutManager, keyboard::KeyboardControllerObserver implementation: | |
130 | |
131 void WorkspaceLayoutManager::OnKeyboardBoundsChanging( | |
132 const gfx::Rect& new_bounds) { | |
133 // If new window behavior flag enabled and in non-sticky mode, do not change | |
134 // the work area. | |
135 bool change_work_area = | |
136 (!base::CommandLine::ForCurrentProcess()->HasSwitch( | |
137 ::switches::kUseNewVirtualKeyboardBehavior) || | |
138 keyboard::KeyboardController::GetInstance()->keyboard_locked()); | |
139 if (!change_work_area) | |
140 return; | |
141 | |
142 WmWindow* window = shell_->GetActiveWindow(); | |
143 if (!window) | |
144 return; | |
145 | |
146 window = window->GetToplevelWindow(); | |
147 if (!window_->Contains(window)) | |
148 return; | |
149 | |
150 wm::WindowState* window_state = window->GetWindowState(); | |
151 if (window_state->ignore_keyboard_bounds_change()) | |
152 return; | |
153 | |
154 if (!new_bounds.IsEmpty()) { | |
155 // Store existing bounds to be restored before resizing for keyboard if it | |
156 // is not already stored. | |
157 if (!window_state->HasRestoreBounds()) | |
158 window_state->SaveCurrentBoundsForRestore(); | |
159 | |
160 gfx::Rect window_bounds = | |
161 window_->ConvertRectToScreen(window->GetTargetBounds()); | |
162 int vertical_displacement = | |
163 std::max(0, window_bounds.bottom() - new_bounds.y()); | |
164 int shift = std::min(vertical_displacement, | |
165 window_bounds.y() - work_area_in_parent_.y()); | |
166 if (shift > 0) { | |
167 gfx::Point origin(window_bounds.x(), window_bounds.y() - shift); | |
168 SetChildBounds(window, gfx::Rect(origin, window_bounds.size())); | |
169 } | |
170 } else if (window_state->HasRestoreBounds()) { | |
171 // Keyboard hidden, restore original bounds if they exist. If the user has | |
172 // resized or dragged the window in the meantime, WorkspaceWindowResizer | |
173 // will have cleared the restore bounds and this code will not accidentally | |
174 // override user intent. | |
175 window_state->SetAndClearRestoreBounds(); | |
176 } | |
177 } | |
178 | |
179 void WorkspaceLayoutManager::OnKeyboardClosed() {} | |
180 | |
181 ////////////////////////////////////////////////////////////////////////////// | |
182 // WorkspaceLayoutManager, aura::WindowObserver implementation: | |
183 | |
184 void WorkspaceLayoutManager::OnWindowHierarchyChanged( | |
185 const HierarchyChangeParams& params) { | |
186 if (!wm::GetWindowState(params.target)->IsActive()) | |
187 return; | |
188 // If the window is already tracked by the workspace this update would be | |
189 // redundant as the fullscreen and shelf state would have been handled in | |
190 // OnWindowAddedToLayout. | |
191 if (windows_.find(WmWindow::Get(params.target)) != windows_.end()) | |
192 return; | |
193 | |
194 // If the active window has moved to this root window then update the | |
195 // fullscreen state. | |
196 // TODO(flackr): Track the active window leaving this root window and update | |
197 // the fullscreen state accordingly. | |
198 if (params.new_parent && | |
199 WmWindow::Get(params.new_parent->GetRootWindow()) == root_window_) { | |
200 UpdateFullscreenState(); | |
201 UpdateShelfVisibility(); | |
202 } | |
203 } | |
204 | |
205 void WorkspaceLayoutManager::OnWindowPropertyChanged(aura::Window* window, | |
206 const void* key, | |
207 intptr_t old) { | |
208 if (key == aura::client::kAlwaysOnTopKey && | |
209 window->GetProperty(aura::client::kAlwaysOnTopKey)) { | |
210 WmWindow* container = | |
211 root_window_controller_->always_on_top_controller()->GetContainer( | |
212 WmWindow::Get(window)); | |
213 if (WmWindow::Get(window->parent()) != container) | |
214 container->AddChild(WmWindow::Get(window)); | |
215 } | |
216 } | |
217 | |
218 void WorkspaceLayoutManager::OnWindowStackingChanged(aura::Window* window) { | |
219 UpdateShelfVisibility(); | |
220 UpdateFullscreenState(); | |
221 if (backdrop_delegate_) | |
222 backdrop_delegate_->OnWindowStackingChanged(WmWindow::Get(window)); | |
223 } | |
224 | |
225 void WorkspaceLayoutManager::OnWindowDestroying(aura::Window* window) { | |
226 if (root_window_ == WmWindow::Get(window)) { | |
227 root_window_->aura_window()->RemoveObserver(this); | |
228 root_window_ = nullptr; | |
229 } | |
230 } | |
231 | |
232 void WorkspaceLayoutManager::OnWindowBoundsChanged( | |
233 aura::Window* window, | |
234 const gfx::Rect& old_bounds, | |
235 const gfx::Rect& new_bounds) { | |
236 if (root_window_ == WmWindow::Get(window)) { | |
237 const wm::WMEvent wm_event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED); | |
238 AdjustAllWindowsBoundsForWorkAreaChange(&wm_event); | |
239 } | |
240 } | |
241 | |
242 ////////////////////////////////////////////////////////////////////////////// | |
243 // WorkspaceLayoutManager, | |
244 // aura::client::ActivationChangeObserver implementation: | |
245 | |
246 void WorkspaceLayoutManager::OnWindowActivated(WmWindow* gained_active, | |
247 WmWindow* lost_active) { | |
248 wm::WindowState* window_state = | |
249 gained_active ? gained_active->GetWindowState() : nullptr; | |
250 if (window_state && window_state->IsMinimized() && | |
251 !gained_active->IsVisible()) { | |
252 window_state->Unminimize(); | |
253 DCHECK(!window_state->IsMinimized()); | |
254 } | |
255 UpdateFullscreenState(); | |
256 UpdateShelfVisibility(); | |
257 } | |
258 | |
259 ////////////////////////////////////////////////////////////////////////////// | |
260 // WorkspaceLayoutManager, wm::WindowStateObserver implementation: | |
261 | |
262 void WorkspaceLayoutManager::OnPostWindowStateTypeChange( | |
263 wm::WindowState* window_state, | |
264 wm::WindowStateType old_type) { | |
265 // Notify observers that fullscreen state may be changing. | |
266 if (window_state->IsFullscreen() || | |
267 old_type == wm::WINDOW_STATE_TYPE_FULLSCREEN) { | |
268 UpdateFullscreenState(); | |
269 } | |
270 | |
271 UpdateShelfVisibility(); | |
272 if (backdrop_delegate_) | |
273 backdrop_delegate_->OnPostWindowStateTypeChange(window_state, old_type); | |
274 } | |
275 | |
276 ////////////////////////////////////////////////////////////////////////////// | |
277 // WorkspaceLayoutManager, display::DisplayObserver implementation: | |
278 | |
279 void WorkspaceLayoutManager::OnDisplayMetricsChanged( | |
280 const display::Display& display, | |
281 uint32_t changed_metrics) { | |
282 if (window_->GetDisplayNearestWindow().id() != display.id()) | |
283 return; | |
284 | |
285 const gfx::Rect work_area(wm::GetDisplayWorkAreaBoundsInParent(window_)); | |
286 if (work_area != work_area_in_parent_) { | |
287 const wm::WMEvent event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED); | |
288 AdjustAllWindowsBoundsForWorkAreaChange(&event); | |
289 } | |
290 if (backdrop_delegate_) | |
291 backdrop_delegate_->OnDisplayWorkAreaInsetsChanged(); | |
292 } | |
293 | |
294 ////////////////////////////////////////////////////////////////////////////// | |
295 // WorkspaceLayoutManager, ShellObserver implementation: | |
296 | |
297 void WorkspaceLayoutManager::OnFullscreenStateChanged(bool is_fullscreen, | |
298 WmWindow* root_window) { | |
299 if (root_window != root_window_ || is_fullscreen_ == is_fullscreen) | |
300 return; | |
301 | |
302 is_fullscreen_ = is_fullscreen; | |
303 if (WmShell::Get()->IsPinned()) { | |
304 // If this is in pinned mode, then this event does not trigger the | |
305 // always-on-top state change, because it is kept disabled regardless of | |
306 // the fullscreen state change. | |
307 return; | |
308 } | |
309 | |
310 UpdateAlwaysOnTop(is_fullscreen_ ? wm::GetWindowForFullscreenMode(window_) | |
311 : nullptr); | |
312 } | |
313 | |
314 void WorkspaceLayoutManager::OnPinnedStateChanged(WmWindow* pinned_window) { | |
315 if (!WmShell::Get()->IsPinned() && is_fullscreen_) { | |
316 // On exiting from pinned mode, if the workspace is still in fullscreen | |
317 // mode, then this event does not trigger the restoring yet. On exiting | |
318 // from fullscreen, the temporarily disabled always-on-top property will be | |
319 // restored. | |
320 return; | |
321 } | |
322 | |
323 UpdateAlwaysOnTop(WmShell::Get()->IsPinned() ? pinned_window : nullptr); | |
324 } | |
325 | |
326 ////////////////////////////////////////////////////////////////////////////// | |
327 // WorkspaceLayoutManager, private: | |
328 | |
329 void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange( | |
330 const wm::WMEvent* event) { | |
331 DCHECK(event->type() == wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED || | |
332 event->type() == wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED); | |
333 | |
334 work_area_in_parent_ = wm::GetDisplayWorkAreaBoundsInParent(window_); | |
335 | |
336 // Don't do any adjustments of the insets while we are in screen locked mode. | |
337 // This would happen if the launcher was auto hidden before the login screen | |
338 // was shown and then gets shown when the login screen gets presented. | |
339 if (event->type() == wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED && | |
340 shell_->GetSessionStateDelegate()->IsScreenLocked()) | |
341 return; | |
342 | |
343 // If a user plugs an external display into a laptop running Aura the | |
344 // display size will change. Maximized windows need to resize to match. | |
345 // We also do this when developers running Aura on a desktop manually resize | |
346 // the host window. | |
347 // We also need to do this when the work area insets changes. | |
348 for (WmWindow* window : windows_) | |
349 window->GetWindowState()->OnWMEvent(event); | |
350 } | |
351 | |
352 void WorkspaceLayoutManager::UpdateShelfVisibility() { | |
353 if (root_window_controller_->HasShelf()) | |
354 root_window_controller_->GetShelf()->UpdateVisibilityState(); | |
355 } | |
356 | |
357 void WorkspaceLayoutManager::UpdateFullscreenState() { | |
358 // TODO(flackr): The fullscreen state is currently tracked per workspace | |
359 // but the shell notification implies a per root window state. Currently | |
360 // only windows in the default workspace container will go fullscreen but | |
361 // this should really be tracked by the RootWindowController since | |
362 // technically any container could get a fullscreen window. | |
363 if (window_->GetShellWindowId() != kShellWindowId_DefaultContainer) | |
364 return; | |
365 bool is_fullscreen = wm::GetWindowForFullscreenMode(window_) != nullptr; | |
366 if (is_fullscreen != is_fullscreen_) { | |
367 WmShell::Get()->NotifyFullscreenStateChanged(is_fullscreen, root_window_); | |
368 is_fullscreen_ = is_fullscreen; | |
369 } | |
370 } | |
371 | |
372 void WorkspaceLayoutManager::UpdateAlwaysOnTop(WmWindow* window_on_top) { | |
373 // Changing always on top state may change window's parent. Iterate on a copy | |
374 // of |windows_| to avoid invalidating an iterator. Since both workspace and | |
375 // always_on_top containers' layouts are managed by this class all the | |
376 // appropriate windows will be included in the iteration. | |
377 WindowSet windows(windows_); | |
378 for (auto* window : windows) { | |
379 wm::WindowState* window_state = window->GetWindowState(); | |
380 if (window_on_top) | |
381 window_state->DisableAlwaysOnTop(window_on_top); | |
382 else | |
383 window_state->RestoreAlwaysOnTop(); | |
384 } | |
385 } | |
386 | |
387 } // namespace ash | |
OLD | NEW |