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