blob: 93d514c80cd49737d0f61453503357bdee53d572 [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 "ui/wm/core/window_animations.h"
#include <math.h>
#include <algorithm>
#include <memory>
#include "base/bind.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_observer.h"
#include "ui/base/class_property.h"
#include "ui/compositor/animation_throughput_reporter.h"
#include "ui/compositor/compositor_observer.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/animation/animation.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/gfx/interpolated_transform.h"
#include "ui/wm/core/window_properties.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/core/wm_core_switches.h"
#include "ui/wm/public/animation_host.h"
namespace wm {
namespace {
// A base class for hiding animation observer which has two roles:
// 1) Notifies AnimationHost at the end of hiding animation.
// 2) Detaches the window's layers for hiding animation and deletes
// them upon completion of the animation. This is necessary to a)
// ensure that the animation continues in the event of the window being
// deleted, and b) to ensure that the animation is visible even if the
// window gets restacked below other windows when focus or activation
// changes.
// The subclass will determine when the animation is completed.
class HidingWindowAnimationObserverBase : public aura::WindowObserver {
public:
explicit HidingWindowAnimationObserverBase(aura::Window* window)
: window_(window) {
window_->AddObserver(this);
}
HidingWindowAnimationObserverBase(const HidingWindowAnimationObserverBase&) =
delete;
HidingWindowAnimationObserverBase& operator=(
const HidingWindowAnimationObserverBase&) = delete;
~HidingWindowAnimationObserverBase() override {
if (window_)
window_->RemoveObserver(this);
}
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
DCHECK_EQ(window, window_);
WindowInvalid();
}
void OnWindowDestroyed(aura::Window* window) override {
DCHECK_EQ(window, window_);
WindowInvalid();
}
// Detach the current layers and create new layers for |window_|.
// Stack the original layers above |window_| and its transient
// children. If the window has transient children, the original
// layers will be moved above the top most transient child so that
// activation change does not put the window above the animating
// layer.
void DetachAndRecreateLayers() {
layer_owner_ = RecreateLayers(window_);
if (window_->parent()) {
const aura::Window::Windows& transient_children =
GetTransientChildren(window_);
auto iter = std::find(window_->parent()->children().begin(),
window_->parent()->children().end(), window_);
DCHECK(iter != window_->parent()->children().end());
aura::Window* topmost_transient_child = nullptr;
for (++iter; iter != window_->parent()->children().end(); ++iter) {
if (base::Contains(transient_children, *iter))
topmost_transient_child = *iter;
}
if (topmost_transient_child) {
window_->parent()->layer()->StackAbove(
layer_owner_->root(), topmost_transient_child->layer());
}
}
// Reset the transform for the |window_|. Because the animation may have
// changed the transform, when recreating the layers we need to reset the
// transform otherwise the recreated layer has the transform installed
// for the animation.
window_->layer()->SetTransform(gfx::Transform());
}
protected:
// Invoked when the hiding animation is completed. It will delete
// 'this', and no operation should be made on this object after this
// point.
void OnAnimationCompleted() {
// Window may have been destroyed by this point.
if (window_) {
AnimationHost* animation_host = GetAnimationHost(window_);
if (animation_host)
animation_host->OnWindowHidingAnimationCompleted();
window_->RemoveObserver(this);
}
delete this;
}
private:
// Invoked when the window is destroyed (or destroying).
void WindowInvalid() {
layer_owner_->root()->SuppressPaint();
window_->RemoveObserver(this);
window_ = nullptr;
}
raw_ptr<aura::Window> window_;
// The owner of detached layers.
std::unique_ptr<ui::LayerTreeOwner> layer_owner_;
};
// TODO(crbug.com/1021774): Find a better home and merge with
// ash::metris_util::ForSmoothness.
using SmoothnessCallback = base::RepeatingCallback<void(int smoothness)>;
ui::AnimationThroughputReporter::ReportCallback ForSmoothness(
SmoothnessCallback callback) {
return base::BindRepeating(
[](SmoothnessCallback callback,
const cc::FrameSequenceMetrics::CustomReportData& data) {
const int smoothness =
std::floor(100.0f * data.frames_produced / data.frames_expected);
callback.Run(smoothness);
},
std::move(callback));
}
void ReportHideSmoothness(int smoothness) {
UMA_HISTOGRAM_PERCENTAGE("Ash.Window.AnimationSmoothness.Hide", smoothness);
}
} // namespace
// A HidingWindowAnimationObserver that deletes observer and detached
// layers upon the completion of the implicit animation.
class ImplicitHidingWindowAnimationObserver
: public HidingWindowAnimationObserverBase,
public ui::ImplicitAnimationObserver {
public:
ImplicitHidingWindowAnimationObserver(
aura::Window* window,
ui::ScopedLayerAnimationSettings* settings);
ImplicitHidingWindowAnimationObserver(
const ImplicitHidingWindowAnimationObserver&) = delete;
ImplicitHidingWindowAnimationObserver& operator=(
const ImplicitHidingWindowAnimationObserver&) = delete;
~ImplicitHidingWindowAnimationObserver() override {}
// ui::ImplicitAnimationObserver:
void OnImplicitAnimationsCompleted() override;
};
namespace {
const int kDefaultAnimationDurationForMenuMS = 150;
const float kWindowAnimation_HideOpacity = 0.f;
const float kWindowAnimation_ShowOpacity = 1.f;
const float kWindowAnimation_TranslateFactor = 0.5f;
const float kWindowAnimation_ScaleFactor = .95f;
const int kWindowAnimation_Rotate_DurationMS = 180;
const int kWindowAnimation_Rotate_OpacityDurationPercent = 90;
const float kWindowAnimation_Rotate_TranslateY = -20.f;
const float kWindowAnimation_Rotate_PerspectiveDepth = 500.f;
const float kWindowAnimation_Rotate_DegreesX = 5.f;
const float kWindowAnimation_Rotate_ScaleFactor = .99f;
const float kWindowAnimation_Bounce_Scale = 1.02f;
const int kWindowAnimation_Bounce_DurationMS = 180;
const int kWindowAnimation_Bounce_GrowShrinkDurationPercent = 40;
base::TimeDelta GetWindowVisibilityAnimationDuration(
const aura::Window& window) {
base::TimeDelta duration =
window.GetProperty(kWindowVisibilityAnimationDurationKey);
if (duration.is_zero() &&
window.GetType() == aura::client::WINDOW_TYPE_MENU) {
return base::Milliseconds(kDefaultAnimationDurationForMenuMS);
}
return duration;
}
// Gets/sets the WindowVisibilityAnimationType associated with a window.
// TODO(beng): redundant/fold into method on public api?
int GetWindowVisibilityAnimationType(aura::Window* window) {
int type = window->GetProperty(kWindowVisibilityAnimationTypeKey);
if (type == WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT) {
return (window->GetType() == aura::client::WINDOW_TYPE_MENU ||
window->GetType() == aura::client::WINDOW_TYPE_TOOLTIP)
? WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
: WINDOW_VISIBILITY_ANIMATION_TYPE_DROP;
}
return type;
}
void GetTransformRelativeToRoot(ui::Layer* layer, gfx::Transform* transform) {
const ui::Layer* root = layer;
while (root->parent())
root = root->parent();
layer->GetTargetTransformRelativeTo(root, transform);
}
gfx::Rect GetLayerWorldBoundsAfterTransform(ui::Layer* layer,
const gfx::Transform& transform) {
gfx::Transform in_world = transform;
GetTransformRelativeToRoot(layer, &in_world);
gfx::RectF transformed = gfx::RectF(layer->bounds());
in_world.TransformRect(&transformed);
return gfx::ToEnclosingRect(transformed);
}
// Augment the host window so that the enclosing bounds of the full
// animation will fit inside of it.
void AugmentWindowSize(aura::Window* window,
const gfx::Transform& end_transform) {
AnimationHost* animation_host = GetAnimationHost(window);
if (!animation_host)
return;
const gfx::Rect& world_at_start = window->bounds();
gfx::Rect world_at_end =
GetLayerWorldBoundsAfterTransform(window->layer(), end_transform);
gfx::Rect union_in_window_space =
gfx::UnionRects(world_at_start, world_at_end);
// Calculate the top left and bottom right deltas to be added to the window
// bounds.
gfx::Vector2d top_left_delta(world_at_start.x() - union_in_window_space.x(),
world_at_start.y() - union_in_window_space.y());
gfx::Vector2d bottom_right_delta(
union_in_window_space.x() + union_in_window_space.width() -
(world_at_start.x() + world_at_start.width()),
union_in_window_space.y() + union_in_window_space.height() -
(world_at_start.y() + world_at_start.height()));
DCHECK(top_left_delta.x() >= 0 && top_left_delta.y() >= 0 &&
bottom_right_delta.x() >= 0 && bottom_right_delta.y() >= 0);
animation_host->SetHostTransitionOffsets(top_left_delta, bottom_right_delta);
}
// Shows a window using an animation, animating its opacity from 0.f to 1.f,
// its visibility to true, and its transform from |start_transform| to
// |end_transform|.
void AnimateShowWindowCommon(aura::Window* window,
const gfx::Transform& start_transform,
const gfx::Transform& end_transform) {
AugmentWindowSize(window, end_transform);
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
window->layer()->SetTransform(start_transform);
window->layer()->SetVisible(true);
{
// Property sets within this scope will be implicitly animated.
ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
base::TimeDelta duration = GetWindowVisibilityAnimationDuration(*window);
if (duration.is_positive())
settings.SetTransitionDuration(duration);
window->layer()->SetTransform(end_transform);
window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
}
}
// Hides a window using an animation, animating its opacity from 1.f to 0.f,
// its visibility to false, and its transform to |end_transform|.
void AnimateHideWindowCommon(aura::Window* window,
const gfx::Transform& end_transform) {
AugmentWindowSize(window, end_transform);
// Property sets within this scope will be implicitly animated.
ScopedHidingAnimationSettings hiding_settings(window);
// Report animation smoothness for animations created within this scope.
ui::AnimationThroughputReporter reporter(
hiding_settings.layer_animation_settings()->GetAnimator(),
ForSmoothness(base::BindRepeating(&ReportHideSmoothness)));
// Render surface caching may not provide a benefit when animating the opacity
// of a single layer.
if (!window->layer()->children().empty())
hiding_settings.layer_animation_settings()->CacheRenderSurface();
base::TimeDelta duration = GetWindowVisibilityAnimationDuration(*window);
if (duration.is_positive())
hiding_settings.layer_animation_settings()->SetTransitionDuration(duration);
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
window->layer()->SetTransform(end_transform);
window->layer()->SetVisible(false);
}
static gfx::Transform GetScaleForWindow(aura::Window* window) {
gfx::Rect bounds = window->bounds();
gfx::Transform scale = gfx::GetScaleTransform(
gfx::Point(kWindowAnimation_TranslateFactor * bounds.width(),
kWindowAnimation_TranslateFactor * bounds.height()),
kWindowAnimation_ScaleFactor);
return scale;
}
// Show/Hide windows using a shrink animation.
void AnimateShowWindow_Drop(aura::Window* window) {
AnimateShowWindowCommon(window, GetScaleForWindow(window), gfx::Transform());
}
void AnimateHideWindow_Drop(aura::Window* window) {
AnimateHideWindowCommon(window, GetScaleForWindow(window));
}
// Show/Hide windows using a vertical Glenimation.
void AnimateShowWindow_Vertical(aura::Window* window) {
gfx::Transform transform;
transform.Translate(0, window->GetProperty(
kWindowVisibilityAnimationVerticalPositionKey));
AnimateShowWindowCommon(window, transform, gfx::Transform());
}
void AnimateHideWindow_Vertical(aura::Window* window) {
gfx::Transform transform;
transform.Translate(0, window->GetProperty(
kWindowVisibilityAnimationVerticalPositionKey));
AnimateHideWindowCommon(window, transform);
}
// Show/Hide windows using a fade.
void AnimateShowWindow_Fade(aura::Window* window) {
AnimateShowWindowCommon(window, gfx::Transform(), gfx::Transform());
}
void AnimateHideWindow_Fade(aura::Window* window) {
AnimateHideWindowCommon(window, gfx::Transform());
}
std::unique_ptr<ui::LayerAnimationElement> CreateGrowShrinkElement(
aura::Window* window,
bool grow) {
std::unique_ptr<ui::InterpolatedTransform> scale =
std::make_unique<ui::InterpolatedScale>(
gfx::Point3F(kWindowAnimation_Bounce_Scale,
kWindowAnimation_Bounce_Scale, 1),
gfx::Point3F(1, 1, 1));
std::unique_ptr<ui::InterpolatedTransform> scale_about_pivot =
std::make_unique<ui::InterpolatedTransformAboutPivot>(
gfx::Point(window->bounds().width() * 0.5,
window->bounds().height() * 0.5),
std::move(scale));
scale_about_pivot->SetReversed(grow);
std::unique_ptr<ui::LayerAnimationElement> transition =
ui::LayerAnimationElement::CreateInterpolatedTransformElement(
std::move(scale_about_pivot),
base::Milliseconds(kWindowAnimation_Bounce_DurationMS *
kWindowAnimation_Bounce_GrowShrinkDurationPercent /
100));
transition->set_tween_type(grow ? gfx::Tween::EASE_OUT : gfx::Tween::EASE_IN);
return transition;
}
void AnimateBounce(aura::Window* window) {
ui::ScopedLayerAnimationSettings scoped_settings(
window->layer()->GetAnimator());
scoped_settings.SetPreemptionStrategy(
ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
std::unique_ptr<ui::LayerAnimationSequence> sequence =
std::make_unique<ui::LayerAnimationSequence>();
sequence->AddElement(CreateGrowShrinkElement(window, true));
sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement(
ui::LayerAnimationElement::BOUNDS,
base::Milliseconds(
kWindowAnimation_Bounce_DurationMS *
(100 - 2 * kWindowAnimation_Bounce_GrowShrinkDurationPercent) /
100)));
sequence->AddElement(CreateGrowShrinkElement(window, false));
window->layer()->GetAnimator()->StartAnimation(sequence.release());
}
// A HidingWindowAnimationObserver that deletes observer and detached
// layers when the last_sequence has been completed or aborted.
class RotateHidingWindowAnimationObserver
: public HidingWindowAnimationObserverBase,
public ui::LayerAnimationObserver {
public:
explicit RotateHidingWindowAnimationObserver(aura::Window* window)
: HidingWindowAnimationObserverBase(window) {}
RotateHidingWindowAnimationObserver(
const RotateHidingWindowAnimationObserver&) = delete;
RotateHidingWindowAnimationObserver& operator=(
const RotateHidingWindowAnimationObserver&) = delete;
~RotateHidingWindowAnimationObserver() override {}
// Destroys itself after |last_sequence| ends or is aborted. Does not take
// ownership of |last_sequence|, which should not be nullptr.
void SetLastSequence(ui::LayerAnimationSequence* last_sequence) {
last_sequence->AddObserver(this);
}
// ui::LayerAnimationObserver:
void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {
OnAnimationCompleted();
}
void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {
OnAnimationCompleted();
}
void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) override {}
};
void AddLayerAnimationsForRotate(aura::Window* window, bool show) {
if (show)
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
base::TimeDelta duration =
base::Milliseconds(kWindowAnimation_Rotate_DurationMS);
if (!show) {
window->layer()->GetAnimator()->SchedulePauseForProperties(
duration * (100 - kWindowAnimation_Rotate_OpacityDurationPercent) / 100,
ui::LayerAnimationElement::OPACITY);
}
std::unique_ptr<ui::LayerAnimationElement> opacity =
ui::LayerAnimationElement::CreateOpacityElement(
show ? kWindowAnimation_ShowOpacity : kWindowAnimation_HideOpacity,
duration * kWindowAnimation_Rotate_OpacityDurationPercent / 100);
opacity->set_tween_type(gfx::Tween::EASE_IN_OUT);
window->layer()->GetAnimator()->ScheduleAnimation(
new ui::LayerAnimationSequence(std::move(opacity)));
float xcenter = window->bounds().width() * 0.5;
gfx::Transform transform;
transform.Translate(xcenter, 0);
transform.ApplyPerspectiveDepth(kWindowAnimation_Rotate_PerspectiveDepth);
transform.Translate(-xcenter, 0);
std::unique_ptr<ui::InterpolatedTransform> perspective =
std::make_unique<ui::InterpolatedConstantTransform>(transform);
std::unique_ptr<ui::InterpolatedTransform> scale =
std::make_unique<ui::InterpolatedScale>(
1, kWindowAnimation_Rotate_ScaleFactor);
std::unique_ptr<ui::InterpolatedTransform> scale_about_pivot =
std::make_unique<ui::InterpolatedTransformAboutPivot>(
gfx::Point(xcenter, kWindowAnimation_Rotate_TranslateY),
std::move(scale));
std::unique_ptr<ui::InterpolatedTransform> translation =
std::make_unique<ui::InterpolatedTranslation>(
gfx::PointF(), gfx::PointF(0, kWindowAnimation_Rotate_TranslateY));
std::unique_ptr<ui::InterpolatedTransform> rotation =
std::make_unique<ui::InterpolatedAxisAngleRotation>(
gfx::Vector3dF(1, 0, 0), 0, kWindowAnimation_Rotate_DegreesX);
scale_about_pivot->SetChild(std::move(perspective));
translation->SetChild(std::move(scale_about_pivot));
rotation->SetChild(std::move(translation));
rotation->SetReversed(show);
std::unique_ptr<ui::LayerAnimationElement> transition =
ui::LayerAnimationElement::CreateInterpolatedTransformElement(
std::move(rotation), duration);
ui::LayerAnimationSequence* last_sequence =
new ui::LayerAnimationSequence(std::move(transition));
auto weak_last_sequence = last_sequence->AsWeakPtr();
window->layer()->GetAnimator()->ScheduleAnimation(last_sequence);
// If the animation is immediate, then |last_sequence| will have been
// deleted.
last_sequence = nullptr;
if (!show && weak_last_sequence) {
// RotateHidingWindowAnimationObserver deletes itself when no longer
// needed.
auto* observer = new RotateHidingWindowAnimationObserver(window);
observer->SetLastSequence(weak_last_sequence.get());
observer->DetachAndRecreateLayers();
}
window->layer()->SetVisible(show);
}
void AnimateShowWindow_Rotate(aura::Window* window) {
AddLayerAnimationsForRotate(window, true);
}
void AnimateHideWindow_Rotate(aura::Window* window) {
AddLayerAnimationsForRotate(window, false);
}
bool AnimateShowWindow(aura::Window* window) {
if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) {
if (HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) {
// Since hide animation may have changed opacity and transform,
// reset them to show the window.
window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
window->layer()->SetTransform(gfx::Transform());
}
return false;
}
switch (GetWindowVisibilityAnimationType(window)) {
case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP:
AnimateShowWindow_Drop(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL:
AnimateShowWindow_Vertical(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE:
AnimateShowWindow_Fade(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE:
AnimateShowWindow_Rotate(window);
return true;
default:
return false;
}
}
bool AnimateHideWindow(aura::Window* window) {
if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) {
if (HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) {
// Since show animation may have changed opacity and transform,
// reset them, though the change should be hidden.
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
window->layer()->SetTransform(gfx::Transform());
}
return false;
}
switch (GetWindowVisibilityAnimationType(window)) {
case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP:
AnimateHideWindow_Drop(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL:
AnimateHideWindow_Vertical(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE:
AnimateHideWindow_Fade(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE:
AnimateHideWindow_Rotate(window);
return true;
default:
return false;
}
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// ImplicitHidingWindowAnimationObserver
ImplicitHidingWindowAnimationObserver::ImplicitHidingWindowAnimationObserver(
aura::Window* window,
ui::ScopedLayerAnimationSettings* settings)
: HidingWindowAnimationObserverBase(window) {
settings->AddObserver(this);
}
void ImplicitHidingWindowAnimationObserver::OnImplicitAnimationsCompleted() {
OnAnimationCompleted();
}
////////////////////////////////////////////////////////////////////////////////
// ScopedHidingAnimationSettings
ScopedHidingAnimationSettings::ScopedHidingAnimationSettings(
aura::Window* window)
: layer_animation_settings_(window->layer()->GetAnimator()),
observer_(new ImplicitHidingWindowAnimationObserver(
window,
&layer_animation_settings_)) {
}
ScopedHidingAnimationSettings::~ScopedHidingAnimationSettings() {
observer_->DetachAndRecreateLayers();
}
////////////////////////////////////////////////////////////////////////////////
// External interface
void SetWindowVisibilityAnimationType(aura::Window* window, int type) {
window->SetProperty(kWindowVisibilityAnimationTypeKey, type);
}
int GetWindowVisibilityAnimationType(aura::Window* window) {
return window->GetProperty(kWindowVisibilityAnimationTypeKey);
}
void SetWindowVisibilityAnimationTransition(
aura::Window* window,
WindowVisibilityAnimationTransition transition) {
window->SetProperty(kWindowVisibilityAnimationTransitionKey, transition);
}
bool HasWindowVisibilityAnimationTransition(
aura::Window* window,
WindowVisibilityAnimationTransition transition) {
WindowVisibilityAnimationTransition prop = window->GetProperty(
kWindowVisibilityAnimationTransitionKey);
return (prop & transition) != 0;
}
void SetWindowVisibilityAnimationDuration(aura::Window* window,
const base::TimeDelta& duration) {
window->SetProperty(kWindowVisibilityAnimationDurationKey, duration);
}
base::TimeDelta GetWindowVisibilityAnimationDuration(
const aura::Window& window) {
return window.GetProperty(kWindowVisibilityAnimationDurationKey);
}
void SetWindowVisibilityAnimationVerticalPosition(aura::Window* window,
float position) {
window->SetProperty(kWindowVisibilityAnimationVerticalPositionKey, position);
}
bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) {
if (WindowAnimationsDisabled(window))
return false;
if (visible)
return AnimateShowWindow(window);
// Don't start hiding the window again if it's already being hidden.
return window->layer()->GetTargetOpacity() != 0.0f &&
AnimateHideWindow(window);
}
bool AnimateWindow(aura::Window* window, WindowAnimationType type) {
switch (type) {
case WINDOW_ANIMATION_TYPE_BOUNCE:
AnimateBounce(window);
return true;
default:
NOTREACHED();
return false;
}
}
bool WindowAnimationsDisabled(aura::Window* window) {
// WARNING: this function is called from VisibilityController to determine
// if an animation should happen when the Window's visibility changes.
// Returning false results in VisibilityController applying default
// handling of the transition. This can result in dramatically different
// results than if an animation occurs. For example, VisibilityController
// doesn't change the opacity, yet many of the animations do. Similarly,
// ash's animations may change the bounds, which VisibilityController won't
// do. Take care when adding a new path that returns false.
// Individual windows can choose to skip animations.
if (window && window->GetProperty(aura::client::kAnimationsDisabledKey))
return true;
// Animations can be disabled globally for testing.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWindowAnimationsDisabled))
return true;
// Tests of animations themselves should still run even if the machine is
// being accessed via Remote Desktop.
if (ui::ScopedAnimationDurationScaleMode::duration_multiplier() ==
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION)
return false;
// Let the user decide whether or not to play the animation.
return !gfx::Animation::ShouldRenderRichAnimation();
}
} // namespace wm