Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3648)

Unified Diff: ash/wm/workspace/workspace_manager2.cc

Issue 10830365: Initial crack at new workspace behavior. Each workspace now has its (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ash/wm/workspace/workspace_manager2.h ('k') | ash/wm/workspace/workspace_manager2_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ash/wm/workspace/workspace_manager2.cc
diff --git a/ash/wm/workspace/workspace_manager2.cc b/ash/wm/workspace/workspace_manager2.cc
new file mode 100644
index 0000000000000000000000000000000000000000..385e346901004b7d8f6857681a84022587ca09f7
--- /dev/null
+++ b/ash/wm/workspace/workspace_manager2.cc
@@ -0,0 +1,522 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/workspace/workspace_manager2.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "ash/screen_ash.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/always_on_top_controller.h"
+#include "ash/wm/base_layout_manager.h"
+#include "ash/wm/property_util.h"
+#include "ash/wm/shelf_layout_manager.h"
+#include "ash/wm/window_animations.h"
+#include "ash/wm/window_properties.h"
+#include "ash/wm/window_util.h"
+#include "ash/wm/workspace/workspace2.h"
+#include "base/auto_reset.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/stringprintf.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/screen.h"
+#include "ui/gfx/transform.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(ash::internal::Workspace2*);
+
+using aura::Window;
+
+namespace ash {
+namespace internal {
+
+DEFINE_WINDOW_PROPERTY_KEY(Workspace2*, kWorkspaceKey, NULL);
+
+namespace {
+
+// Changes the parent of |window| and all its transient children to
+// |new_parent|. If |stack_beneach| is non-NULL all the windows are stacked
+// beneath it.
+void ReparentWindow(Window* window,
+ Window* new_parent,
+ Window* stack_beneath) {
+ window->SetParent(new_parent);
+ if (stack_beneath)
+ new_parent->StackChildBelow(window, stack_beneath);
+ for (size_t i = 0; i < window->transient_children().size(); ++i)
+ ReparentWindow(window->transient_children()[i], new_parent, stack_beneath);
+}
+
+// Workspace -------------------------------------------------------------------
+
+// LayoutManager installed on the parent window of all the Workspace windows (eg
+// |WorkspaceManager2::contents_view_|).
+class WorkspaceManagerLayoutManager2 : public BaseLayoutManager {
+ public:
+ WorkspaceManagerLayoutManager2(Window* window)
+ : BaseLayoutManager(window->GetRootWindow()),
+ window_(window) {
+ }
+ virtual ~WorkspaceManagerLayoutManager2() {}
+
+ // Overridden from BaseWorkspaceLayoutManager:
+ virtual void OnWindowResized() OVERRIDE {
+ for (size_t i = 0; i < window_->children().size(); ++i)
+ window_->children()[i]->SetBounds(gfx::Rect(window_->bounds().size()));
+ }
+ virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {
+ // Only workspaces should be added as children.
+ DCHECK_EQ(kShellWindowId_WorkspaceContainer, child->id());
+ child->SetBounds(gfx::Rect(window_->bounds().size()));
+ }
+
+ private:
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceManagerLayoutManager2);
+};
+
+} // namespace
+
+// WorkspaceLayoutManager ------------------------------------------------------
+
+// LayoutManager installed on the window each workspace contains.
+class WorkspaceManager2::WorkspaceLayoutManager : public BaseLayoutManager {
+ public:
+ WorkspaceLayoutManager(aura::RootWindow* root_window, Workspace2* workspace)
+ : BaseLayoutManager(root_window),
+ workspace_(workspace) {
+ }
+ virtual ~WorkspaceLayoutManager() {
+ }
+
+ // Overridden from BaseWorkspaceLayoutManager:
+ virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {
+ BaseLayoutManager::OnWindowAddedToLayout(child);
+ child->SetProperty(kWorkspaceKey, workspace_);
+ workspace_manager()->OnWindowAddedToWorkspace(workspace_, child);
+ }
+
+ virtual void OnWillRemoveWindowFromLayout(Window* child) OVERRIDE {
+ BaseLayoutManager::OnWillRemoveWindowFromLayout(child);
+ child->ClearProperty(kWorkspaceKey);
+ }
+
+ virtual void OnWindowRemovedFromLayout(Window* child) OVERRIDE {
+ BaseLayoutManager::OnWindowRemovedFromLayout(child);
+ workspace_manager()->OnWindowRemovedFromWorkspace(workspace_, child);
+ }
+
+ virtual void OnChildWindowVisibilityChanged(Window* child,
+ bool visibile) OVERRIDE {
+ BaseLayoutManager::OnChildWindowVisibilityChanged(child, visibile);
+ workspace_manager()->OnWorkspaceChildWindowVisibilityChanged(workspace_,
+ child);
+ }
+
+ virtual void SetChildBounds(Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE {
+ BaseLayoutManager::SetChildBounds(child, requested_bounds);
+ workspace_manager()->OnWorkspaceWindowChildBoundsChanged(workspace_, child);
+ }
+
+
+ // Overriden from WindowObserver:
+ virtual void OnWindowPropertyChanged(Window* window,
+ const void* key,
+ intptr_t old) {
+ BaseLayoutManager::OnWindowPropertyChanged(window, key, old);
+
+ if (key == aura::client::kAlwaysOnTopKey &&
+ window->GetProperty(aura::client::kAlwaysOnTopKey)) {
+ internal::AlwaysOnTopController* controller =
+ window->GetRootWindow()->GetProperty(
+ internal::kAlwaysOnTopControllerKey);
+ controller->GetContainer(window)->AddChild(window);
+ }
+ }
+
+ protected:
+ // Overriden from WindowObserver:
+ virtual void ShowStateChanged(Window* window,
+ ui::WindowShowState last_show_state) OVERRIDE {
+ // NOTE: we can't use BaseLayoutManager::ShowStateChanged() as we need to
+ // forward to WorkspaceManager before the window is hidden.
+ if (wm::IsWindowMinimized(window)) {
+ // Save the previous show state so that we can correctly restore it.
+ window->SetProperty(internal::kRestoreShowStateKey, last_show_state);
+ SetWindowVisibilityAnimationType(
+ window, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
+
+ workspace_manager()->OnWorkspaceWindowShowStateChanged(
+ workspace_, window, last_show_state);
+
+ // Hide the window.
+ window->Hide();
+
+ // Activate another window.
+ if (wm::IsActiveWindow(window))
+ wm::DeactivateWindow(window);
+ } else {
+ if ((window->TargetVisibility() ||
+ last_show_state == ui::SHOW_STATE_MINIMIZED) &&
+ !window->layer()->visible()) {
+ // The layer may be hidden if the window was previously minimized. Make
+ // sure it's visible.
+ window->Show();
+ }
+ workspace_manager()->OnWorkspaceWindowShowStateChanged(
+ workspace_, window, last_show_state);
+ }
+ }
+
+ private:
+ WorkspaceManager2* workspace_manager() {
+ return workspace_->workspace_manager();
+ }
+
+ Workspace2* workspace_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManager);
+};
+
+// WorkspaceManager2 -----------------------------------------------------------
+
+WorkspaceManager2::WorkspaceManager2(Window* contents_view)
+ : contents_view_(contents_view),
+ active_workspace_(NULL),
+ grid_size_(0),
+ shelf_(NULL),
+ in_move_(false) {
+ // Clobber any existing event filter.
+ contents_view->SetEventFilter(NULL);
+ // |contents_view| takes ownership of WorkspaceManagerLayoutManager2.
+ contents_view->SetLayoutManager(
+ new WorkspaceManagerLayoutManager2(contents_view));
+ active_workspace_ = CreateWorkspace(false);
+ workspaces_.push_back(active_workspace_);
+}
+
+WorkspaceManager2::~WorkspaceManager2() {
+ // Release the windows, they'll be destroyed when |contents_view_| is
+ // destroyed.
+ std::for_each(workspaces_.begin(), workspaces_.end(),
+ std::mem_fun(&Workspace2::ReleaseWindow));
+ std::for_each(pending_workspaces_.begin(), pending_workspaces_.end(),
+ std::mem_fun(&Workspace2::ReleaseWindow));
+ STLDeleteElements(&workspaces_);
+ STLDeleteElements(&pending_workspaces_);
+}
+
+// static
+bool WorkspaceManager2::IsMaximized(Window* window) {
+ return wm::IsWindowFullscreen(window) || wm::IsWindowMaximized(window);
+}
+
+// static
+bool WorkspaceManager2::WillRestoreMaximized(aura::Window* window) {
+ if (!wm::IsWindowMinimized(window))
+ return false;
+
+ ui::WindowShowState restore_state =
+ window->GetProperty(internal::kRestoreShowStateKey);
+ return restore_state == ui::SHOW_STATE_MAXIMIZED ||
+ restore_state == ui::SHOW_STATE_FULLSCREEN;
+
+}
+
+bool WorkspaceManager2::IsInMaximizedMode() const {
+ return active_workspace_ && active_workspace_->is_maximized();
+}
+
+void WorkspaceManager2::SetGridSize(int size) {
+ grid_size_ = size;
+ std::for_each(workspaces_.begin(), workspaces_.end(),
+ std::bind2nd(std::mem_fun(&Workspace2::SetGridSize),
+ grid_size_));
+ std::for_each(pending_workspaces_.begin(), pending_workspaces_.end(),
+ std::bind2nd(std::mem_fun(&Workspace2::SetGridSize),
+ grid_size_));
+}
+
+int WorkspaceManager2::GetGridSize() const {
+ return grid_size_;
+}
+
+WorkspaceWindowState WorkspaceManager2::GetWindowState() const {
+ if (!shelf_)
+ return WORKSPACE_WINDOW_STATE_DEFAULT;
+
+ gfx::Rect shelf_bounds(shelf_->GetIdealBounds());
+ const Window::Windows& windows(active_workspace_->window()->children());
+ bool window_overlaps_launcher = false;
+ bool has_maximized_window = false;
+ for (Window::Windows::const_iterator i = windows.begin();
+ i != windows.end(); ++i) {
+ ui::Layer* layer = (*i)->layer();
+ if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f)
+ continue;
+ if (wm::IsWindowMaximized(*i)) {
+ // An untracked window may still be fullscreen so we keep iterating when
+ // we hit a maximized window.
+ has_maximized_window = true;
+ } else if (wm::IsWindowFullscreen(*i)) {
+ return WORKSPACE_WINDOW_STATE_FULL_SCREEN;
+ }
+ if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds))
+ window_overlaps_launcher = true;
+ }
+ if (has_maximized_window)
+ return WORKSPACE_WINDOW_STATE_MAXIMIZED;
+
+ return window_overlaps_launcher ?
+ WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF :
+ WORKSPACE_WINDOW_STATE_DEFAULT;
+}
+
+void WorkspaceManager2::SetShelf(ShelfLayoutManager* shelf) {
+ shelf_ = shelf;
+}
+
+void WorkspaceManager2::SetActiveWorkspaceByWindow(Window* window) {
+ Workspace2* workspace = FindBy(window);
+ if (!workspace)
+ return;
+
+ if (workspace != active_workspace_) {
+ // If the window persists across all workspaces, move it to the current
+ // workspace.
+ if (GetPersistsAcrossAllWorkspaces(window) && !IsMaximized(window))
+ ReparentWindow(window, active_workspace_->window(), NULL);
+ else
+ SetActiveWorkspace(workspace);
+ }
+ UpdateShelfVisibility();
+}
+
+Window* WorkspaceManager2::GetParentForNewWindow(Window* window) {
+ if (window->transient_parent()) {
+ DCHECK(contents_view_->Contains(window->transient_parent()));
+ DCHECK(!IsMaximized(window));
+ return window->transient_parent()->parent();
+ }
+
+ if (IsMaximized(window)) {
+ // Wait for the window to be made active before showing the workspace.
+ Workspace2* workspace = CreateWorkspace(false);
+ pending_workspaces_.insert(workspace);
+ return workspace->window();
+ }
+
+ if (!GetTrackedByWorkspace(window) || GetPersistsAcrossAllWorkspaces(window))
+ return active_workspace_->window();
+
+ return desktop_workspace()->window();
+}
+
+void WorkspaceManager2::UpdateShelfVisibility() {
+ if (shelf_)
+ shelf_->UpdateVisibilityState();
+}
+
+Workspace2* WorkspaceManager2::FindBy(Window* window) const {
+ while (window) {
+ Workspace2* workspace = window->GetProperty(kWorkspaceKey);
+ if (workspace)
+ return workspace;
+ window = window->transient_parent();
+ }
+ return NULL;
+}
+
+void WorkspaceManager2::SetActiveWorkspace(Workspace2* workspace) {
+ DCHECK(workspace);
+ if (active_workspace_ == workspace)
+ return;
+
+ // TODO: sort out animations.
+
+ pending_workspaces_.erase(workspace);
+
+ // Adjust the z-order. No need to adjust the z-order for the desktop since
+ // it always stays at the bottom.
+ if (workspace != desktop_workspace()) {
+ if (FindWorkspace(workspace) == workspaces_.end()) {
+ contents_view_->StackChildAbove(workspace->window(),
+ workspaces_.back()->window());
+ workspaces_.push_back(workspace);
+ }
+ }
+
+ active_workspace_ = workspace;
+
+ for (size_t i = 0; i < workspaces_.size(); ++i) {
+ if (workspaces_[i] == active_workspace_)
+ workspaces_[i]->window()->Show();
+ else
+ workspaces_[i]->window()->Hide();
+ }
+}
+
+WorkspaceManager2::Workspaces::iterator
+WorkspaceManager2::FindWorkspace(Workspace2* workspace) {
+ return std::find(workspaces_.begin(), workspaces_.end(), workspace);
+}
+
+Workspace2* WorkspaceManager2::CreateWorkspace(bool maximized) {
+ Workspace2* workspace = new Workspace2(this, contents_view_, maximized);
+ workspace->SetGridSize(grid_size_);
+ workspace->window()->SetLayoutManager(
+ new WorkspaceLayoutManager(contents_view_->GetRootWindow(), workspace));
+ return workspace;
+}
+
+void WorkspaceManager2::MoveWorkspaceToPendingOrDelete(
+ Workspace2* workspace,
+ Window* stack_beneath) {
+ // We're all ready moving windows.
+ if (in_move_)
+ return;
+
+ DCHECK_NE(desktop_workspace(), workspace);
+
+ if (workspace == active_workspace_)
+ SelectNextWorkspace();
+
+ AutoReset<bool> setter(&in_move_, true);
+
+ // Move all non-maximized/fullscreen windows to the desktop.
+ {
+ // Build the list of windows to move. Exclude maximized/fullscreen and
+ // windows with transient parents.
+ Window::Windows to_move;
+ Window* w_window = workspace->window();
+ for (size_t i = 0; i < w_window->children().size(); ++i) {
+ Window* child = w_window->children()[i];
+ if (!child->transient_parent() && !IsMaximized(child) &&
+ !WillRestoreMaximized(child)) {
+ to_move.push_back(child);
+ }
+ }
+ // Move the windows, but make sure the window is still a child of |w_window|
+ // before moving (moving may cascade and cause other windows to move).
+ for (size_t i = 0; i < to_move.size(); ++i) {
+ if (std::find(w_window->children().begin(), w_window->children().end(),
+ to_move[i]) != w_window->children().end()) {
+ ReparentWindow(to_move[i], desktop_workspace()->window(),
+ stack_beneath);
+ }
+ }
+ }
+
+ {
+ Workspaces::iterator workspace_i(FindWorkspace(workspace));
+ if (workspace_i != workspaces_.end())
+ workspaces_.erase(workspace_i);
+ }
+
+ if (workspace->window()->children().empty()) {
+ pending_workspaces_.erase(workspace);
+ delete workspace->ReleaseWindow();
+ delete workspace;
+ } else {
+ pending_workspaces_.insert(workspace);
+ }
+}
+
+void WorkspaceManager2::SelectNextWorkspace() {
+ DCHECK_NE(active_workspace_, desktop_workspace());
+
+ Workspaces::const_iterator workspace_i(FindWorkspace(active_workspace_));
+ Workspaces::const_iterator next_workspace_i(workspace_i + 1);
+ if (next_workspace_i != workspaces_.end())
+ SetActiveWorkspace(*next_workspace_i);
+ else
+ SetActiveWorkspace(*(workspace_i - 1));
+ UpdateShelfVisibility();
+}
+
+void WorkspaceManager2::OnWindowAddedToWorkspace(Workspace2* workspace,
+ Window* child) {
+ // Do nothing (other than updating shelf visibility) as the right parent was
+ // chosen by way of GetParentForNewWindow() or we explicitly moved the window
+ // to the workspace.
+ if (workspace == active_workspace_)
+ UpdateShelfVisibility();
+}
+
+void WorkspaceManager2::OnWindowRemovedFromWorkspace(Workspace2* workspace,
+ Window* child) {
+ if (workspace->ShouldMoveToPending())
+ MoveWorkspaceToPendingOrDelete(workspace, NULL);
+}
+
+void WorkspaceManager2::OnWorkspaceChildWindowVisibilityChanged(
+ Workspace2* workspace,
+ Window* child) {
+ if (workspace->ShouldMoveToPending())
+ MoveWorkspaceToPendingOrDelete(workspace, NULL);
+}
+
+void WorkspaceManager2::OnWorkspaceWindowChildBoundsChanged(
+ Workspace2* workspace,
+ Window* child) {
+}
+
+void WorkspaceManager2::OnWorkspaceWindowShowStateChanged(
+ Workspace2* workspace,
+ Window* child,
+ ui::WindowShowState last_show_state) {
+ if (wm::IsWindowMinimized(child)) {
+ if (workspace->ShouldMoveToPending())
+ MoveWorkspaceToPendingOrDelete(workspace, NULL);
+ } else {
+ // Here's the cases that need to be handled:
+ // . More than one maximized window: move newly maximized window into
+ // own workspace.
+ // . One maximized window and not in a maximized workspace: move window
+ // into own workspace.
+ // . No maximized window and not in desktop: move to desktop and further
+ // any existing windows are stacked beneath |child|.
+ const bool is_active = wm::IsActiveWindow(child);
+ Workspace2* new_workspace = NULL;
+ const int max_count = workspace->GetNumMaximizedWindows();
+ if (max_count == 0) {
+ if (workspace != desktop_workspace()) {
+ {
+ AutoReset<bool> setter(&in_move_, true);
+ ReparentWindow(child, desktop_workspace()->window(), NULL);
+ }
+ MoveWorkspaceToPendingOrDelete(workspace, child);
+ new_workspace = desktop_workspace();
+ }
+ } else if (max_count == 1) {
+ if (workspace == desktop_workspace()) {
+ new_workspace = CreateWorkspace(true);
+ pending_workspaces_.insert(new_workspace);
+ ReparentWindow(child, new_workspace->window(), NULL);
+ }
+ } else {
+ new_workspace = CreateWorkspace(true);
+ pending_workspaces_.insert(new_workspace);
+ ReparentWindow(child, new_workspace->window(), NULL);
+ }
+ if (is_active && new_workspace)
+ SetActiveWorkspace(new_workspace);
+ }
+ UpdateShelfVisibility();
+}
+
+} // namespace internal
+} // namespace ash
« no previous file with comments | « ash/wm/workspace/workspace_manager2.h ('k') | ash/wm/workspace/workspace_manager2_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698