| // 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 <memory> |
| #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/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/i18n/rtl.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram_macros.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/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; |
| |
| // Durations for the cross-fade animation, in milliseconds. |
| const float kCrossFadeDurationMinMs = 200.f; |
| const float kCrossFadeDurationMaxMs = 400.f; |
| |
| // 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); |
| |
| int64_t Round64(float f) { |
| return static_cast<int64_t>(f + 0.5f); |
| } |
| |
| 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 base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS); |
| |
| int delta_area = std::abs(old_area - new_area); |
| // If the area didn't change, the animation is instantaneous. |
| if (delta_area == 0) |
| return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS); |
| |
| float factor = static_cast<float>(delta_area) / static_cast<float>(max_area); |
| const float kRange = kCrossFadeDurationMaxMs - kCrossFadeDurationMinMs; |
| return base::TimeDelta::FromMilliseconds( |
| Round64(kCrossFadeDurationMinMs + (factor * kRange))); |
| } |
| |
| class CrossFadeMetricsReporter : public ui::AnimationMetricsReporter { |
| public: |
| CrossFadeMetricsReporter() = default; |
| ~CrossFadeMetricsReporter() override = default; |
| |
| void Report(int value) override { |
| UMA_HISTOGRAM_PERCENTAGE("Ash.Window.AnimationSmoothness.CrossFade", value); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(CrossFadeMetricsReporter); |
| }; |
| |
| base::LazyInstance<CrossFadeMetricsReporter>::Leaky g_reporter_cross_fade = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| const int kCrossFadeDurationMS = 200; |
| |
| 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); |
| } |
| |
| bool AnimateShowWindow_SlideDown(aura::Window* window) { |
| HomeScreenController* home_screen_controller = |
| Shell::Get()->home_screen_controller(); |
| const TabletModeController* tablet_mode_controller = |
| Shell::Get()->tablet_mode_controller(); |
| |
| if (home_screen_controller && tablet_mode_controller && |
| tablet_mode_controller->IsTabletModeWindowManagerEnabled()) { |
| // Slide down the window from above screen to show and, meanwhile, slide |
| // down the home launcher off screen. |
| HomeLauncherGestureHandler* handler = |
| home_screen_controller->home_launcher_gesture_handler(); |
| if (handler && |
| handler->HideHomeLauncherForWindow( |
| display::Screen::GetScreen()->GetDisplayNearestView(window), |
| window)) { |
| // 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); |
| return true; |
| } |
| } |
| |
| // Fallback to no animation. |
| return false; |
| } |
| |
| bool AnimateHideWindow_SlideDown(aura::Window* window) { |
| // The hide animation should be handled in HomeLauncherGestureHandler, so |
| // fallback to no animation. |
| return 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); |
| 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 wm::WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE: |
| AnimateShowWindow_Minimize(window); |
| return true; |
| case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE: |
| AnimateShowWindow_BrightnessGrayscale(window); |
| return true; |
| case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_DOWN: |
| return AnimateShowWindow_SlideDown(window); |
| return true; |
| case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE_IN_SLIDE_OUT: |
| AnimateShowWindow_FadeIn(window); |
| return true; |
| case wm::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 wm::WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE: |
| AnimateHideWindow_Minimize(window); |
| return true; |
| case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE: |
| AnimateHideWindow_BrightnessGrayscale(window); |
| return true; |
| case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_DOWN: |
| return AnimateHideWindow_SlideDown(window); |
| case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE_IN_SLIDE_OUT: |
| AnimateHideWindow_SlideOut(window); |
| return true; |
| case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_STEP_END: |
| AnimateHideWindow_StepEnd(window); |
| return true; |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| // 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| and its child layers. |
| CrossFadeObserver(aura::Window* window, |
| std::unique_ptr<ui::LayerTreeOwner> layer_owner) |
| : window_(window), layer_owner_(std::move(layer_owner)) { |
| window_->AddObserver(this); |
| } |
| ~CrossFadeObserver() override { |
| window_->RemoveObserver(this); |
| window_ = NULL; |
| } |
| |
| // aura::WindowObserver overrides: |
| void OnWindowDestroying(aura::Window* window) override { |
| // Triggers OnImplicitAnimationsCompleted() to be called and deletes us. |
| layer_owner_->root()->GetAnimator()->StopAnimating(); |
| } |
| void OnWindowRemovingFromRootWindow(aura::Window* window, |
| aura::Window* new_root) override { |
| layer_owner_->root()->GetAnimator()->StopAnimating(); |
| } |
| |
| // ui::ImplicitAnimationObserver overrides: |
| void OnImplicitAnimationsCompleted() override { delete this; } |
| |
| private: |
| aura::Window* window_; // not owned |
| std::unique_ptr<ui::LayerTreeOwner> layer_owner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CrossFadeObserver); |
| }; |
| |
| base::TimeDelta CrossFadeAnimation( |
| aura::Window* window, |
| std::unique_ptr<ui::LayerTreeOwner> old_layer_owner, |
| gfx::Tween::Type tween_type) { |
| 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 duration = |
| GetCrossFadeDuration(window, old_transformed_bounds, new_bounds); |
| |
| // 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()); |
| // Animation observer owns the old layer and deletes itself. |
| settings.AddObserver( |
| new CrossFadeObserver(window, std::move(old_layer_owner))); |
| settings.SetTransitionDuration(duration); |
| settings.SetTweenType(tween_type); |
| // Only add reporter to |old_layer|. |
| settings.SetAnimationMetricsReporter(g_reporter_cross_fade.Pointer()); |
| 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); |
| } |
| gfx::Transform out_transform; |
| float scale_x = static_cast<float>(new_bounds.width()) / |
| static_cast<float>(old_bounds.width()); |
| float scale_y = static_cast<float>(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 = NULL; |
| } |
| |
| // 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); |
| } |
| { |
| // Animate the new layer to the identity transform, so the window goes to |
| // its newly set bounds. |
| ui::ScopedLayerAnimationSettings settings(new_layer->GetAnimator()); |
| settings.SetTransitionDuration(duration); |
| settings.SetTweenType(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()); |
| } |
| return duration; |
| } |
| |
| 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() == SHELF_ALIGNMENT_LEFT) |
| item_rect.set_x(shelf_bounds.right()); |
| else if (shelf->alignment() == SHELF_ALIGNMENT_RIGHT) |
| 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 SHELF_ALIGNMENT_BOTTOM: |
| case SHELF_ALIGNMENT_BOTTOM_LOCKED: |
| return gfx::Rect(ltr_adjusted_x, work_area.bottom(), 0, 0); |
| case SHELF_ALIGNMENT_LEFT: |
| return gfx::Rect(work_area.x(), work_area.y(), 0, 0); |
| case SHELF_ALIGNMENT_RIGHT: |
| return gfx::Rect(work_area.right(), work_area.y(), 0, 0); |
| } |
| NOTREACHED(); |
| return gfx::Rect(); |
| } |
| |
| } // namespace ash |