1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/wm/window_animations.h"
6
7 #include <math.h>
8 #include <algorithm>
9 #include <utility>
10 #include <vector>
11
12 #include "ash/home_screen/home_launcher_gesture_handler.h"
13 #include "ash/home_screen/home_screen_controller.h"
14 #include "ash/public/cpp/metrics_util.h"
15 #include "ash/public/cpp/window_animation_types.h"
16 #include "ash/shelf/shelf.h"
17 #include "ash/shell.h"
18 #include "ash/wm/pip/pip_positioner.h"
19 #include "ash/wm/window_util.h"
20 #include "ash/wm/workspace_controller.h"
21 #include "base/check.h"
22 #include "base/i18n/rtl.h"
23 #include "base/metrics/histogram_functions.h"
24 #include "base/metrics/histogram_macros.h"
25 #include "base/notreached.h"
26 #include "base/optional.h"
27 #include "base/stl_util.h"
28 #include "base/time/time.h"
29 #include "ui/aura/client/aura_constants.h"
30 #include "ui/aura/window.h"
31 #include "ui/aura/window_observer.h"
32 #include "ui/base/class_property.h"
33 #include "ui/compositor/animation_throughput_reporter.h"
34 #include "ui/compositor/compositor_observer.h"
35 #include "ui/compositor/layer.h"
36 #include "ui/compositor/layer_animation_observer.h"
37 #include "ui/compositor/layer_animation_sequence.h"
38 #include "ui/compositor/layer_animator.h"
39 #include "ui/compositor/layer_tree_owner.h"
40 #include "ui/compositor/scoped_layer_animation_settings.h"
41 #include "ui/display/display.h"
42 #include "ui/display/screen.h"
43 #include "ui/gfx/interpolated_transform.h"
44 #include "ui/gfx/transform.h"
45 #include "ui/wm/core/coordinate_conversion.h"
46 #include "ui/wm/core/window_util.h"
47
48 namespace ash {
49 namespace {
50
51 const int kLayerAnimationsForMinimizeDurationMS = 200;
52
53 // Amount of time for the cross fade animation.
54 constexpr base::TimeDelta kCrossFadeDuration =
55 base::TimeDelta::FromMilliseconds(200);
56
57 constexpr base::TimeDelta kCrossFadeMaxDuration =
58 base::TimeDelta::FromMilliseconds(400);
59
60 // Durations for the brightness/grayscale fade animation, in milliseconds.
61 const int kBrightnessGrayscaleFadeDurationMs = 1000;
62
63 // Duration for fade in animation, in milliseconds.
64 const int kFadeInAnimationMs = 200;
65
66 // Brightness/grayscale values for hide/show window animations.
67 const float kWindowAnimation_HideBrightnessGrayscale = 1.f;
68 const float kWindowAnimation_ShowBrightnessGrayscale = 0.f;
69
70 const float kWindowAnimation_HideOpacity = 0.f;
71 const float kWindowAnimation_ShowOpacity = 1.f;
72
73 // Duration for gfx::Tween::ZERO animation of showing window.
74 constexpr base::TimeDelta kZeroAnimationMs =
75 base::TimeDelta::FromMilliseconds(300);
76
77 constexpr char kCrossFadeSmoothness[] =
78 "Ash.Window.AnimationSmoothness.CrossFade";
79
GetCrossFadeDuration(aura::Window * window,const gfx::RectF & old_bounds,const gfx::Rect & new_bounds)80 base::TimeDelta GetCrossFadeDuration(aura::Window* window,
81 const gfx::RectF& old_bounds,
82 const gfx::Rect& new_bounds) {
83 if (::wm::WindowAnimationsDisabled(window))
84 return base::TimeDelta();
85
86 int old_area = static_cast<int>(old_bounds.width() * old_bounds.height());
87 int new_area = new_bounds.width() * new_bounds.height();
88 int max_area = std::max(old_area, new_area);
89 // Avoid divide by zero.
90 if (max_area == 0)
91 return kCrossFadeDuration;
92
93 int delta_area = std::abs(old_area - new_area);
94 // If the area didn't change, the animation is instantaneous.
95 if (delta_area == 0)
96 return kCrossFadeDuration;
97
98 float factor = static_cast<float>(delta_area) / static_cast<float>(max_area);
99 const auto kRange = kCrossFadeMaxDuration - kCrossFadeDuration;
100 return kCrossFadeDuration + factor * kRange;
101 }
102
103 // Observer for a window cross-fade animation. If either the window closes or
104 // the layer's animation completes, it deletes the layer and removes itself as
105 // an observer.
106 class CrossFadeObserver : public aura::WindowObserver,
107 public ui::ImplicitAnimationObserver {
108 public:
109 // Observes |window| for destruction, but does not take ownership.
110 // 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)111 CrossFadeObserver(aura::Window* window,
112 std::unique_ptr<ui::LayerTreeOwner> layer_owner,
113 base::Optional<std::string> histogram_name)
114 : window_(window),
115 layer_(window->layer()),
116 layer_owner_(std::move(layer_owner)) {
117 window_->AddObserver(this);
118
119 smoothness_tracker_ =
120 layer_->GetCompositor()->RequestNewThroughputTracker();
121 smoothness_tracker_->Start(metrics_util::ForSmoothness(base::BindRepeating(
122 [](const base::Optional<std::string>& histogram_name, int smoothness) {
123 if (histogram_name) {
124 DCHECK(!histogram_name->empty());
125 base::UmaHistogramPercentageObsoleteDoNotUse(*histogram_name,
126 smoothness);
127 } else {
128 UMA_HISTOGRAM_PERCENTAGE(kCrossFadeSmoothness, smoothness);
129 }
130 },
131 std::move(histogram_name))));
132 }
133 CrossFadeObserver(const CrossFadeObserver&) = delete;
134 CrossFadeObserver& operator=(const CrossFadeObserver&) = delete;
~CrossFadeObserver()135 ~CrossFadeObserver() override {
136 smoothness_tracker_->Stop();
137 smoothness_tracker_.reset();
138
139 // Stop the old animator to trigger aborts or ends on any observers it may
140 // have.
141 layer_owner_->root()->GetAnimator()->StopAnimating();
142
143 window_->RemoveObserver(this);
144 window_ = nullptr;
145 layer_ = nullptr;
146 }
147
148 // aura::WindowObserver:
OnWindowDestroying(aura::Window * window)149 void OnWindowDestroying(aura::Window* window) override { StopAnimating(); }
OnWindowRemovingFromRootWindow(aura::Window * window,aura::Window * new_root)150 void OnWindowRemovingFromRootWindow(aura::Window* window,
151 aura::Window* new_root) override {
152 StopAnimating();
153 }
OnWindowLayerRecreated(aura::Window * window)154 void OnWindowLayerRecreated(aura::Window* window) override {
155 // If the window layer is recreated. |layer_| will hold the LayerAnimator we
156 // were originally observing. Layer recreation is usually done when doing
157 // another window animation, so stop this current one and trigger
158 // OnImplicitAnimationsCompleted to delete ourselves.
159 layer_->GetAnimator()->StopAnimating();
160 }
161
162 // ui::ImplicitAnimationObserver:
OnImplicitAnimationsCompleted()163 void OnImplicitAnimationsCompleted() override { delete this; }
164
165 protected:
StopAnimating()166 void StopAnimating() {
167 // Trigger OnImplicitAnimationsCompleted() to be called and deletes us. If
168 // no animation is running then do the deletion ourselves.
169 DCHECK(window_);
170 window_->layer()->GetAnimator()->StopAnimating();
171 }
172
173 // The window and the associated layer this observer is watching. The window
174 // layer may be recreated during the course of the animation so |layer_| will
175 // be different |window_->layer()| after construction.
176 aura::Window* window_;
177 ui::Layer* layer_;
178
179 std::unique_ptr<ui::LayerTreeOwner> layer_owner_;
180
181 base::Optional<ui::ThroughputTracker> smoothness_tracker_;
182 };
183
184 // A version of CrossFadeObserver which updates its transform to match the
185 // visible bounds of the window it is cross-fading. It is expected users of this
186 // will not perform their own transform animation on the passed LayerTreeOwner.
187 class CrossFadeUpdateTransformObserver
188 : public CrossFadeObserver,
189 public ui::CompositorAnimationObserver {
190 public:
CrossFadeUpdateTransformObserver(aura::Window * window,std::unique_ptr<ui::LayerTreeOwner> layer_owner,base::Optional<std::string> histogram_name)191 CrossFadeUpdateTransformObserver(
192 aura::Window* window,
193 std::unique_ptr<ui::LayerTreeOwner> layer_owner,
194 base::Optional<std::string> histogram_name)
195 : CrossFadeObserver(window, std::move(layer_owner), histogram_name) {
196 compositor_ = window->layer()->GetCompositor();
197 compositor_->AddAnimationObserver(this);
198 }
199 CrossFadeUpdateTransformObserver(const CrossFadeUpdateTransformObserver&) =
200 delete;
201 CrossFadeUpdateTransformObserver& operator=(
202 const CrossFadeUpdateTransformObserver&) = delete;
~CrossFadeUpdateTransformObserver()203 ~CrossFadeUpdateTransformObserver() override {
204 compositor_->RemoveAnimationObserver(this);
205 }
206
207 // CrossFadeObserver:
OnLayerAnimationStarted(ui::LayerAnimationSequence * sequence)208 void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override {
209 DCHECK(!layer_owner_->root()->GetAnimator()->IsAnimatingProperty(
210 ui::LayerAnimationElement::TRANSFORM));
211 CrossFadeObserver::OnLayerAnimationStarted(sequence);
212 }
213
214 // ui::CompositorAnimationObserver:
OnAnimationStep(base::TimeTicks timestamp)215 void OnAnimationStep(base::TimeTicks timestamp) override {
216 // If these get shut down or destroyed we should delete ourselves.
217 DCHECK(compositor_);
218 DCHECK(window_);
219
220 // Calculate the transform needed to place |layer_owner_| in the same bounds
221 // plus transform as |window_|.
222 gfx::RectF old_bounds(layer_owner_->root()->bounds());
223 gfx::RectF new_bounds(window_->bounds());
224 gfx::Transform new_transform = window_->transform();
225 DCHECK(new_transform.IsScaleOrTranslation());
226
227 // Apply the transform on the bounds to get the location of |window_|.
228 // Transforms are calculated in a way where scale does not affect position,
229 // so use the same logic here.
230 gfx::RectF effective_bounds(new_bounds.size());
231 new_transform.TransformRect(&effective_bounds);
232 effective_bounds.set_x(effective_bounds.x() + new_bounds.x());
233 effective_bounds.set_y(effective_bounds.y() + new_bounds.y());
234
235 const gfx::Transform old_transform =
236 gfx::TransformBetweenRects(old_bounds, effective_bounds);
237 layer_owner_->root()->SetTransform(old_transform);
238 }
239
OnCompositingShuttingDown(ui::Compositor * compositor)240 void OnCompositingShuttingDown(ui::Compositor* compositor) override {
241 DCHECK_EQ(compositor_, compositor);
242 StopAnimating();
243 }
244
245 private:
246 ui::Compositor* compositor_ = nullptr;
247 };
248
249 // Internal implementation of a cross fade animation. If
250 // |animate_old_layer_transform| is true, both new and old layers will animate
251 // their opacities and transforms. Otherwise, the old layer will on animate its
252 // opacity; its transforms will be updated via an observer.
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)253 void CrossFadeAnimationInternal(
254 aura::Window* window,
255 std::unique_ptr<ui::LayerTreeOwner> old_layer_owner,
256 bool animate_old_layer_transform,
257 base::Optional<base::TimeDelta> duration,
258 base::Optional<gfx::Tween::Type> tween_type,
259 base::Optional<std::string> histogram_name) {
260 ui::Layer* old_layer = old_layer_owner->root();
261 ui::Layer* new_layer = window->layer();
262
263 DCHECK(old_layer);
264 const gfx::Rect old_bounds(old_layer_owner->root()->bounds());
265
266 gfx::RectF old_transformed_bounds(old_bounds);
267 gfx::Transform old_transform(old_layer_owner->root()->transform());
268 gfx::Transform old_transform_in_root;
269 old_transform_in_root.Translate(old_bounds.x(), old_bounds.y());
270 old_transform_in_root.PreconcatTransform(old_transform);
271 old_transform_in_root.Translate(-old_bounds.x(), -old_bounds.y());
272 old_transform_in_root.TransformRect(&old_transformed_bounds);
273 const gfx::Rect new_bounds(window->bounds());
274 const bool old_on_top = (old_bounds.width() > new_bounds.width());
275
276 // Ensure the higher-resolution layer is on top.
277 if (old_on_top)
278 old_layer->parent()->StackBelow(new_layer, old_layer);
279 else
280 old_layer->parent()->StackAbove(new_layer, old_layer);
281
282 // Shorten the animation if there's not much visual movement.
283 const base::TimeDelta animation_duration = duration.value_or(
284 GetCrossFadeDuration(window, old_transformed_bounds, new_bounds));
285 const gfx::Tween::Type animation_tween_type =
286 tween_type.value_or(gfx::Tween::EASE_OUT);
287
288 // Scale up the old layer while translating to new position.
289 {
290 ui::Layer* old_layer = old_layer_owner->root();
291 old_layer->GetAnimator()->StopAnimating();
292 old_layer->SetTransform(old_transform);
293 ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
294 settings.SetTransitionDuration(animation_duration);
295 settings.SetTweenType(animation_tween_type);
296 settings.DeferPaint();
297
298 if (old_on_top) {
299 // Only caching render surface when there is an opacity animation and
300 // multiple layers.
301 if (!old_layer->children().empty())
302 settings.CacheRenderSurface();
303 // The old layer is on top, and should fade out. The new layer below will
304 // stay opaque to block the desktop.
305 old_layer->SetOpacity(kWindowAnimation_HideOpacity);
306 } else if (!animate_old_layer_transform) {
307 // If |animate_old_layer_transform| and |old_on_top| are both false, then
308 // the old layer will have no animations and the observer will delete
309 // itself (and the old layer) right away. To make sure the old layer stays
310 // behind the new layer with opacity 1.f for the duration of the
311 // animation, change the tween to zero.
312 settings.SetTweenType(gfx::Tween::ZERO);
313 old_layer->SetOpacity(kWindowAnimation_HideOpacity);
314 }
315
316 if (animate_old_layer_transform) {
317 gfx::Transform out_transform;
318 float scale_x =
319 new_bounds.width() / static_cast<float>(old_bounds.width());
320 float scale_y =
321 new_bounds.height() / static_cast<float>(old_bounds.height());
322 out_transform.Translate(new_bounds.x() - old_bounds.x(),
323 new_bounds.y() - old_bounds.y());
324 out_transform.Scale(scale_x, scale_y);
325 old_layer->SetTransform(out_transform);
326 }
327 // In tests |old_layer| is deleted here, as animations have zero duration.
328 old_layer = nullptr;
329 }
330
331 // Set the new layer's current transform, such that the user sees a scaled
332 // version of the window with the original bounds at the original position.
333 gfx::Transform in_transform;
334 const float scale_x =
335 old_transformed_bounds.width() / static_cast<float>(new_bounds.width());
336 const float scale_y =
337 old_transformed_bounds.height() / static_cast<float>(new_bounds.height());
338 in_transform.Translate(old_transformed_bounds.x() - new_bounds.x(),
339 old_transformed_bounds.y() - new_bounds.y());
340 in_transform.Scale(scale_x, scale_y);
341 new_layer->SetTransform(in_transform);
342 if (!old_on_top) {
343 // The new layer is on top and should fade in. The old layer below will
344 // stay opaque and block the desktop.
345 new_layer->SetOpacity(kWindowAnimation_HideOpacity);
346 }
347 {
348 // Animation observer owns the old layer and deletes itself. It should be
349 // attached to the new layer so that if the new layer animation gets
350 // aborted, we can delete the old layer.
351 CrossFadeObserver* observer =
352 animate_old_layer_transform
353 ? new CrossFadeObserver(window, std::move(old_layer_owner),
354 histogram_name)
355 : new CrossFadeUpdateTransformObserver(
356 window, std::move(old_layer_owner), histogram_name);
357
358 // Animate the new layer to the identity transform, so the window goes to
359 // its newly set bounds.
360 ui::ScopedLayerAnimationSettings settings(new_layer->GetAnimator());
361 settings.AddObserver(observer);
362 settings.SetTransitionDuration(animation_duration);
363 settings.SetTweenType(animation_tween_type);
364 settings.DeferPaint();
365 if (!old_on_top) {
366 // Only caching render surface when there is an opacity animation and
367 // multiple layers.
368 if (!new_layer->children().empty())
369 settings.CacheRenderSurface();
370 // New layer is on top, fade it in.
371 new_layer->SetOpacity(kWindowAnimation_ShowOpacity);
372 }
373 new_layer->SetTransform(gfx::Transform());
374 }
375 }
376
377 } // namespace
378
SetTransformForScaleAnimation(ui::Layer * layer,LayerScaleAnimationDirection type)379 void SetTransformForScaleAnimation(ui::Layer* layer,
380 LayerScaleAnimationDirection type) {
381 // Scales for windows above and below the current workspace.
382 constexpr float kLayerScaleAboveSize = 1.1f;
383 constexpr float kLayerScaleBelowSize = .9f;
384
385 const float scale = type == LAYER_SCALE_ANIMATION_ABOVE
386 ? kLayerScaleAboveSize
387 : kLayerScaleBelowSize;
388 gfx::Transform transform;
389 transform.Translate(-layer->bounds().width() * (scale - 1.0f) / 2,
390 -layer->bounds().height() * (scale - 1.0f) / 2);
391 transform.Scale(scale, scale);
392 layer->SetTransform(transform);
393 }
394
AddLayerAnimationsForMinimize(aura::Window * window,bool show)395 void AddLayerAnimationsForMinimize(aura::Window* window, bool show) {
396 // Recalculate the transform at restore time since the launcher item may have
397 // moved while the window was minimized.
398 gfx::Rect bounds = window->bounds();
399 gfx::Rect target_bounds = GetMinimizeAnimationTargetBoundsInScreen(window);
400 ::wm::ConvertRectFromScreen(window->parent(), &target_bounds);
401
402 float scale_x = static_cast<float>(target_bounds.width()) / bounds.width();
403 float scale_y = static_cast<float>(target_bounds.height()) / bounds.height();
404
405 std::unique_ptr<ui::InterpolatedTransform> scale =
406 std::make_unique<ui::InterpolatedScale>(
407 gfx::Point3F(1, 1, 1), gfx::Point3F(scale_x, scale_y, 1));
408
409 std::unique_ptr<ui::InterpolatedTransform> translation =
410 std::make_unique<ui::InterpolatedTranslation>(
411 gfx::PointF(), gfx::PointF(target_bounds.x() - bounds.x(),
412 target_bounds.y() - bounds.y()));
413
414 scale->SetChild(std::move(translation));
415 scale->SetReversed(show);
416
417 base::TimeDelta duration =
418 window->layer()->GetAnimator()->GetTransitionDuration();
419
420 std::unique_ptr<ui::LayerAnimationElement> transition =
421 ui::LayerAnimationElement::CreateInterpolatedTransformElement(
422 std::move(scale), duration);
423
424 transition->set_tween_type(show ? gfx::Tween::EASE_IN
425 : gfx::Tween::EASE_IN_OUT);
426
427 window->layer()->GetAnimator()->ScheduleAnimation(
428 new ui::LayerAnimationSequence(std::move(transition)));
429
430 // When hiding a window, turn off blending until the animation is 3 / 4 done
431 // to save bandwidth and reduce jank.
432 if (!show) {
433 window->layer()->GetAnimator()->SchedulePauseForProperties(
434 (duration * 3) / 4, ui::LayerAnimationElement::OPACITY);
435 }
436
437 // Fade in and out quickly when the window is small to reduce jank.
438 float opacity = show ? 1.0f : 0.0f;
439 window->layer()->GetAnimator()->ScheduleAnimation(
440 new ui::LayerAnimationSequence(
441 ui::LayerAnimationElement::CreateOpacityElement(opacity,
442 duration / 4)));
443
444 // Reset the transform to identity when the minimize animation is completed.
445 window->layer()->GetAnimator()->ScheduleAnimation(
446 new ui::LayerAnimationSequence(
447 ui::LayerAnimationElement::CreateTransformElement(
448 gfx::Transform(), base::TimeDelta())));
449 }
450
AnimateShowWindow_Minimize(aura::Window * window)451 void AnimateShowWindow_Minimize(aura::Window* window) {
452 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
453 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
454 base::TimeDelta duration =
455 base::TimeDelta::FromMilliseconds(kLayerAnimationsForMinimizeDurationMS);
456 settings.SetTransitionDuration(duration);
457 AddLayerAnimationsForMinimize(window, true);
458
459 // Now that the window has been restored, we need to clear its animation style
460 // to default so that normal animation applies.
461 ::wm::SetWindowVisibilityAnimationType(
462 window, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
463 }
464
AnimateHideWindow_Minimize(aura::Window * window)465 void AnimateHideWindow_Minimize(aura::Window* window) {
466 // Property sets within this scope will be implicitly animated.
467 ::wm::ScopedHidingAnimationSettings hiding_settings(window);
468 base::TimeDelta duration =
469 base::TimeDelta::FromMilliseconds(kLayerAnimationsForMinimizeDurationMS);
470 hiding_settings.layer_animation_settings()->SetTransitionDuration(duration);
471 window->layer()->SetVisible(false);
472
473 AddLayerAnimationsForMinimize(window, false);
474 }
475
AnimateShowHideWindowCommon_BrightnessGrayscale(aura::Window * window,bool show)476 void AnimateShowHideWindowCommon_BrightnessGrayscale(aura::Window* window,
477 bool show) {
478 float start_value, end_value;
479 if (show) {
480 start_value = kWindowAnimation_HideBrightnessGrayscale;
481 end_value = kWindowAnimation_ShowBrightnessGrayscale;
482 } else {
483 start_value = kWindowAnimation_ShowBrightnessGrayscale;
484 end_value = kWindowAnimation_HideBrightnessGrayscale;
485 }
486
487 window->layer()->SetLayerBrightness(start_value);
488 window->layer()->SetLayerGrayscale(start_value);
489 if (show) {
490 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
491 window->layer()->SetVisible(true);
492 }
493
494 base::TimeDelta duration =
495 base::TimeDelta::FromMilliseconds(kBrightnessGrayscaleFadeDurationMs);
496
497 if (show) {
498 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
499 window->layer()->GetAnimator()->ScheduleTogether(
500 CreateBrightnessGrayscaleAnimationSequence(end_value, duration));
501 } else {
502 ::wm::ScopedHidingAnimationSettings hiding_settings(window);
503 window->layer()->GetAnimator()->ScheduleTogether(
504 CreateBrightnessGrayscaleAnimationSequence(end_value, duration));
505 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
506 window->layer()->SetVisible(false);
507 }
508 }
509
AnimateShowWindow_BrightnessGrayscale(aura::Window * window)510 void AnimateShowWindow_BrightnessGrayscale(aura::Window* window) {
511 AnimateShowHideWindowCommon_BrightnessGrayscale(window, true);
512 }
513
514 // TODO(edcourtney): Consolidate with AnimateShowWindow_Fade in ui/wm/core.
AnimateShowWindow_FadeIn(aura::Window * window)515 void AnimateShowWindow_FadeIn(aura::Window* window) {
516 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
517 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
518 settings.SetTransitionDuration(
519 base::TimeDelta::FromMilliseconds(kFadeInAnimationMs));
520 window->layer()->SetVisible(true);
521 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
522 }
523
AnimateHideWindow_BrightnessGrayscale(aura::Window * window)524 void AnimateHideWindow_BrightnessGrayscale(aura::Window* window) {
525 AnimateShowHideWindowCommon_BrightnessGrayscale(window, false);
526 }
527
AnimateHideWindow_SlideOut(aura::Window * window)528 void AnimateHideWindow_SlideOut(aura::Window* window) {
529 base::TimeDelta duration =
530 base::TimeDelta::FromMilliseconds(PipPositioner::kPipDismissTimeMs);
531
532 ::wm::ScopedHidingAnimationSettings settings(window);
533 settings.layer_animation_settings()->SetTransitionDuration(duration);
534 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
535 window->layer()->SetVisible(false);
536
537 gfx::Rect bounds = window->GetBoundsInScreen();
538 display::Display display =
539 display::Screen::GetScreen()->GetDisplayNearestWindow(window);
540 gfx::Rect dismissed_bounds =
541 PipPositioner::GetDismissedPosition(display, bounds);
542 ::wm::ConvertRectFromScreen(window->parent(), &dismissed_bounds);
543 window->layer()->SetBounds(dismissed_bounds);
544 }
545
AnimateShowWindow_StepEnd(aura::Window * window)546 void AnimateShowWindow_StepEnd(aura::Window* window) {
547 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
548 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
549 settings.SetTransitionDuration(kZeroAnimationMs);
550 settings.SetTweenType(gfx::Tween::ZERO);
551 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
552 }
553
AnimateHideWindow_StepEnd(aura::Window * window)554 void AnimateHideWindow_StepEnd(aura::Window* window) {
555 ::wm::ScopedHidingAnimationSettings settings(window);
556 settings.layer_animation_settings()->SetTransitionDuration(kZeroAnimationMs);
557 settings.layer_animation_settings()->SetTweenType(gfx::Tween::ZERO);
558 window->layer()->SetVisible(false);
559 }
560
AnimateShowWindow(aura::Window * window)561 bool AnimateShowWindow(aura::Window* window) {
562 if (!::wm::HasWindowVisibilityAnimationTransition(window,
563 ::wm::ANIMATE_SHOW)) {
564 return false;
565 }
566
567 switch (::wm::GetWindowVisibilityAnimationType(window)) {
568 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
569 AnimateShowWindow_Minimize(window);
570 return true;
571 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
572 AnimateShowWindow_BrightnessGrayscale(window);
573 return true;
574 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE_IN_SLIDE_OUT:
575 AnimateShowWindow_FadeIn(window);
576 return true;
577 case WINDOW_VISIBILITY_ANIMATION_TYPE_STEP_END:
578 AnimateShowWindow_StepEnd(window);
579 return true;
580 default:
581 NOTREACHED();
582 return false;
583 }
584 }
585
AnimateHideWindow(aura::Window * window)586 bool AnimateHideWindow(aura::Window* window) {
587 if (!::wm::HasWindowVisibilityAnimationTransition(window,
588 ::wm::ANIMATE_HIDE)) {
589 return false;
590 }
591
592 switch (::wm::GetWindowVisibilityAnimationType(window)) {
593 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
594 AnimateHideWindow_Minimize(window);
595 return true;
596 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
597 AnimateHideWindow_BrightnessGrayscale(window);
598 return true;
599 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE_IN_SLIDE_OUT:
600 AnimateHideWindow_SlideOut(window);
601 return true;
602 case WINDOW_VISIBILITY_ANIMATION_TYPE_STEP_END:
603 AnimateHideWindow_StepEnd(window);
604 return true;
605 default:
606 NOTREACHED();
607 return false;
608 }
609 }
610
CrossFadeAnimation(aura::Window * window,std::unique_ptr<ui::LayerTreeOwner> old_layer_owner)611 void CrossFadeAnimation(aura::Window* window,
612 std::unique_ptr<ui::LayerTreeOwner> old_layer_owner) {
613 CrossFadeAnimationInternal(
614 window, std::move(old_layer_owner), /*animate_old_layer=*/true,
615 /*duration=*/base::nullopt, /*tween_type=*/base::nullopt,
616 /*histogram_name=*/base::nullopt);
617 }
618
CrossFadeAnimationAnimateNewLayerOnly(aura::Window * window,const gfx::Rect & target_bounds,base::TimeDelta duration,gfx::Tween::Type tween_type,const std::string & histogram_name)619 void CrossFadeAnimationAnimateNewLayerOnly(aura::Window* window,
620 const gfx::Rect& target_bounds,
621 base::TimeDelta duration,
622 gfx::Tween::Type tween_type,
623 const std::string& histogram_name) {
624 std::unique_ptr<ui::LayerTreeOwner> old_layer_owner =
625 ::wm::RecreateLayers(window);
626 window->SetBounds(target_bounds);
627 CrossFadeAnimationInternal(window, std::move(old_layer_owner),
628 /*animate_old_layer=*/false, duration, tween_type,
629 histogram_name);
630 }
631
AnimateOnChildWindowVisibilityChanged(aura::Window * window,bool visible)632 bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) {
633 if (::wm::WindowAnimationsDisabled(window))
634 return false;
635
636 // Attempt to run CoreWm supplied animation types.
637 if (::wm::AnimateOnChildWindowVisibilityChanged(window, visible))
638 return true;
639
640 // Otherwise try to run an Ash-specific animation.
641 if (visible)
642 return AnimateShowWindow(window);
643 // Don't start hiding the window again if it's already being hidden.
644 return window->layer()->GetTargetOpacity() != 0.0f &&
645 AnimateHideWindow(window);
646 }
647
648 std::vector<ui::LayerAnimationSequence*>
CreateBrightnessGrayscaleAnimationSequence(float target_value,base::TimeDelta duration)649 CreateBrightnessGrayscaleAnimationSequence(float target_value,
650 base::TimeDelta duration) {
651 gfx::Tween::Type animation_type = gfx::Tween::EASE_OUT;
652 std::unique_ptr<ui::LayerAnimationSequence> brightness_sequence =
653 std::make_unique<ui::LayerAnimationSequence>();
654 std::unique_ptr<ui::LayerAnimationSequence> grayscale_sequence =
655 std::make_unique<ui::LayerAnimationSequence>();
656
657 std::unique_ptr<ui::LayerAnimationElement> brightness_element =
658 ui::LayerAnimationElement::CreateBrightnessElement(target_value,
659 duration);
660 brightness_element->set_tween_type(animation_type);
661 brightness_sequence->AddElement(std::move(brightness_element));
662
663 std::unique_ptr<ui::LayerAnimationElement> grayscale_element =
664 ui::LayerAnimationElement::CreateGrayscaleElement(target_value, duration);
665 grayscale_element->set_tween_type(animation_type);
666 grayscale_sequence->AddElement(std::move(grayscale_element));
667
668 std::vector<ui::LayerAnimationSequence*> animations;
669 animations.push_back(brightness_sequence.release());
670 animations.push_back(grayscale_sequence.release());
671
672 return animations;
673 }
674
GetMinimizeAnimationTargetBoundsInScreen(aura::Window * window)675 gfx::Rect GetMinimizeAnimationTargetBoundsInScreen(aura::Window* window) {
676 Shelf* shelf = Shelf::ForWindow(window);
677 gfx::Rect item_rect = shelf->GetScreenBoundsOfItemIconForWindow(window);
678
679 // The launcher item is visible and has an icon.
680 if (!item_rect.IsEmpty())
681 return item_rect;
682
683 // If both the icon width and height are 0, then there is no icon in the
684 // launcher for |window|. If the launcher is auto hidden, one of the height or
685 // width will be 0 but the position in the launcher and the major dimension
686 // are still reported correctly and the window can be animated to the launcher
687 // item's light bar.
688 if (item_rect.width() != 0 || item_rect.height() != 0) {
689 if (shelf->GetVisibilityState() == SHELF_AUTO_HIDE) {
690 gfx::Rect shelf_bounds = shelf->GetWindow()->GetBoundsInScreen();
691 if (shelf->alignment() == ShelfAlignment::kLeft)
692 item_rect.set_x(shelf_bounds.right());
693 else if (shelf->alignment() == ShelfAlignment::kRight)
694 item_rect.set_x(shelf_bounds.x());
695 else
696 item_rect.set_y(shelf_bounds.y());
697 return item_rect;
698 }
699 }
700
701 // Coming here, there is no visible icon of that shelf item and we zoom back
702 // to the location of the application launcher (which is fixed as first item
703 // of the shelf).
704 gfx::Rect work_area =
705 display::Screen::GetScreen()->GetDisplayNearestWindow(window).work_area();
706 int ltr_adjusted_x = base::i18n::IsRTL() ? work_area.right() : work_area.x();
707 switch (shelf->alignment()) {
708 case ShelfAlignment::kBottom:
709 case ShelfAlignment::kBottomLocked:
710 return gfx::Rect(ltr_adjusted_x, work_area.bottom(), 0, 0);
711 case ShelfAlignment::kLeft:
712 return gfx::Rect(work_area.x(), work_area.y(), 0, 0);
713 case ShelfAlignment::kRight:
714 return gfx::Rect(work_area.right(), work_area.y(), 0, 0);
715 }
716 NOTREACHED();
717 return gfx::Rect();
718 }
719
720 } // namespace ash
721