// 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/window_animations.h" #include #include #include #include #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/window_util.h" #include "ash/wm/workspace_controller.h" #include "base/bind.h" #include "base/check.h" #include "base/i18n/rtl.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/notreached.h" #include "base/time/time.h" #include "third_party/abseil-cpp/absl/types/optional.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_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/interpolated_transform.h" #include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/core/window_util.h" namespace ash { namespace { const int kLayerAnimationsForMinimizeDurationMS = 200; // Amount of time for the cross fade animation. constexpr base::TimeDelta kCrossFadeDuration = base::Milliseconds(200); constexpr base::TimeDelta kCrossFadeMaxDuration = base::Milliseconds(400); // Durations for the brightness/grayscale fade animation, in milliseconds. const int kBrightnessGrayscaleFadeDurationMs = 1000; // Duration for fade in animation, in milliseconds. const int kFadeInAnimationMs = 200; // Brightness/grayscale values for hide/show window animations. const float kWindowAnimation_HideBrightnessGrayscale = 1.f; const float kWindowAnimation_ShowBrightnessGrayscale = 0.f; const float kWindowAnimation_HideOpacity = 0.f; const float kWindowAnimation_ShowOpacity = 1.f; // Duration for gfx::Tween::ZERO animation of showing window. 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(old_bounds.width() * old_bounds.height()); int new_area = new_bounds.width() * new_bounds.height(); int max_area = std::max(old_area, new_area); // Avoid divide by zero. if (max_area == 0) return kCrossFadeDuration; int delta_area = std::abs(old_area - new_area); // If the area didn't change, the animation is instantaneous. if (delta_area == 0) return kCrossFadeDuration; float factor = static_cast(delta_area) / static_cast(max_area); const auto kRange = kCrossFadeMaxDuration - kCrossFadeDuration; return kCrossFadeDuration + factor * kRange; } // Observer for a window cross-fade animation. If either the window closes or // the layer's animation completes, it deletes the layer and removes itself as // an observer. class CrossFadeObserver : public aura::WindowObserver, public ui::ImplicitAnimationObserver { public: // Observes |window| for destruction, but does not take ownership. // Takes ownership of |layer_owner| and its child layers. CrossFadeObserver(aura::Window* window, std::unique_ptr layer_owner, absl::optional histogram_name) : window_(window), layer_(window->layer()), layer_owner_(std::move(layer_owner)) { window_->AddObserver(this); smoothness_tracker_ = layer_->GetCompositor()->RequestNewThroughputTracker(); smoothness_tracker_->Start(metrics_util::ForSmoothness(base::BindRepeating( [](const absl::optional& 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(); // Stop the old animator to trigger aborts or ends on any observers it may // have. layer_owner_->root()->GetAnimator()->StopAnimating(); window_->RemoveObserver(this); window_ = nullptr; layer_ = nullptr; } // aura::WindowObserver: void OnWindowDestroying(aura::Window* window) override { StopAnimating(); } void OnWindowRemovingFromRootWindow(aura::Window* window, aura::Window* new_root) override { StopAnimating(); } void OnWindowLayerRecreated(aura::Window* window) override { // If the window layer is recreated. |layer_| will hold the LayerAnimator we // were originally observing. Layer recreation is usually done when doing // another window animation, so stop this current one and trigger // OnImplicitAnimationsCompleted to delete ourselves. layer_->GetAnimator()->StopAnimating(); } // ui::ImplicitAnimationObserver: void OnImplicitAnimationsCompleted() override { delete this; } protected: void StopAnimating() { // Trigger OnImplicitAnimationsCompleted() to be called and deletes us. If // no animation is running then do the deletion ourselves. DCHECK(window_); window_->layer()->GetAnimator()->StopAnimating(); } // The window and the associated layer this observer is watching. The window // layer may be recreated during the course of the animation so |layer_| will // be different |window_->layer()| after construction. aura::Window* window_; ui::Layer* layer_; std::unique_ptr layer_owner_; absl::optional smoothness_tracker_; }; // A version of CrossFadeObserver which updates its transform to match the // visible bounds of the window it is cross-fading. It is expected users of this // will not perform their own transform animation on the passed LayerTreeOwner. class CrossFadeUpdateTransformObserver : public CrossFadeObserver, public ui::CompositorAnimationObserver { public: CrossFadeUpdateTransformObserver( aura::Window* window, std::unique_ptr layer_owner, absl::optional 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); } // CrossFadeObserver: void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override { DCHECK(!layer_owner_->root()->GetAnimator()->IsAnimatingProperty( ui::LayerAnimationElement::TRANSFORM)); CrossFadeObserver::OnLayerAnimationStarted(sequence); } // ui::CompositorAnimationObserver: void OnAnimationStep(base::TimeTicks timestamp) override { // If these get shut down or destroyed we should delete ourselves. DCHECK(compositor_); DCHECK(window_); // Calculate the transform needed to place |layer_owner_| in the same bounds // plus transform as |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()); // Apply the transform on the bounds to get the location of |window_|. // Transforms are calculated in a way where scale does not affect position, // so use the same logic here. gfx::RectF effective_bounds(new_bounds.size()); new_transform.TransformRect(&effective_bounds); effective_bounds.set_x(effective_bounds.x() + new_bounds.x()); effective_bounds.set_y(effective_bounds.y() + new_bounds.y()); 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: ui::Compositor* compositor_ = nullptr; }; // Internal implementation of a cross fade animation. If // |animate_old_layer_transform| is true, both new and old layers will animate // their opacities and transforms. Otherwise, the old layer will on animate its // opacity; its transforms will be updated via an observer. void CrossFadeAnimationInternal( aura::Window* window, std::unique_ptr old_layer_owner, bool animate_old_layer_transform, absl::optional duration, absl::optional tween_type, absl::optional histogram_name) { ui::Layer* old_layer = old_layer_owner->root(); ui::Layer* new_layer = window->layer(); DCHECK(old_layer); const gfx::Rect old_bounds(old_layer_owner->root()->bounds()); gfx::RectF old_transformed_bounds(old_bounds); gfx::Transform old_transform(old_layer_owner->root()->transform()); gfx::Transform old_transform_in_root; old_transform_in_root.Translate(old_bounds.x(), old_bounds.y()); old_transform_in_root.PreconcatTransform(old_transform); old_transform_in_root.Translate(-old_bounds.x(), -old_bounds.y()); old_transform_in_root.TransformRect(&old_transformed_bounds); const gfx::Rect new_bounds(window->bounds()); const bool old_on_top = (old_bounds.width() > new_bounds.width()); // Ensure the higher-resolution layer is on top. if (old_on_top) old_layer->parent()->StackBelow(new_layer, old_layer); else old_layer->parent()->StackAbove(new_layer, old_layer); // Shorten the animation if there's not much visual movement. 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); // Scale up the old layer while translating to new position. { ui::Layer* old_layer = old_layer_owner->root(); old_layer->GetAnimator()->StopAnimating(); // If Overview exit animation is in the sequence, stopping animation may // trigger `OverviewController::OnEndingAnimationComplete` which may finally // start the frame animation. // 'FrameHeader::FrameAnimatorView::StartAnimation' recreates the window // layer such that the `new_layer` is no longer the window's current layer. // Therefore, we should update the `new_layer`. Refer to // https://crbug.com/1313977. // TODO(zxdan): find a way to change the window state after exiting Overview // or avoid frame animation when setting bounds. new_layer = window->layer(); 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) { // Only caching render surface when there is an opacity animation and // multiple layers. if (!old_layer->children().empty()) settings.CacheRenderSurface(); // The old layer is on top, and should fade out. The new layer below will // stay opaque to block the desktop. old_layer->SetOpacity(kWindowAnimation_HideOpacity); } else if (!animate_old_layer_transform) { // If |animate_old_layer_transform| and |old_on_top| are both false, then // the old layer will have no animations and the observer will delete // itself (and the old layer) right away. To make sure the old layer stays // behind the new layer with opacity 1.f for the duration of the // animation, change the tween to zero. 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(old_bounds.width()); float scale_y = new_bounds.height() / static_cast(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); } // In tests |old_layer| is deleted here, as animations have zero duration. old_layer = nullptr; } // Set the new layer's current transform, such that the user sees a scaled // version of the window with the original bounds at the original position. gfx::Transform in_transform; const float scale_x = old_transformed_bounds.width() / static_cast(new_bounds.width()); const float scale_y = old_transformed_bounds.height() / static_cast(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) { // The new layer is on top and should fade in. The old layer below will // stay opaque and block the desktop. new_layer->SetOpacity(kWindowAnimation_HideOpacity); } { // Animation observer owns the old layer and deletes itself. It should be // attached to the new layer so that if the new layer animation gets // aborted, we can delete the old layer. 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); // Animate the new layer to the identity transform, so the window goes to // its newly set bounds. ui::ScopedLayerAnimationSettings settings(new_layer->GetAnimator()); settings.AddObserver(observer); settings.SetTransitionDuration(animation_duration); settings.SetTweenType(animation_tween_type); settings.DeferPaint(); if (!old_on_top) { // Only caching render surface when there is an opacity animation and // multiple layers. if (!new_layer->children().empty()) settings.CacheRenderSurface(); // New layer is on top, fade it in. new_layer->SetOpacity(kWindowAnimation_ShowOpacity); } new_layer->SetTransform(gfx::Transform()); } } } // namespace void SetTransformForScaleAnimation(ui::Layer* layer, LayerScaleAnimationDirection type) { // Scales for windows above and below the current workspace. 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) { // Recalculate the transform at restore time since the launcher item may have // moved while the window was minimized. gfx::Rect bounds = window->bounds(); gfx::Rect target_bounds = GetMinimizeAnimationTargetBoundsInScreen(window); ::wm::ConvertRectFromScreen(window->parent(), &target_bounds); float scale_x = static_cast(target_bounds.width()) / bounds.width(); float scale_y = static_cast(target_bounds.height()) / bounds.height(); std::unique_ptr scale = std::make_unique( gfx::Point3F(1, 1, 1), gfx::Point3F(scale_x, scale_y, 1)); std::unique_ptr translation = std::make_unique( 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 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))); // When hiding a window, turn off blending until the animation is 3 / 4 done // to save bandwidth and reduce jank. if (!show) { window->layer()->GetAnimator()->SchedulePauseForProperties( (duration * 3) / 4, ui::LayerAnimationElement::OPACITY); } // Fade in and out quickly when the window is small to reduce jank. float opacity = show ? 1.0f : 0.0f; window->layer()->GetAnimator()->ScheduleAnimation( new ui::LayerAnimationSequence( ui::LayerAnimationElement::CreateOpacityElement(opacity, duration / 4))); // Reset the transform to identity when the minimize animation is completed. 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::ForSmoothness( base::BindRepeating(static_cast( &base::UmaHistogramPercentage), "Ash.Window.AnimationSmoothness.Unminimize"))); base::TimeDelta duration = base::Milliseconds(kLayerAnimationsForMinimizeDurationMS); settings.SetTransitionDuration(duration); AddLayerAnimationsForMinimize(window, true); // Now that the window has been restored, we need to clear its animation style // to default so that normal animation applies. ::wm::SetWindowVisibilityAnimationType( window, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT); } void AnimateHideWindow_Minimize(aura::Window* window) { // Property sets within this scope will be implicitly animated. ::wm::ScopedHidingAnimationSettings hiding_settings(window); // Report animation smoothness for animations created within this scope. ui::AnimationThroughputReporter reporter( hiding_settings.layer_animation_settings()->GetAnimator(), metrics_util::ForSmoothness( base::BindRepeating(static_cast( &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); } // TODO(edcourtney): Consolidate with AnimateShowWindow_Fade in ui/wm/core. 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::GetScreen()->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(); return false; } } 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(); return false; } } void CrossFadeAnimation(aura::Window* window, std::unique_ptr old_layer_owner) { CrossFadeAnimationInternal( window, std::move(old_layer_owner), /*animate_old_layer=*/true, /*duration=*/absl::nullopt, /*tween_type=*/absl::nullopt, /*histogram_name=*/absl::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 old_layer_owner = ::wm::RecreateLayers(window); window->SetBounds(target_bounds); CrossFadeAnimationInternal(window, std::move(old_layer_owner), /*animate_old_layer=*/false, duration, tween_type, histogram_name); } bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) { if (::wm::WindowAnimationsDisabled(window)) return false; // Attempt to run CoreWm supplied animation types. if (::wm::AnimateOnChildWindowVisibilityChanged(window, visible)) return true; // Otherwise try to run an Ash-specific animation. 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); } std::vector CreateBrightnessGrayscaleAnimationSequence(float target_value, base::TimeDelta duration) { gfx::Tween::Type animation_type = gfx::Tween::EASE_OUT; std::unique_ptr brightness_sequence = std::make_unique(); std::unique_ptr grayscale_sequence = std::make_unique(); std::unique_ptr 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 grayscale_element = ui::LayerAnimationElement::CreateGrayscaleElement(target_value, duration); grayscale_element->set_tween_type(animation_type); grayscale_sequence->AddElement(std::move(grayscale_element)); std::vector 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); // The launcher item is visible and has an icon. if (!item_rect.IsEmpty()) return item_rect; // If both the icon width and height are 0, then there is no icon in the // launcher for |window|. If the launcher is auto hidden, one of the height or // width will be 0 but the position in the launcher and the major dimension // are still reported correctly and the window can be animated to the launcher // item's light bar. 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; } } // Coming here, there is no visible icon of that shelf item and we zoom back // to the location of the application launcher (which is fixed as first item // of the shelf). gfx::Rect work_area = display::Screen::GetScreen()->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(); return gfx::Rect(); } } // namespace ash