| 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 |