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_manager2.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <functional> |
| 9 |
| 10 #include "ash/screen_ash.h" |
| 11 #include "ash/shell.h" |
| 12 #include "ash/shell_window_ids.h" |
| 13 #include "ash/wm/always_on_top_controller.h" |
| 14 #include "ash/wm/base_layout_manager.h" |
| 15 #include "ash/wm/property_util.h" |
| 16 #include "ash/wm/shelf_layout_manager.h" |
| 17 #include "ash/wm/window_animations.h" |
| 18 #include "ash/wm/window_properties.h" |
| 19 #include "ash/wm/window_util.h" |
| 20 #include "ash/wm/workspace/workspace2.h" |
| 21 #include "base/auto_reset.h" |
| 22 #include "base/logging.h" |
| 23 #include "base/stl_util.h" |
| 24 #include "base/stringprintf.h" |
| 25 #include "ui/aura/client/aura_constants.h" |
| 26 #include "ui/aura/env.h" |
| 27 #include "ui/aura/root_window.h" |
| 28 #include "ui/aura/window.h" |
| 29 #include "ui/aura/window_property.h" |
| 30 #include "ui/base/ui_base_types.h" |
| 31 #include "ui/compositor/layer.h" |
| 32 #include "ui/compositor/layer_animator.h" |
| 33 #include "ui/compositor/scoped_layer_animation_settings.h" |
| 34 #include "ui/gfx/screen.h" |
| 35 #include "ui/gfx/transform.h" |
| 36 |
| 37 DECLARE_WINDOW_PROPERTY_TYPE(ash::internal::Workspace2*); |
| 38 |
| 39 using aura::Window; |
| 40 |
| 41 namespace ash { |
| 42 namespace internal { |
| 43 |
| 44 DEFINE_WINDOW_PROPERTY_KEY(Workspace2*, kWorkspaceKey, NULL); |
| 45 |
| 46 namespace { |
| 47 |
| 48 // Changes the parent of |window| and all its transient children to |
| 49 // |new_parent|. If |stack_beneach| is non-NULL all the windows are stacked |
| 50 // beneath it. |
| 51 void ReparentWindow(Window* window, |
| 52 Window* new_parent, |
| 53 Window* stack_beneath) { |
| 54 window->SetParent(new_parent); |
| 55 if (stack_beneath) |
| 56 new_parent->StackChildBelow(window, stack_beneath); |
| 57 for (size_t i = 0; i < window->transient_children().size(); ++i) |
| 58 ReparentWindow(window->transient_children()[i], new_parent, stack_beneath); |
| 59 } |
| 60 |
| 61 // Workspace ------------------------------------------------------------------- |
| 62 |
| 63 // LayoutManager installed on the parent window of all the Workspace windows (eg |
| 64 // |WorkspaceManager2::contents_view_|). |
| 65 class WorkspaceManagerLayoutManager2 : public BaseLayoutManager { |
| 66 public: |
| 67 WorkspaceManagerLayoutManager2(Window* window) |
| 68 : BaseLayoutManager(window->GetRootWindow()), |
| 69 window_(window) { |
| 70 } |
| 71 virtual ~WorkspaceManagerLayoutManager2() {} |
| 72 |
| 73 // Overridden from BaseWorkspaceLayoutManager: |
| 74 virtual void OnWindowResized() OVERRIDE { |
| 75 for (size_t i = 0; i < window_->children().size(); ++i) |
| 76 window_->children()[i]->SetBounds(gfx::Rect(window_->bounds().size())); |
| 77 } |
| 78 virtual void OnWindowAddedToLayout(Window* child) OVERRIDE { |
| 79 // Only workspaces should be added as children. |
| 80 DCHECK_EQ(kShellWindowId_WorkspaceContainer, child->id()); |
| 81 child->SetBounds(gfx::Rect(window_->bounds().size())); |
| 82 } |
| 83 |
| 84 private: |
| 85 Window* window_; |
| 86 |
| 87 DISALLOW_COPY_AND_ASSIGN(WorkspaceManagerLayoutManager2); |
| 88 }; |
| 89 |
| 90 } // namespace |
| 91 |
| 92 // WorkspaceLayoutManager ------------------------------------------------------ |
| 93 |
| 94 // LayoutManager installed on the window each workspace contains. |
| 95 class WorkspaceManager2::WorkspaceLayoutManager : public BaseLayoutManager { |
| 96 public: |
| 97 WorkspaceLayoutManager(aura::RootWindow* root_window, Workspace2* workspace) |
| 98 : BaseLayoutManager(root_window), |
| 99 workspace_(workspace) { |
| 100 } |
| 101 virtual ~WorkspaceLayoutManager() { |
| 102 } |
| 103 |
| 104 // Overridden from BaseWorkspaceLayoutManager: |
| 105 virtual void OnWindowAddedToLayout(Window* child) OVERRIDE { |
| 106 BaseLayoutManager::OnWindowAddedToLayout(child); |
| 107 child->SetProperty(kWorkspaceKey, workspace_); |
| 108 workspace_manager()->OnWindowAddedToWorkspace(workspace_, child); |
| 109 } |
| 110 |
| 111 virtual void OnWillRemoveWindowFromLayout(Window* child) OVERRIDE { |
| 112 BaseLayoutManager::OnWillRemoveWindowFromLayout(child); |
| 113 child->ClearProperty(kWorkspaceKey); |
| 114 } |
| 115 |
| 116 virtual void OnWindowRemovedFromLayout(Window* child) OVERRIDE { |
| 117 BaseLayoutManager::OnWindowRemovedFromLayout(child); |
| 118 workspace_manager()->OnWindowRemovedFromWorkspace(workspace_, child); |
| 119 } |
| 120 |
| 121 virtual void OnChildWindowVisibilityChanged(Window* child, |
| 122 bool visibile) OVERRIDE { |
| 123 BaseLayoutManager::OnChildWindowVisibilityChanged(child, visibile); |
| 124 workspace_manager()->OnWorkspaceChildWindowVisibilityChanged(workspace_, |
| 125 child); |
| 126 } |
| 127 |
| 128 virtual void SetChildBounds(Window* child, |
| 129 const gfx::Rect& requested_bounds) OVERRIDE { |
| 130 BaseLayoutManager::SetChildBounds(child, requested_bounds); |
| 131 workspace_manager()->OnWorkspaceWindowChildBoundsChanged(workspace_, child); |
| 132 } |
| 133 |
| 134 |
| 135 // Overriden from WindowObserver: |
| 136 virtual void OnWindowPropertyChanged(Window* window, |
| 137 const void* key, |
| 138 intptr_t old) { |
| 139 BaseLayoutManager::OnWindowPropertyChanged(window, key, old); |
| 140 |
| 141 if (key == aura::client::kAlwaysOnTopKey && |
| 142 window->GetProperty(aura::client::kAlwaysOnTopKey)) { |
| 143 internal::AlwaysOnTopController* controller = |
| 144 window->GetRootWindow()->GetProperty( |
| 145 internal::kAlwaysOnTopControllerKey); |
| 146 controller->GetContainer(window)->AddChild(window); |
| 147 } |
| 148 } |
| 149 |
| 150 protected: |
| 151 // Overriden from WindowObserver: |
| 152 virtual void ShowStateChanged(Window* window, |
| 153 ui::WindowShowState last_show_state) OVERRIDE { |
| 154 // NOTE: we can't use BaseLayoutManager::ShowStateChanged() as we need to |
| 155 // forward to WorkspaceManager before the window is hidden. |
| 156 if (wm::IsWindowMinimized(window)) { |
| 157 // Save the previous show state so that we can correctly restore it. |
| 158 window->SetProperty(internal::kRestoreShowStateKey, last_show_state); |
| 159 SetWindowVisibilityAnimationType( |
| 160 window, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); |
| 161 |
| 162 workspace_manager()->OnWorkspaceWindowShowStateChanged( |
| 163 workspace_, window, last_show_state); |
| 164 |
| 165 // Hide the window. |
| 166 window->Hide(); |
| 167 |
| 168 // Activate another window. |
| 169 if (wm::IsActiveWindow(window)) |
| 170 wm::DeactivateWindow(window); |
| 171 } else { |
| 172 if ((window->TargetVisibility() || |
| 173 last_show_state == ui::SHOW_STATE_MINIMIZED) && |
| 174 !window->layer()->visible()) { |
| 175 // The layer may be hidden if the window was previously minimized. Make |
| 176 // sure it's visible. |
| 177 window->Show(); |
| 178 } |
| 179 workspace_manager()->OnWorkspaceWindowShowStateChanged( |
| 180 workspace_, window, last_show_state); |
| 181 } |
| 182 } |
| 183 |
| 184 private: |
| 185 WorkspaceManager2* workspace_manager() { |
| 186 return workspace_->workspace_manager(); |
| 187 } |
| 188 |
| 189 Workspace2* workspace_; |
| 190 |
| 191 DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManager); |
| 192 }; |
| 193 |
| 194 // WorkspaceManager2 ----------------------------------------------------------- |
| 195 |
| 196 WorkspaceManager2::WorkspaceManager2(Window* contents_view) |
| 197 : contents_view_(contents_view), |
| 198 active_workspace_(NULL), |
| 199 grid_size_(0), |
| 200 shelf_(NULL), |
| 201 in_move_(false) { |
| 202 // Clobber any existing event filter. |
| 203 contents_view->SetEventFilter(NULL); |
| 204 // |contents_view| takes ownership of WorkspaceManagerLayoutManager2. |
| 205 contents_view->SetLayoutManager( |
| 206 new WorkspaceManagerLayoutManager2(contents_view)); |
| 207 active_workspace_ = CreateWorkspace(false); |
| 208 workspaces_.push_back(active_workspace_); |
| 209 } |
| 210 |
| 211 WorkspaceManager2::~WorkspaceManager2() { |
| 212 // Release the windows, they'll be destroyed when |contents_view_| is |
| 213 // destroyed. |
| 214 std::for_each(workspaces_.begin(), workspaces_.end(), |
| 215 std::mem_fun(&Workspace2::ReleaseWindow)); |
| 216 std::for_each(pending_workspaces_.begin(), pending_workspaces_.end(), |
| 217 std::mem_fun(&Workspace2::ReleaseWindow)); |
| 218 STLDeleteElements(&workspaces_); |
| 219 STLDeleteElements(&pending_workspaces_); |
| 220 } |
| 221 |
| 222 // static |
| 223 bool WorkspaceManager2::IsMaximized(Window* window) { |
| 224 return wm::IsWindowFullscreen(window) || wm::IsWindowMaximized(window); |
| 225 } |
| 226 |
| 227 // static |
| 228 bool WorkspaceManager2::WillRestoreMaximized(aura::Window* window) { |
| 229 if (!wm::IsWindowMinimized(window)) |
| 230 return false; |
| 231 |
| 232 ui::WindowShowState restore_state = |
| 233 window->GetProperty(internal::kRestoreShowStateKey); |
| 234 return restore_state == ui::SHOW_STATE_MAXIMIZED || |
| 235 restore_state == ui::SHOW_STATE_FULLSCREEN; |
| 236 |
| 237 } |
| 238 |
| 239 bool WorkspaceManager2::IsInMaximizedMode() const { |
| 240 return active_workspace_ && active_workspace_->is_maximized(); |
| 241 } |
| 242 |
| 243 void WorkspaceManager2::SetGridSize(int size) { |
| 244 grid_size_ = size; |
| 245 std::for_each(workspaces_.begin(), workspaces_.end(), |
| 246 std::bind2nd(std::mem_fun(&Workspace2::SetGridSize), |
| 247 grid_size_)); |
| 248 std::for_each(pending_workspaces_.begin(), pending_workspaces_.end(), |
| 249 std::bind2nd(std::mem_fun(&Workspace2::SetGridSize), |
| 250 grid_size_)); |
| 251 } |
| 252 |
| 253 int WorkspaceManager2::GetGridSize() const { |
| 254 return grid_size_; |
| 255 } |
| 256 |
| 257 WorkspaceWindowState WorkspaceManager2::GetWindowState() const { |
| 258 if (!shelf_) |
| 259 return WORKSPACE_WINDOW_STATE_DEFAULT; |
| 260 |
| 261 gfx::Rect shelf_bounds(shelf_->GetIdealBounds()); |
| 262 const Window::Windows& windows(active_workspace_->window()->children()); |
| 263 bool window_overlaps_launcher = false; |
| 264 bool has_maximized_window = false; |
| 265 for (Window::Windows::const_iterator i = windows.begin(); |
| 266 i != windows.end(); ++i) { |
| 267 ui::Layer* layer = (*i)->layer(); |
| 268 if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f) |
| 269 continue; |
| 270 if (wm::IsWindowMaximized(*i)) { |
| 271 // An untracked window may still be fullscreen so we keep iterating when |
| 272 // we hit a maximized window. |
| 273 has_maximized_window = true; |
| 274 } else if (wm::IsWindowFullscreen(*i)) { |
| 275 return WORKSPACE_WINDOW_STATE_FULL_SCREEN; |
| 276 } |
| 277 if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds)) |
| 278 window_overlaps_launcher = true; |
| 279 } |
| 280 if (has_maximized_window) |
| 281 return WORKSPACE_WINDOW_STATE_MAXIMIZED; |
| 282 |
| 283 return window_overlaps_launcher ? |
| 284 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF : |
| 285 WORKSPACE_WINDOW_STATE_DEFAULT; |
| 286 } |
| 287 |
| 288 void WorkspaceManager2::SetShelf(ShelfLayoutManager* shelf) { |
| 289 shelf_ = shelf; |
| 290 } |
| 291 |
| 292 void WorkspaceManager2::SetActiveWorkspaceByWindow(Window* window) { |
| 293 Workspace2* workspace = FindBy(window); |
| 294 if (!workspace) |
| 295 return; |
| 296 |
| 297 if (workspace != active_workspace_) { |
| 298 // If the window persists across all workspaces, move it to the current |
| 299 // workspace. |
| 300 if (GetPersistsAcrossAllWorkspaces(window) && !IsMaximized(window)) |
| 301 ReparentWindow(window, active_workspace_->window(), NULL); |
| 302 else |
| 303 SetActiveWorkspace(workspace); |
| 304 } |
| 305 UpdateShelfVisibility(); |
| 306 } |
| 307 |
| 308 Window* WorkspaceManager2::GetParentForNewWindow(Window* window) { |
| 309 if (window->transient_parent()) { |
| 310 DCHECK(contents_view_->Contains(window->transient_parent())); |
| 311 DCHECK(!IsMaximized(window)); |
| 312 return window->transient_parent()->parent(); |
| 313 } |
| 314 |
| 315 if (IsMaximized(window)) { |
| 316 // Wait for the window to be made active before showing the workspace. |
| 317 Workspace2* workspace = CreateWorkspace(false); |
| 318 pending_workspaces_.insert(workspace); |
| 319 return workspace->window(); |
| 320 } |
| 321 |
| 322 if (!GetTrackedByWorkspace(window) || GetPersistsAcrossAllWorkspaces(window)) |
| 323 return active_workspace_->window(); |
| 324 |
| 325 return desktop_workspace()->window(); |
| 326 } |
| 327 |
| 328 void WorkspaceManager2::UpdateShelfVisibility() { |
| 329 if (shelf_) |
| 330 shelf_->UpdateVisibilityState(); |
| 331 } |
| 332 |
| 333 Workspace2* WorkspaceManager2::FindBy(Window* window) const { |
| 334 while (window) { |
| 335 Workspace2* workspace = window->GetProperty(kWorkspaceKey); |
| 336 if (workspace) |
| 337 return workspace; |
| 338 window = window->transient_parent(); |
| 339 } |
| 340 return NULL; |
| 341 } |
| 342 |
| 343 void WorkspaceManager2::SetActiveWorkspace(Workspace2* workspace) { |
| 344 DCHECK(workspace); |
| 345 if (active_workspace_ == workspace) |
| 346 return; |
| 347 |
| 348 // TODO: sort out animations. |
| 349 |
| 350 pending_workspaces_.erase(workspace); |
| 351 |
| 352 // Adjust the z-order. No need to adjust the z-order for the desktop since |
| 353 // it always stays at the bottom. |
| 354 if (workspace != desktop_workspace()) { |
| 355 if (FindWorkspace(workspace) == workspaces_.end()) { |
| 356 contents_view_->StackChildAbove(workspace->window(), |
| 357 workspaces_.back()->window()); |
| 358 workspaces_.push_back(workspace); |
| 359 } |
| 360 } |
| 361 |
| 362 active_workspace_ = workspace; |
| 363 |
| 364 for (size_t i = 0; i < workspaces_.size(); ++i) { |
| 365 if (workspaces_[i] == active_workspace_) |
| 366 workspaces_[i]->window()->Show(); |
| 367 else |
| 368 workspaces_[i]->window()->Hide(); |
| 369 } |
| 370 } |
| 371 |
| 372 WorkspaceManager2::Workspaces::iterator |
| 373 WorkspaceManager2::FindWorkspace(Workspace2* workspace) { |
| 374 return std::find(workspaces_.begin(), workspaces_.end(), workspace); |
| 375 } |
| 376 |
| 377 Workspace2* WorkspaceManager2::CreateWorkspace(bool maximized) { |
| 378 Workspace2* workspace = new Workspace2(this, contents_view_, maximized); |
| 379 workspace->SetGridSize(grid_size_); |
| 380 workspace->window()->SetLayoutManager( |
| 381 new WorkspaceLayoutManager(contents_view_->GetRootWindow(), workspace)); |
| 382 return workspace; |
| 383 } |
| 384 |
| 385 void WorkspaceManager2::MoveWorkspaceToPendingOrDelete( |
| 386 Workspace2* workspace, |
| 387 Window* stack_beneath) { |
| 388 // We're all ready moving windows. |
| 389 if (in_move_) |
| 390 return; |
| 391 |
| 392 DCHECK_NE(desktop_workspace(), workspace); |
| 393 |
| 394 if (workspace == active_workspace_) |
| 395 SelectNextWorkspace(); |
| 396 |
| 397 AutoReset<bool> setter(&in_move_, true); |
| 398 |
| 399 // Move all non-maximized/fullscreen windows to the desktop. |
| 400 { |
| 401 // Build the list of windows to move. Exclude maximized/fullscreen and |
| 402 // windows with transient parents. |
| 403 Window::Windows to_move; |
| 404 Window* w_window = workspace->window(); |
| 405 for (size_t i = 0; i < w_window->children().size(); ++i) { |
| 406 Window* child = w_window->children()[i]; |
| 407 if (!child->transient_parent() && !IsMaximized(child) && |
| 408 !WillRestoreMaximized(child)) { |
| 409 to_move.push_back(child); |
| 410 } |
| 411 } |
| 412 // Move the windows, but make sure the window is still a child of |w_window| |
| 413 // before moving (moving may cascade and cause other windows to move). |
| 414 for (size_t i = 0; i < to_move.size(); ++i) { |
| 415 if (std::find(w_window->children().begin(), w_window->children().end(), |
| 416 to_move[i]) != w_window->children().end()) { |
| 417 ReparentWindow(to_move[i], desktop_workspace()->window(), |
| 418 stack_beneath); |
| 419 } |
| 420 } |
| 421 } |
| 422 |
| 423 { |
| 424 Workspaces::iterator workspace_i(FindWorkspace(workspace)); |
| 425 if (workspace_i != workspaces_.end()) |
| 426 workspaces_.erase(workspace_i); |
| 427 } |
| 428 |
| 429 if (workspace->window()->children().empty()) { |
| 430 pending_workspaces_.erase(workspace); |
| 431 delete workspace->ReleaseWindow(); |
| 432 delete workspace; |
| 433 } else { |
| 434 pending_workspaces_.insert(workspace); |
| 435 } |
| 436 } |
| 437 |
| 438 void WorkspaceManager2::SelectNextWorkspace() { |
| 439 DCHECK_NE(active_workspace_, desktop_workspace()); |
| 440 |
| 441 Workspaces::const_iterator workspace_i(FindWorkspace(active_workspace_)); |
| 442 Workspaces::const_iterator next_workspace_i(workspace_i + 1); |
| 443 if (next_workspace_i != workspaces_.end()) |
| 444 SetActiveWorkspace(*next_workspace_i); |
| 445 else |
| 446 SetActiveWorkspace(*(workspace_i - 1)); |
| 447 UpdateShelfVisibility(); |
| 448 } |
| 449 |
| 450 void WorkspaceManager2::OnWindowAddedToWorkspace(Workspace2* workspace, |
| 451 Window* child) { |
| 452 // Do nothing (other than updating shelf visibility) as the right parent was |
| 453 // chosen by way of GetParentForNewWindow() or we explicitly moved the window |
| 454 // to the workspace. |
| 455 if (workspace == active_workspace_) |
| 456 UpdateShelfVisibility(); |
| 457 } |
| 458 |
| 459 void WorkspaceManager2::OnWindowRemovedFromWorkspace(Workspace2* workspace, |
| 460 Window* child) { |
| 461 if (workspace->ShouldMoveToPending()) |
| 462 MoveWorkspaceToPendingOrDelete(workspace, NULL); |
| 463 } |
| 464 |
| 465 void WorkspaceManager2::OnWorkspaceChildWindowVisibilityChanged( |
| 466 Workspace2* workspace, |
| 467 Window* child) { |
| 468 if (workspace->ShouldMoveToPending()) |
| 469 MoveWorkspaceToPendingOrDelete(workspace, NULL); |
| 470 } |
| 471 |
| 472 void WorkspaceManager2::OnWorkspaceWindowChildBoundsChanged( |
| 473 Workspace2* workspace, |
| 474 Window* child) { |
| 475 } |
| 476 |
| 477 void WorkspaceManager2::OnWorkspaceWindowShowStateChanged( |
| 478 Workspace2* workspace, |
| 479 Window* child, |
| 480 ui::WindowShowState last_show_state) { |
| 481 if (wm::IsWindowMinimized(child)) { |
| 482 if (workspace->ShouldMoveToPending()) |
| 483 MoveWorkspaceToPendingOrDelete(workspace, NULL); |
| 484 } else { |
| 485 // Here's the cases that need to be handled: |
| 486 // . More than one maximized window: move newly maximized window into |
| 487 // own workspace. |
| 488 // . One maximized window and not in a maximized workspace: move window |
| 489 // into own workspace. |
| 490 // . No maximized window and not in desktop: move to desktop and further |
| 491 // any existing windows are stacked beneath |child|. |
| 492 const bool is_active = wm::IsActiveWindow(child); |
| 493 Workspace2* new_workspace = NULL; |
| 494 const int max_count = workspace->GetNumMaximizedWindows(); |
| 495 if (max_count == 0) { |
| 496 if (workspace != desktop_workspace()) { |
| 497 { |
| 498 AutoReset<bool> setter(&in_move_, true); |
| 499 ReparentWindow(child, desktop_workspace()->window(), NULL); |
| 500 } |
| 501 MoveWorkspaceToPendingOrDelete(workspace, child); |
| 502 new_workspace = desktop_workspace(); |
| 503 } |
| 504 } else if (max_count == 1) { |
| 505 if (workspace == desktop_workspace()) { |
| 506 new_workspace = CreateWorkspace(true); |
| 507 pending_workspaces_.insert(new_workspace); |
| 508 ReparentWindow(child, new_workspace->window(), NULL); |
| 509 } |
| 510 } else { |
| 511 new_workspace = CreateWorkspace(true); |
| 512 pending_workspaces_.insert(new_workspace); |
| 513 ReparentWindow(child, new_workspace->window(), NULL); |
| 514 } |
| 515 if (is_active && new_workspace) |
| 516 SetActiveWorkspace(new_workspace); |
| 517 } |
| 518 UpdateShelfVisibility(); |
| 519 } |
| 520 |
| 521 } // namespace internal |
| 522 } // namespace ash |
OLD | NEW |