| // 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 <math.h> |
| #include <algorithm> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/home_screen/home_launcher_gesture_handler.h" |
| #include "ash/home_screen/home_screen_controller.h" |
| #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/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/optional.h" |
| #include "base/stl_util.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_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/interpolated_transform.h" |
| #include "ui/gfx/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::TimeDelta::FromMilliseconds(200); |
| |
| constexpr base::TimeDelta kCrossFadeMaxDuration = |
| base::TimeDelta::FromMilliseconds(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::TimeDelta::FromMilliseconds(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); |
| // 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<float>(delta_area) / static_cast<float>(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<ui::LayerTreeOwner> layer_owner, |
| base::Optional<std::string> 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 base::Optional<std::string>& histogram_name, int smoothness) { |
| if (histogram_name) { |
| DCHECK(!histogram_name->empty()); |
| base::UmaHistogramPercentageObsoleteDoNotUse(*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<ui::LayerTreeOwner> layer_owner_; |
| |
| base::Optional<ui::ThroughputTracker> 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<ui::LayerTreeOwner> layer_owner, |
| base::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); |
| } |
| |
| // 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<ui::LayerTreeOwner> old_layer_owner, |
| bool animate_old_layer_transform, |
| base::Optional<base::TimeDelta> duration, |
| base::Optional<gfx::Tween::Type> tween_type, |
| base::Optional<std::string> 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(); |
| 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<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); |
| } |
| // 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<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) { |
| // 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<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))); |
| |
| // 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()); |
| base::TimeDelta duration = |
| base::TimeDelta::FromMilliseconds(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); |
| base::TimeDelta duration = |
| base::TimeDelta::FromMilliseconds(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::TimeDelta::FromMilliseconds(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::TimeDelta::FromMilliseconds(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::TimeDelta::FromMilliseconds(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<ui::LayerTreeOwner> old_layer_owner) { |
| CrossFadeAnimationInternal( |
| window, std::move(old_layer_owner), /*animate_old_layer=*/true, |
| /*duration=*/base::nullopt, /*tween_type=*/base::nullopt, |
| /*histogram_name=*/base::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), |
| /*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<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); |
| |
| // 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 |