#include "ash/wm/window_animations.h"
#include <math.h>
#include <algorithm>
#include <optional>
#include <utility>
#include <vector>
#include "ash/public/cpp/metrics_util.h"
#include "ash/public/cpp/window_animation_types.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/wm/pip/pip_positioner.h"
#include "ash/wm/resize_shadow_controller.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace_controller.h"
#include "base/check.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/functional/bind.h"
#include "base/i18n/rtl.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/scoped_observation.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/base/class_property.h"
#include "ui/compositor/animation_throughput_reporter.h"
#include "ui/compositor/compositor.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_observer.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/geometry/transform_util.h"
#include "ui/gfx/interpolated_transform.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/shadow_types.h"
#include "ui/wm/core/window_animations.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
const int kLayerAnimationsForMinimizeDurationMS = 200;
constexpr base::TimeDelta kCrossFadeDuration = base::Milliseconds(200);
constexpr base::TimeDelta kCrossFadeMaxDuration = base::Milliseconds(400);
static constexpr base::TimeDelta kFloatUnfloatDuration =
base::Milliseconds(400);
const int kBrightnessGrayscaleFadeDurationMs = 1000;
const int kFadeInAnimationMs = 200;
const float kWindowAnimation_HideBrightnessGrayscale = 1.f;
const float kWindowAnimation_ShowBrightnessGrayscale = 0.f;
const float kWindowAnimation_HideOpacity = 0.f;
const float kWindowAnimation_ShowOpacity = 1.f;
constexpr base::TimeDelta kZeroAnimationMs = base::Milliseconds(300);
constexpr char kCrossFadeSmoothness[] =
"Ash.Window.AnimationSmoothness.CrossFade";
base::TimeDelta GetCrossFadeDuration(aura::Window* window,
const gfx::RectF& old_bounds,
const gfx::Rect& new_bounds) {
if (::wm::WindowAnimationsDisabled(window))
return base::TimeDelta();
int old_area = static_cast<int>(old_bounds.width() * old_bounds.height());
int new_area = new_bounds.width() * new_bounds.height();
int max_area = std::max(old_area, new_area);
if (max_area == 0)
return kCrossFadeDuration;
int delta_area = std::abs(old_area - new_area);
if (delta_area == 0)
return kCrossFadeDuration;
float factor = static_cast<float>(delta_area) / static_cast<float>(max_area);
const auto kRange = kCrossFadeMaxDuration - kCrossFadeDuration;
return kCrossFadeDuration + factor * kRange;
}
class LayerDeletionDumper : public ui::LayerObserver {
public:
explicit LayerDeletionDumper(ui::Layer* layer) {
observation_.Observe(layer);
}
LayerDeletionDumper(const LayerDeletionDumper&) = delete;
LayerDeletionDumper& operator=(const LayerDeletionDumper&) = delete;
~LayerDeletionDumper() override = default;
void LayerDestroyed(ui::Layer* layer) override {
observation_.Reset();
base::debug::DumpWithoutCrashing();
}
private:
base::ScopedObservation<ui::Layer, ui::LayerObserver> observation_{this};
};
class CrossFadeObserver : public aura::WindowObserver,
public ui::ImplicitAnimationObserver {
public:
CrossFadeObserver(aura::Window* window,
std::unique_ptr<ui::LayerTreeOwner> layer_owner,
std::optional<std::string> histogram_name)
: window_(window),
layer_(window->layer()),
layer_owner_(std::move(layer_owner)) {
window_->AddObserver(this);
smoothness_tracker_ =
layer_->GetCompositor()->RequestNewCompositorMetricsTracker();
smoothness_tracker_->Start(
metrics_util::ForSmoothnessV3(base::BindRepeating(
[](const std::optional<std::string>& histogram_name,
int smoothness) {
if (histogram_name) {
DCHECK(!histogram_name->empty());
base::UmaHistogramPercentage(*histogram_name, smoothness);
} else {
UMA_HISTOGRAM_PERCENTAGE(kCrossFadeSmoothness, smoothness);
}
},
std::move(histogram_name))));
}
CrossFadeObserver(const CrossFadeObserver&) = delete;
CrossFadeObserver& operator=(const CrossFadeObserver&) = delete;
~CrossFadeObserver() override {
smoothness_tracker_->Stop();
smoothness_tracker_.reset();
layer_owner_->root()->GetAnimator()->StopAnimating();
window_->RemoveObserver(this);
window_ = nullptr;
layer_ = nullptr;
}
void OnWindowDestroying(aura::Window* window) override { StopAnimating(); }
void OnWindowRemovingFromRootWindow(aura::Window* window,
aura::Window* new_root) override {
StopAnimating();
}
void OnWindowLayerRecreated(aura::Window* window) override {
layer_->GetAnimator()->StopAnimating();
}
void OnImplicitAnimationsCompleted() override {
if (auto* resize_shadow_controller =
Shell::Get()->resize_shadow_controller()) {
resize_shadow_controller->OnCrossFadeAnimationCompleted(window_);
}
delete this;
}
protected:
void StopAnimating() {
DCHECK(window_);
window_->layer()->GetAnimator()->StopAnimating();
}
raw_ptr<aura::Window> window_;
raw_ptr<ui::Layer> layer_;
std::unique_ptr<ui::LayerTreeOwner> layer_owner_;
std::optional<ui::ThroughputTracker> smoothness_tracker_;
};
class CrossFadeUpdateTransformObserver
: public CrossFadeObserver,
public ui::CompositorAnimationObserver {
public:
CrossFadeUpdateTransformObserver(
aura::Window* window,
std::unique_ptr<ui::LayerTreeOwner> layer_owner,
std::optional<std::string> histogram_name)
: CrossFadeObserver(window, std::move(layer_owner), histogram_name) {
compositor_ = window->layer()->GetCompositor();
compositor_->AddAnimationObserver(this);
}
CrossFadeUpdateTransformObserver(const CrossFadeUpdateTransformObserver&) =
delete;
CrossFadeUpdateTransformObserver& operator=(
const CrossFadeUpdateTransformObserver&) = delete;
~CrossFadeUpdateTransformObserver() override {
compositor_->RemoveAnimationObserver(this);
}
void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override {
DCHECK(!layer_owner_->root()->GetAnimator()->IsAnimatingProperty(
ui::LayerAnimationElement::TRANSFORM));
CrossFadeObserver::OnLayerAnimationStarted(sequence);
}
void OnAnimationStep(base::TimeTicks timestamp) override {
DCHECK(compositor_);
DCHECK(window_);
gfx::RectF old_bounds(layer_owner_->root()->bounds());
gfx::RectF new_bounds(window_->bounds());
gfx::Transform new_transform = window_->transform();
DCHECK(new_transform.IsScaleOrTranslation());
gfx::RectF effective_bounds =
new_transform.MapRect(gfx::RectF(new_bounds.size()));
effective_bounds.Offset(new_bounds.OffsetFromOrigin());
const gfx::Transform old_transform =
gfx::TransformBetweenRects(old_bounds, effective_bounds);
layer_owner_->root()->SetTransform(old_transform);
}
void OnCompositingShuttingDown(ui::Compositor* compositor) override {
DCHECK_EQ(compositor_, compositor);
StopAnimating();
}
private:
raw_ptr<ui::Compositor> compositor_ = nullptr;
};
void CrossFadeAnimationInternal(
aura::Window* window,
std::unique_ptr<ui::LayerTreeOwner> old_layer_owner,
bool animate_old_layer_transform,
std::optional<base::TimeDelta> duration,
std::optional<gfx::Tween::Type> tween_type,
std::optional<std::string> histogram_name) {
ui::Layer* old_layer = old_layer_owner->root();
auto new_layer = [window]() -> ui::Layer* { return window->layer(); };
DCHECK(old_layer);
const gfx::Rect old_bounds(old_layer->bounds());
gfx::Transform old_transform(old_layer->transform());
gfx::Transform old_transform_in_root;
old_transform_in_root.Translate(old_bounds.x(), old_bounds.y());
old_transform_in_root.PreConcat(old_transform);
old_transform_in_root.Translate(-old_bounds.x(), -old_bounds.y());
gfx::RectF old_transformed_bounds =
old_transform_in_root.MapRect(gfx::RectF(old_bounds));
const gfx::Rect new_bounds(window->bounds());
const bool old_on_top = (old_bounds.width() > new_bounds.width());
SCOPED_CRASH_KEY_BOOL("333095196", "animate_old_layer_transform",
animate_old_layer_transform);
SCOPED_CRASH_KEY_BOOL("333095196", "old_on_top", old_on_top);
SCOPED_CRASH_KEY_STRING256("333095196", "window_title",
base::UTF16ToUTF8(window->GetTitle()));
SCOPED_CRASH_KEY_STRING256("333095196", "new_layer_begin",
base::StringPrintf("%p", new_layer()));
if (old_on_top)
old_layer->parent()->StackBelow(new_layer(), old_layer);
else
old_layer->parent()->StackAbove(new_layer(), old_layer);
const base::TimeDelta animation_duration = duration.value_or(
GetCrossFadeDuration(window, old_transformed_bounds, new_bounds));
const gfx::Tween::Type animation_tween_type =
tween_type.value_or(gfx::Tween::EASE_OUT);
{
old_layer->GetAnimator()->StopAnimating();
old_layer->SetTransform(old_transform);
ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
settings.SetTransitionDuration(animation_duration);
settings.SetTweenType(animation_tween_type);
settings.DeferPaint();
if (old_on_top) {
if (!old_layer->children().empty())
settings.CacheRenderSurface();
old_layer->SetOpacity(kWindowAnimation_HideOpacity);
} else if (!animate_old_layer_transform) {
settings.SetTweenType(gfx::Tween::ZERO);
old_layer->SetOpacity(kWindowAnimation_HideOpacity);
}
if (animate_old_layer_transform) {
gfx::Transform out_transform;
float scale_x =
new_bounds.width() / static_cast<float>(old_bounds.width());
float scale_y =
new_bounds.height() / static_cast<float>(old_bounds.height());
out_transform.Translate(new_bounds.x() - old_bounds.x(),
new_bounds.y() - old_bounds.y());
out_transform.Scale(scale_x, scale_y);
old_layer->SetTransform(out_transform);
}
old_layer = nullptr;
}
SCOPED_CRASH_KEY_STRING256("333095196", "new_layer_mid",
base::StringPrintf("%p", new_layer()));
LayerDeletionDumper deletion_dumper(new_layer());
gfx::Transform in_transform;
const float scale_x =
old_transformed_bounds.width() / static_cast<float>(new_bounds.width());
const float scale_y =
old_transformed_bounds.height() / static_cast<float>(new_bounds.height());
in_transform.Translate(old_transformed_bounds.x() - new_bounds.x(),
old_transformed_bounds.y() - new_bounds.y());
in_transform.Scale(scale_x, scale_y);
new_layer()->SetTransform(in_transform);
if (!old_on_top) {
new_layer()->SetOpacity(kWindowAnimation_HideOpacity);
}
{
CrossFadeObserver* observer =
animate_old_layer_transform
? new CrossFadeObserver(window, std::move(old_layer_owner),
histogram_name)
: new CrossFadeUpdateTransformObserver(
window, std::move(old_layer_owner), histogram_name);
ui::ScopedLayerAnimationSettings settings(new_layer()->GetAnimator());
settings.AddObserver(observer);
settings.SetTransitionDuration(animation_duration);
settings.SetTweenType(animation_tween_type);
settings.DeferPaint();
if (!old_on_top) {
if (!new_layer()->children().empty()) {
settings.CacheRenderSurface();
}
new_layer()->SetOpacity(kWindowAnimation_ShowOpacity);
}
SCOPED_CRASH_KEY_NUMBER("333095196", "animation_duration_ms",
animation_duration.InMillisecondsF());
SCOPED_CRASH_KEY_BOOL("333095196", "is_destroying",
window->is_destroying());
SCOPED_CRASH_KEY_STRING256("333095196", "new_layer_end",
base::StringPrintf("%p", new_layer()));
new_layer()->SetTransform(gfx::Transform());
}
}
}
void SetTransformForScaleAnimation(ui::Layer* layer,
LayerScaleAnimationDirection type) {
constexpr float kLayerScaleAboveSize = 1.1f;
constexpr float kLayerScaleBelowSize = .9f;
const float scale = type == LAYER_SCALE_ANIMATION_ABOVE
? kLayerScaleAboveSize
: kLayerScaleBelowSize;
gfx::Transform transform;
transform.Translate(-layer->bounds().width() * (scale - 1.0f) / 2,
-layer->bounds().height() * (scale - 1.0f) / 2);
transform.Scale(scale, scale);
layer->SetTransform(transform);
}
void AddLayerAnimationsForMinimize(aura::Window* window, bool show) {
gfx::Rect bounds = window->bounds();
gfx::Rect target_bounds = GetMinimizeAnimationTargetBoundsInScreen(window);
::wm::ConvertRectFromScreen(window->parent(), &target_bounds);
float scale_x = static_cast<float>(target_bounds.width()) / bounds.width();
float scale_y = static_cast<float>(target_bounds.height()) / bounds.height();
std::unique_ptr<ui::InterpolatedTransform> scale =
std::make_unique<ui::InterpolatedScale>(
gfx::Point3F(1, 1, 1), gfx::Point3F(scale_x, scale_y, 1));
std::unique_ptr<ui::InterpolatedTransform> translation =
std::make_unique<ui::InterpolatedTranslation>(
gfx::PointF(), gfx::PointF(target_bounds.x() - bounds.x(),
target_bounds.y() - bounds.y()));
scale->SetChild(std::move(translation));
scale->SetReversed(show);
base::TimeDelta duration =
window->layer()->GetAnimator()->GetTransitionDuration();
std::unique_ptr<ui::LayerAnimationElement> transition =
ui::LayerAnimationElement::CreateInterpolatedTransformElement(
std::move(scale), duration);
transition->set_tween_type(show ? gfx::Tween::EASE_IN
: gfx::Tween::EASE_IN_OUT);
window->layer()->GetAnimator()->ScheduleAnimation(
new ui::LayerAnimationSequence(std::move(transition)));
if (!show) {
window->layer()->GetAnimator()->SchedulePauseForProperties(
(duration * 3) / 4, ui::LayerAnimationElement::OPACITY);
}
float opacity = show ? 1.0f : 0.0f;
window->layer()->GetAnimator()->ScheduleAnimation(
new ui::LayerAnimationSequence(
ui::LayerAnimationElement::CreateOpacityElement(opacity,
duration / 4)));
window->layer()->GetAnimator()->ScheduleAnimation(
new ui::LayerAnimationSequence(
ui::LayerAnimationElement::CreateTransformElement(
gfx::Transform(), base::TimeDelta())));
}
void AnimateShowWindow_Minimize(aura::Window* window) {
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
ui::AnimationThroughputReporter reporter(
settings.GetAnimator(),
metrics_util::ForSmoothnessV3(
base::BindRepeating(static_cast<void (*)(std::string_view, int)>(
&base::UmaHistogramPercentage),
"Ash.Window.AnimationSmoothness.Unminimize")));
base::TimeDelta duration =
base::Milliseconds(kLayerAnimationsForMinimizeDurationMS);
settings.SetTransitionDuration(duration);
AddLayerAnimationsForMinimize(window, true);
::wm::SetWindowVisibilityAnimationType(
window, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
}
void AnimateHideWindow_Minimize(aura::Window* window) {
::wm::ScopedHidingAnimationSettings hiding_settings(window);
ui::AnimationThroughputReporter reporter(
hiding_settings.layer_animation_settings()->GetAnimator(),
metrics_util::ForSmoothnessV3(
base::BindRepeating(static_cast<void (*)(std::string_view, int)>(
&base::UmaHistogramPercentage),
"Ash.Window.AnimationSmoothness.Minimize")));
base::TimeDelta duration =
base::Milliseconds(kLayerAnimationsForMinimizeDurationMS);
hiding_settings.layer_animation_settings()->SetTransitionDuration(duration);
window->layer()->SetVisible(false);
AddLayerAnimationsForMinimize(window, false);
}
void AnimateShowHideWindowCommon_BrightnessGrayscale(aura::Window* window,
bool show) {
float start_value, end_value;
if (show) {
start_value = kWindowAnimation_HideBrightnessGrayscale;
end_value = kWindowAnimation_ShowBrightnessGrayscale;
} else {
start_value = kWindowAnimation_ShowBrightnessGrayscale;
end_value = kWindowAnimation_HideBrightnessGrayscale;
}
window->layer()->SetLayerBrightness(start_value);
window->layer()->SetLayerGrayscale(start_value);
if (show) {
window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
window->layer()->SetVisible(true);
}
base::TimeDelta duration =
base::Milliseconds(kBrightnessGrayscaleFadeDurationMs);
if (show) {
ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
window->layer()->GetAnimator()->ScheduleTogether(
CreateBrightnessGrayscaleAnimationSequence(end_value, duration));
} else {
::wm::ScopedHidingAnimationSettings hiding_settings(window);
window->layer()->GetAnimator()->ScheduleTogether(
CreateBrightnessGrayscaleAnimationSequence(end_value, duration));
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
window->layer()->SetVisible(false);
}
}
void AnimateShowWindow_BrightnessGrayscale(aura::Window* window) {
AnimateShowHideWindowCommon_BrightnessGrayscale(window, true);
}
void AnimateShowWindow_FadeIn(aura::Window* window) {
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
settings.SetTransitionDuration(base::Milliseconds(kFadeInAnimationMs));
window->layer()->SetVisible(true);
window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
}
void AnimateHideWindow_BrightnessGrayscale(aura::Window* window) {
AnimateShowHideWindowCommon_BrightnessGrayscale(window, false);
}
void AnimateHideWindow_SlideOut(aura::Window* window) {
base::TimeDelta duration =
base::Milliseconds(PipPositioner::kPipDismissTimeMs);
::wm::ScopedHidingAnimationSettings settings(window);
settings.layer_animation_settings()->SetTransitionDuration(duration);
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
window->layer()->SetVisible(false);
gfx::Rect bounds = window->GetBoundsInScreen();
display::Display display =
display::Screen::Get()->GetDisplayNearestWindow(window);
gfx::Rect dismissed_bounds =
PipPositioner::GetDismissedPosition(display, bounds);
::wm::ConvertRectFromScreen(window->parent(), &dismissed_bounds);
window->layer()->SetBounds(dismissed_bounds);
}
void AnimateShowWindow_StepEnd(aura::Window* window) {
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
settings.SetTransitionDuration(kZeroAnimationMs);
settings.SetTweenType(gfx::Tween::ZERO);
window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
}
void AnimateHideWindow_StepEnd(aura::Window* window) {
::wm::ScopedHidingAnimationSettings settings(window);
settings.layer_animation_settings()->SetTransitionDuration(kZeroAnimationMs);
settings.layer_animation_settings()->SetTweenType(gfx::Tween::ZERO);
window->layer()->SetVisible(false);
}
bool AnimateShowWindow(aura::Window* window) {
if (!::wm::HasWindowVisibilityAnimationTransition(window,
::wm::ANIMATE_SHOW)) {
return false;
}
switch (::wm::GetWindowVisibilityAnimationType(window)) {
case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
AnimateShowWindow_Minimize(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
AnimateShowWindow_BrightnessGrayscale(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE_IN_SLIDE_OUT:
AnimateShowWindow_FadeIn(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_STEP_END:
AnimateShowWindow_StepEnd(window);
return true;
default:
NOTREACHED();
}
}
bool AnimateHideWindow(aura::Window* window) {
if (!::wm::HasWindowVisibilityAnimationTransition(window,
::wm::ANIMATE_HIDE)) {
return false;
}
switch (::wm::GetWindowVisibilityAnimationType(window)) {
case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
AnimateHideWindow_Minimize(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
AnimateHideWindow_BrightnessGrayscale(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE_IN_SLIDE_OUT:
AnimateHideWindow_SlideOut(window);
return true;
case WINDOW_VISIBILITY_ANIMATION_TYPE_STEP_END:
AnimateHideWindow_StepEnd(window);
return true;
default:
NOTREACHED();
}
}
void CrossFadeAnimation(aura::Window* window,
std::unique_ptr<ui::LayerTreeOwner> old_layer_owner) {
CrossFadeAnimationInternal(
window, std::move(old_layer_owner), true,
std::nullopt, std::nullopt,
std::nullopt);
}
void CrossFadeAnimationForFloatUnfloat(
aura::Window* window,
std::unique_ptr<ui::LayerTreeOwner> old_layer_owner,
bool to_float) {
CrossFadeAnimationInternal(window, std::move(old_layer_owner),
true,
kFloatUnfloatDuration,
to_float ? gfx::Tween::Type::ACCEL_30_DECEL_20_85
: gfx::Tween::Type::FAST_OUT_SLOW_IN_3,
std::nullopt);
}
void CrossFadeAnimationAnimateNewLayerOnly(aura::Window* window,
const gfx::Rect& target_bounds,
base::TimeDelta duration,
gfx::Tween::Type tween_type,
const std::string& histogram_name) {
std::unique_ptr<ui::LayerTreeOwner> old_layer_owner =
::wm::RecreateLayers(window);
window->SetBounds(target_bounds);
CrossFadeAnimationInternal(window, std::move(old_layer_owner),
false, duration, tween_type,
histogram_name);
}
bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) {
if (::wm::WindowAnimationsDisabled(window))
return false;
if (::wm::AnimateOnChildWindowVisibilityChanged(window, visible))
return true;
if (visible)
return AnimateShowWindow(window);
return window->layer()->GetTargetOpacity() != 0.0f &&
AnimateHideWindow(window);
}
std::vector<ui::LayerAnimationSequence*>
CreateBrightnessGrayscaleAnimationSequence(float target_value,
base::TimeDelta duration) {
gfx::Tween::Type animation_type = gfx::Tween::EASE_OUT;
std::unique_ptr<ui::LayerAnimationSequence> brightness_sequence =
std::make_unique<ui::LayerAnimationSequence>();
std::unique_ptr<ui::LayerAnimationSequence> grayscale_sequence =
std::make_unique<ui::LayerAnimationSequence>();
std::unique_ptr<ui::LayerAnimationElement> brightness_element =
ui::LayerAnimationElement::CreateBrightnessElement(target_value,
duration);
brightness_element->set_tween_type(animation_type);
brightness_sequence->AddElement(std::move(brightness_element));
std::unique_ptr<ui::LayerAnimationElement> grayscale_element =
ui::LayerAnimationElement::CreateGrayscaleElement(target_value, duration);
grayscale_element->set_tween_type(animation_type);
grayscale_sequence->AddElement(std::move(grayscale_element));
std::vector<ui::LayerAnimationSequence*> animations;
animations.push_back(brightness_sequence.release());
animations.push_back(grayscale_sequence.release());
return animations;
}
gfx::Rect GetMinimizeAnimationTargetBoundsInScreen(aura::Window* window) {
Shelf* shelf = Shelf::ForWindow(window);
gfx::Rect item_rect = shelf->GetScreenBoundsOfItemIconForWindow(window);
if (!item_rect.IsEmpty())
return item_rect;
if (item_rect.width() != 0 || item_rect.height() != 0) {
if (shelf->GetVisibilityState() == SHELF_AUTO_HIDE) {
gfx::Rect shelf_bounds = shelf->GetWindow()->GetBoundsInScreen();
if (shelf->alignment() == ShelfAlignment::kLeft)
item_rect.set_x(shelf_bounds.right());
else if (shelf->alignment() == ShelfAlignment::kRight)
item_rect.set_x(shelf_bounds.x());
else
item_rect.set_y(shelf_bounds.y());
return item_rect;
}
}
gfx::Rect work_area =
display::Screen::Get()->GetDisplayNearestWindow(window).work_area();
int ltr_adjusted_x = base::i18n::IsRTL() ? work_area.right() : work_area.x();
switch (shelf->alignment()) {
case ShelfAlignment::kBottom:
case ShelfAlignment::kBottomLocked:
return gfx::Rect(ltr_adjusted_x, work_area.bottom(), 0, 0);
case ShelfAlignment::kLeft:
return gfx::Rect(work_area.x(), work_area.y(), 0, 0);
case ShelfAlignment::kRight:
return gfx::Rect(work_area.right(), work_area.y(), 0, 0);
}
NOTREACHED();
}
void BounceWindow(aura::Window* window) {
wm::AnimateWindow(window, wm::WINDOW_ANIMATION_TYPE_BOUNCE);
}
}