blob: d4ab3cbf24b98aa5d9e3e4c74040475d349849a7 [file] [log] [blame]
// 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_manager.h"
#include <algorithm>
#include "ash/screen_ash.h"
#include "ash/shell.h"
#include "ash/wm/property_util.h"
#include "ash/wm/shelf_layout_manager.h"
#include "ash/wm/window_animations.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace/managed_workspace.h"
#include "ash/wm/workspace/maximized_workspace.h"
#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/stl_util.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/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"
namespace {
// Returns a list of all the windows with layers in |result|. Optionally
// ignores the window |ignore_window|.
void BuildWindowList(const std::vector<aura::Window*>& windows,
aura::Window* ignore_window,
std::vector<aura::Window*>* result) {
for (size_t i = 0; i < windows.size(); ++i) {
if (windows[i] == ignore_window)
continue;
if (windows[i]->layer())
result->push_back(windows[i]);
BuildWindowList(windows[i]->transient_children(), ignore_window, result);
}
}
}
namespace ash {
namespace internal {
////////////////////////////////////////////////////////////////////////////////
// WindowManager, public:
WorkspaceManager::WorkspaceManager(aura::Window* contents_view)
: contents_view_(contents_view),
active_workspace_(NULL),
maximize_restore_window_(NULL),
grid_size_(0),
shelf_(NULL) {
DCHECK(contents_view);
}
WorkspaceManager::~WorkspaceManager() {
std::vector<Workspace*> copy_to_delete(workspaces_);
STLDeleteElements(&copy_to_delete);
}
// static
bool WorkspaceManager::ShouldManageWindow(aura::Window* window) {
return window->type() == aura::client::WINDOW_TYPE_NORMAL &&
!window->transient_parent() &&
ash::GetTrackedByWorkspace(window) &&
!ash::GetPersistsAcrossAllWorkspaces(window);
}
bool WorkspaceManager::Contains(aura::Window* window) const {
return FindBy(window) != NULL;
}
bool WorkspaceManager::IsInMaximizedMode() const {
return active_workspace_ &&
active_workspace_->type() == Workspace::TYPE_MAXIMIZED;
}
void WorkspaceManager::AddWindow(aura::Window* window) {
DCHECK(ShouldManageWindow(window));
Workspace* current_workspace = FindBy(window);
if (current_workspace) {
// Already know about this window. Make sure the workspace is active.
if (active_workspace_ != current_workspace) {
if (active_workspace_)
window->layer()->GetAnimator()->StopAnimating();
current_workspace->Activate();
}
window->Show();
UpdateShelfVisibility();
return;
}
Workspace* workspace = NULL;
Workspace::Type type_for_window = Workspace::TypeForWindow(window);
switch (type_for_window) {
case Workspace::TYPE_MANAGED:
// All normal windows go in the same workspace.
workspace = GetManagedWorkspace();
break;
case Workspace::TYPE_MAXIMIZED:
// All maximized windows go in their own workspace.
break;
}
if (!workspace)
workspace = CreateWorkspace(type_for_window);
workspace->AddWindowAfter(window, NULL);
workspace->Activate();
UpdateShelfVisibility();
}
void WorkspaceManager::RemoveWindow(aura::Window* window) {
Workspace* workspace = FindBy(window);
if (!workspace)
return;
workspace->RemoveWindow(window);
CleanupWorkspace(workspace);
}
void WorkspaceManager::SetActiveWorkspaceByWindow(aura::Window* window) {
Workspace* workspace = FindBy(window);
if (workspace)
workspace->Activate();
}
void WorkspaceManager::UpdateShelfVisibility() {
if (shelf_)
shelf_->UpdateVisibilityState();
}
WorkspaceManager::WindowState WorkspaceManager::GetWindowState() {
if (!shelf_ || !active_workspace_)
return WINDOW_STATE_DEFAULT;
// TODO: this code needs to be made multi-display aware.
gfx::Rect shelf_bounds(shelf_->GetIdealBounds());
const aura::Window::Windows& windows(contents_view_->children());
bool window_overlaps_launcher = false;
bool has_maximized_window = false;
for (aura::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 WINDOW_STATE_FULL_SCREEN;
}
if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds))
window_overlaps_launcher = true;
}
if (has_maximized_window)
return WINDOW_STATE_MAXIMIZED;
return window_overlaps_launcher ? WINDOW_STATE_WINDOW_OVERLAPS_SHELF :
WINDOW_STATE_DEFAULT;
}
void WorkspaceManager::ShowStateChanged(aura::Window* window) {
if (!ShouldManageWindow(window) || !FindBy(window))
return;
Workspace::Type old_type = FindBy(window)->type();
Workspace::Type new_type = Workspace::TypeForWindow(window);
if (new_type != old_type)
OnTypeOfWorkspacedNeededChanged(window);
UpdateShelfVisibility();
}
////////////////////////////////////////////////////////////////////////////////
// WorkspaceManager, private:
void WorkspaceManager::AddWorkspace(Workspace* workspace) {
DCHECK(std::find(workspaces_.begin(), workspaces_.end(),
workspace) == workspaces_.end());
if (active_workspace_) {
// New workspaces go right after current workspace.
Workspaces::iterator i = std::find(workspaces_.begin(), workspaces_.end(),
active_workspace_);
workspaces_.insert(++i, workspace);
} else {
workspaces_.push_back(workspace);
}
}
void WorkspaceManager::RemoveWorkspace(Workspace* workspace) {
Workspaces::iterator i = std::find(workspaces_.begin(),
workspaces_.end(),
workspace);
DCHECK(i != workspaces_.end());
i = workspaces_.erase(i);
if (active_workspace_ == workspace) {
// TODO: need mru order.
if (i != workspaces_.end())
SetActiveWorkspace(*i);
else if (!workspaces_.empty())
SetActiveWorkspace(workspaces_.back());
else
active_workspace_ = NULL;
}
}
void WorkspaceManager::SetVisibilityOfWorkspaceWindows(
ash::internal::Workspace* workspace,
AnimateChangeType change_type,
bool value) {
std::vector<aura::Window*> children;
BuildWindowList(workspace->windows(), maximize_restore_window_, &children);
SetWindowLayerVisibility(children, change_type, value);
}
void WorkspaceManager::SetWindowLayerVisibility(
const std::vector<aura::Window*>& windows,
AnimateChangeType change_type,
bool value) {
for (size_t i = 0; i < windows.size(); ++i) {
ui::Layer* layer = windows[i]->layer();
// Only show the layer for windows that want to be visible.
if (layer && (!value || windows[i]->TargetVisibility())) {
bool animation_disabled =
windows[i]->GetProperty(aura::client::kAnimationsDisabledKey);
WindowVisibilityAnimationType animation_type =
GetWindowVisibilityAnimationType(windows[i]);
windows[i]->SetProperty(aura::client::kAnimationsDisabledKey,
change_type == DONT_ANIMATE);
bool update_layer = true;
if (change_type == ANIMATE) {
ash::SetWindowVisibilityAnimationType(
windows[i],
value ? ash::WINDOW_VISIBILITY_ANIMATION_TYPE_WORKSPACE_SHOW :
ash::WINDOW_VISIBILITY_ANIMATION_TYPE_WORKSPACE_HIDE);
if (ash::internal::AnimateOnChildWindowVisibilityChanged(
windows[i], value))
update_layer = false;
}
if (update_layer)
layer->SetVisible(value);
// Reset the animation type so it isn't used in a future hide/show.
ash::SetWindowVisibilityAnimationType(
windows[i], animation_type);
windows[i]->SetProperty(aura::client::kAnimationsDisabledKey,
animation_disabled);
}
}
}
Workspace* WorkspaceManager::GetActiveWorkspace() const {
return active_workspace_;
}
Workspace* WorkspaceManager::FindBy(aura::Window* window) const {
int index = GetWorkspaceIndexContaining(window);
return index < 0 ? NULL : workspaces_[index];
}
void WorkspaceManager::SetActiveWorkspace(Workspace* workspace) {
if (active_workspace_ == workspace)
return;
DCHECK(std::find(workspaces_.begin(), workspaces_.end(),
workspace) != workspaces_.end());
if (active_workspace_)
SetVisibilityOfWorkspaceWindows(active_workspace_, ANIMATE, false);
Workspace* last_active = active_workspace_;
active_workspace_ = workspace;
if (active_workspace_) {
SetVisibilityOfWorkspaceWindows(active_workspace_,
last_active ? ANIMATE : DONT_ANIMATE, true);
UpdateShelfVisibility();
}
}
// Returns the index of the workspace that contains the |window|.
int WorkspaceManager::GetWorkspaceIndexContaining(aura::Window* window) const {
for (Workspaces::const_iterator i = workspaces_.begin();
i != workspaces_.end();
++i) {
if ((*i)->Contains(window))
return i - workspaces_.begin();
}
return -1;
}
void WorkspaceManager::SetWindowBounds(aura::Window* window,
const gfx::Rect& bounds) {
window->SetBounds(bounds);
}
void WorkspaceManager::OnTypeOfWorkspacedNeededChanged(aura::Window* window) {
DCHECK(ShouldManageWindow(window));
Workspace* current_workspace = FindBy(window);
DCHECK(current_workspace);
Workspace* new_workspace = NULL;
if (Workspace::TypeForWindow(window) == Workspace::TYPE_MAXIMIZED) {
// Unmaximized -> maximized; create a new workspace.
current_workspace->RemoveWindow(window);
new_workspace = CreateWorkspace(Workspace::TYPE_MAXIMIZED);
new_workspace->AddWindowAfter(window, NULL);
} else {
// Maximized -> unmaximized; move window to unmaximized workspace.
new_workspace = GetManagedWorkspace();
current_workspace->RemoveWindow(window);
if (!new_workspace)
new_workspace = CreateWorkspace(Workspace::TYPE_MANAGED);
new_workspace->AddWindowAfter(window, NULL);
}
maximize_restore_window_ = window;
SetActiveWorkspace(new_workspace);
maximize_restore_window_ = NULL;
// Delete at the end so that we don't attempt to switch to another
// workspace in RemoveWorkspace().
CleanupWorkspace(current_workspace);
}
Workspace* WorkspaceManager::GetManagedWorkspace() {
for (size_t i = 0; i < workspaces_.size(); ++i) {
if (workspaces_[i]->type() == Workspace::TYPE_MANAGED)
return workspaces_[i];
}
return NULL;
}
Workspace* WorkspaceManager::CreateWorkspace(Workspace::Type type) {
Workspace* workspace = NULL;
if (type == Workspace::TYPE_MAXIMIZED)
workspace = new MaximizedWorkspace(this);
else
workspace = new ManagedWorkspace(this);
AddWorkspace(workspace);
return workspace;
}
void WorkspaceManager::CleanupWorkspace(Workspace* workspace) {
if (workspace->type() != Workspace::TYPE_MANAGED && workspace->is_empty())
delete workspace;
}
} // namespace internal
} // namespace ash