blob: 1d6f775f5632fb9dccbc0995ebe82850c56059e0 [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_cycler_animator.h"
#include <algorithm>
#include <cmath>
#include "ash/launcher/launcher.h"
#include "ash/root_window_controller.h"
#include "ash/screen_ash.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/property_util.h"
#include "ash/wm/window_properties.h"
#include "ash/wm/workspace/colored_window_controller.h"
#include "ash/wm/workspace/workspace.h"
#include "ash/wm/workspace/workspace_cycler_configuration.h"
#include "base/values.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/base/events/event_utils.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/transform_util.h"
#include "ui/views/widget/widget.h"
typedef ash::WorkspaceCyclerConfiguration Config;
namespace ash {
namespace internal {
// Class which computes the transform, brightness, and visbility of workspaces
// on behalf of the animator.
class StyleCalculator {
public:
StyleCalculator(const gfx::Rect& screen_bounds,
const gfx::Rect& maximized_bounds,
size_t num_workspaces);
~StyleCalculator();
// Returns the transform, brightness, and visibility that the workspace at
// |workspace_index| should be animated to when the user has stopped cycling
// through workspaces given |selected_workspace_index|.
// Unlike GetTargetProperties(), the method does not have a |scroll_delta|
// argument as stopping the workspace cycler always results in complately
// selecting a workspace.
void GetStoppedTargetProperties(size_t selected_workspace_index,
size_t workspace_index,
gfx::Transform* transform,
float* brightness,
bool* visible) const;
// Returns the transform, brightness, and visibility that the workspace at
// |workspace_index| should be animated to when the user is cycling through
// workspaces given |selected_workspace_index| and |scroll_delta|.
// |scroll_delta| is the amount of pixels that the user has scrolled
// vertically since completely selecting the workspace at
// |selected_workspace_index|.
void GetTargetProperties(size_t selected_workspace_index,
size_t workspace_index,
double scroll_delta,
gfx::Transform* transform,
float* brightness,
bool* visible) const;
private:
// Returns the target animation transform of the workspace which is at
// |offset_from_selected| from the selected workspace when the user is not
// cycling through workspaces.
// This method assumes |scroll_delta| == 0.
gfx::Transform GetStoppedTargetTransformForOffset(
int offset_from_selected) const;
// Returns the target animation transform of the workspace which is at
// |offset_from_selected| from the selected workspace.
// This method like all the methods below assumes |scroll_delta| == 0 for the
// sake of simplicity. The transform for |scroll_delta| != 0 can be obtained
// via interpolation.
gfx::DecomposedTransform GetTargetTransformForOffset(
int offset_from_selected) const;
// Returns the target animation brightness of the workspace which is at
// |offset_from_selected| from the selected workspace.
// This method assumes |scroll_delta| == 0.
float GetTargetBrightnessForOffset(int offset_from_selected) const;
// Returns the target animation visibility of the workspace with the given
// parameters.
// This method assumes |scroll_delta| == 0.
bool GetTargetVisibilityForOffset(int offset_from_selected,
size_t workspace_index) const;
// Returns the minimum offset from the selected workspace at which a
// workspace can be visible. The offset is used to limit the amount of
// workspaces which are simultaneously visible.
int GetMinVisibleOffsetFromSelected() const;
// Returns the maximum offset from the selected workspace at which a
// workspace can be visible. The offset is used to limit the amount of
// workspaces which are simultaneously visible.
int GetMaxVisibleOffsetFromSelected() const;
// The bounds of the display containing the workspaces in workspace
// coordinates, including the shelf if any.
const gfx::Rect screen_bounds_;
// The bounds of a maximized window. This excludes the shelf if any.
const gfx::Rect maximized_bounds_;
// The combined number of visible and hidden workspaces.
size_t num_workspaces_;
DISALLOW_COPY_AND_ASSIGN(StyleCalculator);
};
StyleCalculator::StyleCalculator(const gfx::Rect& screen_bounds,
const gfx::Rect& maximized_bounds,
size_t num_workspaces)
: screen_bounds_(screen_bounds),
maximized_bounds_(maximized_bounds),
num_workspaces_(num_workspaces) {
}
StyleCalculator::~StyleCalculator() {
}
void StyleCalculator::GetStoppedTargetProperties(
size_t selected_workspace_index,
size_t workspace_index,
gfx::Transform* transform,
float* brightness,
bool* visible) const {
DCHECK_LT(selected_workspace_index, num_workspaces_);
DCHECK_LT(workspace_index, num_workspaces_);
int offset_from_selected = static_cast<int>(
workspace_index - selected_workspace_index);
if (transform)
*transform = GetStoppedTargetTransformForOffset(offset_from_selected);
if (brightness)
*brightness = GetTargetBrightnessForOffset(offset_from_selected);
// All the workspaces other than the selected workspace are either occluded by
// the selected workspace or off screen.
if (visible)
*visible = (selected_workspace_index == workspace_index);
}
void StyleCalculator::GetTargetProperties(
size_t selected_workspace_index,
size_t workspace_index,
double scroll_delta,
gfx::Transform* transform,
float* brightness,
bool* visible) const {
DCHECK_LT(selected_workspace_index, num_workspaces_);
DCHECK_LT(workspace_index, num_workspaces_);
int offset_from_selected = static_cast<int>(
workspace_index - selected_workspace_index);
int first_offset_from_selected = offset_from_selected;
int second_offset_from_selected = offset_from_selected;
if (scroll_delta < 0) {
// The user is part of the way to selecting the workspace at
// |selected_workspace_index| - 1 -> |offset_from_selected| + 1.
second_offset_from_selected = offset_from_selected + 1;
} else if (scroll_delta > 0) {
// The user is part of the way to selecting the workspace at
// |selected_workspace_index| + 1 -> |offset_from_selected| - 1.
second_offset_from_selected = offset_from_selected - 1;
}
double scroll_distance_to_cycle_to_next_workspace = Config::GetDouble(
Config::SCROLL_DISTANCE_TO_CYCLE_TO_NEXT_WORKSPACE);
double progress = fabs(
scroll_delta / scroll_distance_to_cycle_to_next_workspace);
DCHECK_GT(1.0, progress);
if (transform) {
gfx::DecomposedTransform first_transform = GetTargetTransformForOffset(
first_offset_from_selected);
gfx::DecomposedTransform interpolated_transform;
if (first_offset_from_selected == second_offset_from_selected) {
interpolated_transform = first_transform;
} else {
gfx::DecomposedTransform second_transform = GetTargetTransformForOffset(
second_offset_from_selected);
gfx::BlendDecomposedTransforms(&interpolated_transform,
second_transform,
first_transform,
progress);
}
*transform = gfx::ComposeTransform(interpolated_transform);
}
if (brightness) {
float first_brightness = GetTargetBrightnessForOffset(
first_offset_from_selected);
float second_brightness = GetTargetBrightnessForOffset(
second_offset_from_selected);
*brightness = first_brightness + progress *
(second_brightness - first_brightness);
}
if (visible) {
bool first_visible = GetTargetVisibilityForOffset(
first_offset_from_selected, workspace_index);
bool second_visible = GetTargetVisibilityForOffset(
second_offset_from_selected, workspace_index);
*visible = (first_visible || second_visible);
}
}
gfx::Transform StyleCalculator::GetStoppedTargetTransformForOffset(
int offset_from_selected) const {
if (offset_from_selected <= 0) {
// The selected workspace takes up the entire screen. The workspaces deeper
// than the selected workspace are stacked exactly under the selected
// workspace and are completely occluded by it.
return gfx::Transform();
}
// The workspaces shallower than the selected workspace are stacked exactly
// on top of each other offscreen.
double max_scale = Config::GetDouble(Config::MAX_SCALE);
gfx::Transform transform;
transform.Translate(0, screen_bounds_.height());
transform.Scale(max_scale, max_scale);
return transform;
}
gfx::DecomposedTransform StyleCalculator::GetTargetTransformForOffset(
int offset_from_selected) const {
// When cycling, the workspaces are spread out from the positions computed by
// GetStoppedTargetTransformForOffset(). The transforms are computed so that
// on screen the workspaces look like this:
// ____________________________________________
// | ________________________ |
// | | deepest workpace |_________|_
// | _|________________________|_ | |
// | | | | |
// | _|____________________________|_ | |_ top stack.
// | | selected / shallowest workspace| | |
// | | | | |
// | | |_____|_|
// | | | |
// | | | |
// | _|________________________________|_ |
// | | deepest workspace |___|_
// | _|____________________________________|_ | |_ bottom stack.
// | | shallowest workspace |_|_|
// |_|________________________________________|_|
// The selected workspace is the most visible workspace. It is the workspace
// which will be activated if the user does not do any more cycling.
bool in_top_stack = (offset_from_selected <= 0);
int min_visible_offset_from_selected = GetMinVisibleOffsetFromSelected();
int max_visible_offset_from_selected = GetMaxVisibleOffsetFromSelected();
// Give workspaces at hidden offsets the transform of the workspace at the
// nearest visible offset. This is needed to produce a valid result when
// interpolating between the values of GetTargetTransformForOffset() for a
// visible and hidden offset.
if (offset_from_selected < min_visible_offset_from_selected)
offset_from_selected = min_visible_offset_from_selected;
else if (offset_from_selected > max_visible_offset_from_selected)
offset_from_selected = max_visible_offset_from_selected;
double selected_scale = Config::GetDouble(Config::SELECTED_SCALE);
double scale = selected_scale;
if (in_top_stack) {
double min_scale = Config::GetDouble(Config::MIN_SCALE);
scale -= static_cast<double>(offset_from_selected) /
min_visible_offset_from_selected * (selected_scale - min_scale);
} else {
double max_scale = Config::GetDouble(Config::MAX_SCALE);
scale += static_cast<double>(offset_from_selected) /
max_visible_offset_from_selected * (max_scale - selected_scale);
}
// Compute the workspace's y offset.
double y_offset = 0;
if (offset_from_selected == 0) {
y_offset = Config::GetDouble(Config::SELECTED_Y_OFFSET);
} else {
Config::Property y_offsets_property;
if (in_top_stack) {
y_offsets_property = Config::DEEPER_THAN_SELECTED_Y_OFFSETS;
} else {
y_offsets_property = Config::SHALLOWER_THAN_SELECTED_Y_OFFSETS;
y_offset = maximized_bounds_.height();
}
const base::ListValue& y_offsets = Config::GetListValue(y_offsets_property);
size_t y_offset_index = static_cast<size_t>(
std::abs(offset_from_selected) - 1);
DCHECK_GT(y_offsets.GetSize(), y_offset_index);
double y_offset_config_value = 0;
y_offsets.GetDouble(y_offset_index, &y_offset_config_value);
y_offset += y_offset_config_value;
}
// Center the workspace horizontally.
double x_offset = maximized_bounds_.width() * (1 - scale) / 2;
gfx::DecomposedTransform transform;
transform.translate[0] = x_offset;
transform.translate[1] = y_offset;
transform.scale[0] = scale;
transform.scale[1] = scale;
return transform;
}
float StyleCalculator::GetTargetBrightnessForOffset(
int offset_from_selected) const {
int max_visible_distance_from_selected = std::max(
std::abs(GetMinVisibleOffsetFromSelected()),
GetMaxVisibleOffsetFromSelected());
return Config::GetDouble(Config::MIN_BRIGHTNESS) * std::min(
1.0,
static_cast<double>(std::abs(offset_from_selected)) /
max_visible_distance_from_selected);
}
bool StyleCalculator::GetTargetVisibilityForOffset(
int offset_from_selected,
size_t workspace_index) const {
// The workspace at the highest possible index is the shallowest workspace
// out of both stacks and is always visible.
// The workspace at |GetMaxVisibleOffsetFromSelected()| is hidden because it
// has the same transform as the shallowest workspace and is completely
// occluded by it.
if (workspace_index == num_workspaces_ - 1)
return true;
return offset_from_selected >= GetMinVisibleOffsetFromSelected() &&
offset_from_selected < GetMaxVisibleOffsetFromSelected();
}
int StyleCalculator::GetMinVisibleOffsetFromSelected() const {
const base::ListValue& y_offsets = Config::GetListValue(
Config::DEEPER_THAN_SELECTED_Y_OFFSETS);
// Hide workspaces for which there is no y offset specified.
return -1 * static_cast<int>(y_offsets.GetSize());
}
int StyleCalculator::GetMaxVisibleOffsetFromSelected() const {
const base::ListValue& y_offsets = Config::GetListValue(
Config::SHALLOWER_THAN_SELECTED_Y_OFFSETS);
// Hide workspaces for which there is no y offset specified.
return y_offsets.GetSize();
}
WorkspaceCyclerAnimator::WorkspaceCyclerAnimator(Delegate* delegate)
: delegate_(delegate),
initial_active_workspace_index_(0),
selected_workspace_index_(0),
scroll_delta_(0),
animation_type_(NONE) {}
WorkspaceCyclerAnimator::~WorkspaceCyclerAnimator() {
StopObservingImplicitAnimations();
animation_type_ = NONE;
}
void WorkspaceCyclerAnimator::Init(const std::vector<Workspace*>& workspaces,
Workspace* initial_active_workspace) {
workspaces_ = workspaces;
std::vector<Workspace*>::iterator it = std::find(workspaces_.begin(),
workspaces_.end(), initial_active_workspace);
DCHECK(it != workspaces_.end());
initial_active_workspace_index_ = it - workspaces_.begin();
selected_workspace_index_ = initial_active_workspace_index_;
screen_bounds_ = ScreenAsh::GetDisplayBoundsInParent(
workspaces_[0]->window());
maximized_bounds_ = ScreenAsh::GetMaximizedWindowBoundsInParent(
workspaces_[0]->window());
style_calculator_.reset(new StyleCalculator(
screen_bounds_, maximized_bounds_, workspaces_.size()));
}
void WorkspaceCyclerAnimator::AnimateStartingCycler() {
// Set kCyclingThroughWorkspaces so that the window frame is painted with the
// correct style.
workspaces_[0]->window()->GetRootWindow()->SetProperty(
ash::internal::kCyclingThroughWorkspacesKey, true);
// Ensure that the workspaces are stacked with respect to their order
// in |workspaces_|.
aura::Window* parent = workspaces_[0]->window()->parent();
DCHECK(parent);
for (size_t i = 0; i < workspaces_.size() - 1; ++i) {
parent->StackChildAbove(workspaces_[i + 1]->window(),
workspaces_[i]->window());
}
// Set the initial transform and brightness of the workspaces such that they
// animate correctly when AnimateToUpdatedState() is called after the loop.
// Start at index 1 as the desktop workspace is not animated.
for (size_t i = 1; i < workspaces_.size(); ++i) {
aura::Window* window = workspaces_[i]->window();
ui::Layer* layer = window->layer();
gfx::Transform transform;
float brightness = 0.0f;
style_calculator_->GetStoppedTargetProperties(selected_workspace_index_, i,
&transform, &brightness, NULL);
layer->SetTransform(transform);
layer->SetLayerBrightness(brightness);
}
int start_cycler_animation_duration = static_cast<int>(Config::GetDouble(
Config::START_CYCLER_ANIMATION_DURATION));
scroll_delta_ = 0;
animation_type_ = CYCLER_START;
AnimateToUpdatedState(start_cycler_animation_duration);
// Dim the desktop workspace and the desktop background.
AnimateTo(0, false, start_cycler_animation_duration, gfx::Transform(),
Config::GetDouble(Config::DESKTOP_WORKSPACE_BRIGHTNESS), true);
aura::Window* background = GetDesktopBackground();
if (background) {
ui::Layer* layer = background->layer();
if (!background->IsVisible()) {
background->Show();
layer->SetOpacity(0.0f);
}
{
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
start_cycler_animation_duration));
layer->SetOpacity(Config::GetDouble(Config::BACKGROUND_OPACITY));
}
}
// Create a window to simulate a fully opaque launcher. This prevents
// workspaces from showing from behind the launcher.
CreateLauncherBackground();
}
void WorkspaceCyclerAnimator::AnimateStoppingCycler() {
if (scroll_delta_ != 0) {
// Completely select the workspace at |selected_workspace_index_|.
int animation_duration = GetAnimationDurationForChangeInScrollDelta(
-scroll_delta_);
scroll_delta_ = 0;
animation_type_ = CYCLER_COMPLETELY_SELECT;
AnimateToUpdatedState(animation_duration);
return;
}
int stop_cycler_animation_duration = static_cast<int>(Config::GetDouble(
Config::STOP_CYCLER_ANIMATION_DURATION));
animation_type_ = CYCLER_END;
AnimateToUpdatedState(stop_cycler_animation_duration);
AnimateTo(0, false, stop_cycler_animation_duration, gfx::Transform(),
0.0f, true);
aura::Window* background = GetDesktopBackground();
if (background) {
ui::Layer* layer = background->layer();
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
stop_cycler_animation_duration));
layer->SetOpacity((selected_workspace_index_ == 0) ? 1.0f : 0.0f);
}
}
void WorkspaceCyclerAnimator::AbortAnimations() {
StopObservingImplicitAnimations();
animation_type_ = NONE;
CyclerStopped(initial_active_workspace_index_);
}
void WorkspaceCyclerAnimator::AnimateCyclingByScrollDelta(double scroll_delta) {
if (scroll_delta == 0)
return;
// Drop any updates received while an animation is running.
// TODO(pkotwicz): Do something better.
if (animation_type_ != NONE)
return;
if (ui::IsNaturalScrollEnabled())
scroll_delta *= -1;
double old_scroll_delta = scroll_delta_;
scroll_delta_ += scroll_delta;
double scroll_distance_to_cycle_to_next_workspace = Config::GetDouble(
Config::SCROLL_DISTANCE_TO_CYCLE_TO_NEXT_WORKSPACE);
double min_scroll_delta = -1 *
static_cast<double>(selected_workspace_index_) *
scroll_distance_to_cycle_to_next_workspace;
double max_scroll_delta = static_cast<double>(
workspaces_.size() - 1 - selected_workspace_index_) *
scroll_distance_to_cycle_to_next_workspace;
if (scroll_delta_ < min_scroll_delta)
scroll_delta_ = min_scroll_delta;
else if (scroll_delta_ > max_scroll_delta)
scroll_delta_ = max_scroll_delta;
if (scroll_delta_ == old_scroll_delta)
return;
// Set the selected workspace to the workspace that the user is closest to
// selecting completely. A workspace is completely selected when the user
// has scrolled the exact amount to select the workspace with no undershoot /
// overshoot.
int workspace_change = floor(scroll_delta_ /
scroll_distance_to_cycle_to_next_workspace + .5);
selected_workspace_index_ += workspace_change;
// Set |scroll_delta_| to the amount of undershoot / overshoot.
scroll_delta_ -= workspace_change *
scroll_distance_to_cycle_to_next_workspace;
int animation_duration = GetAnimationDurationForChangeInScrollDelta(
scroll_delta_ - old_scroll_delta);
animation_type_ = CYCLER_UPDATE;
AnimateToUpdatedState(animation_duration);
}
void WorkspaceCyclerAnimator::AnimateToUpdatedState(int animation_duration) {
DCHECK_NE(NONE, animation_type_);
bool animator_to_wait_for_selected = false;
// Start at index 1, as the animator does not animate the desktop workspace.
for (size_t i = 1; i < workspaces_.size(); ++i) {
gfx::Transform transform;
float brightness = 0.0f;
bool visible = false;
if (animation_type_ == CYCLER_END) {
DCHECK_EQ(0, scroll_delta_);
style_calculator_->GetStoppedTargetProperties(selected_workspace_index_,
i, &transform, &brightness, &visible);
} else {
style_calculator_->GetTargetProperties(selected_workspace_index_, i,
scroll_delta_, &transform, &brightness, &visible);
}
aura::Window* window = workspaces_[i]->window();
int workspace_animation_duration = animation_duration;
if (!window->IsVisible()) {
if (visible) {
// The workspace's previous state is unknown, set the state immediately.
workspace_animation_duration = 0;
} else {
// Don't bother animating workspaces which aren't visible.
continue;
}
}
// Hide the workspace after |animation_duration| in the case of
// |visible| == false as the workspace to be hidden may not be completely
// occluded till the animation has completed.
bool wait_for_animator = false;
if (!animator_to_wait_for_selected && workspace_animation_duration != 0) {
// The completion of the animations for this workspace will be used to
// indicate that the animations for all workspaces have completed.
wait_for_animator = true;
animator_to_wait_for_selected = true;
}
AnimateTo(i, wait_for_animator, workspace_animation_duration,
transform, brightness, visible);
}
// All of the animations started by this method were of zero duration.
// Call the animation callback.
if (!animator_to_wait_for_selected)
OnImplicitAnimationsCompleted();
}
void WorkspaceCyclerAnimator::CyclerStopped(size_t visible_workspace_index) {
aura::Window* root_window = workspaces_[0]->window()->GetRootWindow();
root_window->SetProperty(ash::internal::kCyclingThroughWorkspacesKey,
false);
for(size_t i = 0; i < workspaces_.size(); ++i) {
aura::Window* window = workspaces_[i]->window();
ui::Layer* layer = window->layer();
layer->SetLayerBrightness(0.0f);
layer->SetTransform(gfx::Transform());
if (i == visible_workspace_index)
window->Show();
else
window->Hide();
}
aura::Window* background = GetDesktopBackground();
if (background) {
background->layer()->SetOpacity(1.0);
if (visible_workspace_index != 0u)
background->Hide();
}
launcher_background_controller_.reset();
}
void WorkspaceCyclerAnimator::AnimateTo(size_t workspace_index,
bool wait_for_animation_to_complete,
int animation_duration,
const gfx::Transform& target_transform,
float target_brightness,
bool target_visibility) {
aura::Window* window = workspaces_[workspace_index]->window();
ui::Layer* layer = window->layer();
ui::LayerAnimator* animator = layer->GetAnimator();
ui::ScopedLayerAnimationSettings settings(animator);
if (wait_for_animation_to_complete) {
StopObservingImplicitAnimations();
DCHECK_NE(animation_type_, NONE);
settings.AddObserver(this);
}
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
animation_duration));
if (target_visibility) {
window->Show();
// Set the opacity in case the layer has some weird initial state.
layer->SetOpacity(1.0f);
} else {
window->Hide();
}
layer->SetTransform(target_transform);
layer->SetLayerBrightness(target_brightness);
}
int WorkspaceCyclerAnimator::GetAnimationDurationForChangeInScrollDelta(
double change) const {
double ratio = Config::GetDouble(
Config::CYCLER_STEP_ANIMATION_DURATION_RATIO);
return static_cast<int>(fabs(change) * ratio);
}
void WorkspaceCyclerAnimator::CreateLauncherBackground() {
if (screen_bounds_ == maximized_bounds_)
return;
// TODO(pkotwicz): Figure out what to do when the launcher visible state is
// SHELF_AUTO_HIDE.
ShelfLayoutManager* shelf_layout_manager =
ShelfLayoutManager::ForLauncher(workspaces_[0]->window());
if (!shelf_layout_manager->IsVisible())
return;
aura::Window* launcher_window = shelf_layout_manager->shelf_widget()->
GetNativeWindow();
gfx::Rect shelf_bounds = shelf_layout_manager->GetIdealBounds();
launcher_background_controller_.reset(new ColoredWindowController(
launcher_window->parent(), "LauncherBackground"));
launcher_background_controller_->SetColor(SK_ColorBLACK);
aura::Window* tint_window =
launcher_background_controller_->GetWidget()->GetNativeWindow();
tint_window->SetBounds(shelf_bounds);
launcher_window->parent()->StackChildBelow(tint_window, launcher_window);
tint_window->Show();
}
aura::Window* WorkspaceCyclerAnimator::GetDesktopBackground() const {
RootWindowController* root_controller = GetRootWindowController(
workspaces_[0]->window()->GetRootWindow());
if (!root_controller)
return NULL;
return root_controller->GetContainer(
kShellWindowId_DesktopBackgroundContainer);
}
void WorkspaceCyclerAnimator::NotifyDelegate(
AnimationType completed_animation) {
if (completed_animation == CYCLER_START)
delegate_->StartWorkspaceCyclerAnimationFinished();
else if (completed_animation == CYCLER_END)
delegate_->StopWorkspaceCyclerAnimationFinished();
}
void WorkspaceCyclerAnimator::OnImplicitAnimationsCompleted() {
AnimationType completed_animation = animation_type_;
animation_type_ = NONE;
if (completed_animation == CYCLER_COMPLETELY_SELECT)
AnimateStoppingCycler();
else if (completed_animation == CYCLER_END)
CyclerStopped(selected_workspace_index_);
if (completed_animation == CYCLER_START ||
completed_animation == CYCLER_END) {
// Post a task to notify the delegate of the animation completion because
// the delegate may delete |this| as a result of getting notified.
base::MessageLoopForUI::current()->PostTask(
FROM_HERE,
base::Bind(&WorkspaceCyclerAnimator::NotifyDelegate,
AsWeakPtr(),
completed_animation));
}
}
} // namespace internal
} // namespace ash