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.
5 #include "ash/wm/window_animations.h"
12 #include "ash/launcher/launcher.h"
13 #include "ash/screen_ash.h"
14 #include "ash/shelf/shelf_layout_manager.h"
15 #include "ash/shelf/shelf_widget.h"
16 #include "ash/shell.h"
17 #include "ash/wm/window_util.h"
18 #include "ash/wm/workspace_controller.h"
19 #include "base/command_line.h"
20 #include "base/compiler_specific.h"
21 #include "base/logging.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/stl_util.h"
24 #include "base/time/time.h"
25 #include "ui/aura/client/aura_constants.h"
26 #include "ui/aura/window.h"
27 #include "ui/aura/window_observer.h"
28 #include "ui/aura/window_property.h"
29 #include "ui/compositor/compositor_observer.h"
30 #include "ui/compositor/layer.h"
31 #include "ui/compositor/layer_animation_observer.h"
32 #include "ui/compositor/layer_animation_sequence.h"
33 #include "ui/compositor/layer_animator.h"
34 #include "ui/compositor/scoped_layer_animation_settings.h"
35 #include "ui/gfx/interpolated_transform.h"
36 #include "ui/gfx/screen.h"
37 #include "ui/gfx/vector3d_f.h"
38 #include "ui/views/corewm/window_util.h"
39 #include "ui/views/view.h"
40 #include "ui/views/widget/widget.h"
44 const int kLayerAnimationsForMinimizeDurationMS
= 200;
46 // Durations for the cross-fade animation, in milliseconds.
47 const float kCrossFadeDurationMinMs
= 200.f
;
48 const float kCrossFadeDurationMaxMs
= 400.f
;
50 // Durations for the brightness/grayscale fade animation, in milliseconds.
51 const int kBrightnessGrayscaleFadeDurationMs
= 1000;
53 // Brightness/grayscale values for hide/show window animations.
54 const float kWindowAnimation_HideBrightnessGrayscale
= 1.f
;
55 const float kWindowAnimation_ShowBrightnessGrayscale
= 0.f
;
57 const float kWindowAnimation_HideOpacity
= 0.f
;
58 const float kWindowAnimation_ShowOpacity
= 1.f
;
59 // TODO(sky): if we end up sticking with 0, nuke the code doing the rotation.
60 const float kWindowAnimation_MinimizeRotate
= 0.f
;
62 // Tween type when cross fading a workspace window.
63 const gfx::Tween::Type kCrossFadeTweenType
= gfx::Tween::EASE_IN_OUT
;
65 // Scales for AshWindow above/below current workspace.
66 const float kLayerScaleAboveSize
= 1.1f
;
67 const float kLayerScaleBelowSize
= .9f
;
69 int64
Round64(float f
) {
70 return static_cast<int64
>(f
+ 0.5f
);
75 const int kCrossFadeDurationMS
= 200;
77 void AddLayerAnimationsForMinimize(aura::Window
* window
, bool show
) {
78 // Recalculate the transform at restore time since the launcher item may have
79 // moved while the window was minimized.
80 gfx::Rect bounds
= window
->bounds();
81 gfx::Rect target_bounds
= GetMinimizeAnimationTargetBoundsInScreen(window
);
83 ScreenAsh::ConvertRectFromScreen(window
->parent(), target_bounds
);
85 float scale_x
= static_cast<float>(target_bounds
.width()) / bounds
.width();
86 float scale_y
= static_cast<float>(target_bounds
.height()) / bounds
.height();
88 scoped_ptr
<ui::InterpolatedTransform
> scale(
89 new ui::InterpolatedScale(gfx::Point3F(1, 1, 1),
90 gfx::Point3F(scale_x
, scale_y
, 1)));
92 scoped_ptr
<ui::InterpolatedTransform
> translation(
93 new ui::InterpolatedTranslation(
95 gfx::Point(target_bounds
.x() - bounds
.x(),
96 target_bounds
.y() - bounds
.y())));
98 scoped_ptr
<ui::InterpolatedTransform
> rotation(
99 new ui::InterpolatedRotation(0, kWindowAnimation_MinimizeRotate
));
101 scoped_ptr
<ui::InterpolatedTransform
> rotation_about_pivot(
102 new ui::InterpolatedTransformAboutPivot(
103 gfx::Point(bounds
.width() * 0.5, bounds
.height() * 0.5),
104 rotation
.release()));
106 scale
->SetChild(translation
.release());
107 rotation_about_pivot
->SetChild(scale
.release());
109 rotation_about_pivot
->SetReversed(show
);
111 base::TimeDelta duration
= base::TimeDelta::FromMilliseconds(
112 kLayerAnimationsForMinimizeDurationMS
);
114 scoped_ptr
<ui::LayerAnimationElement
> transition(
115 ui::LayerAnimationElement::CreateInterpolatedTransformElement(
116 rotation_about_pivot
.release(), duration
));
118 transition
->set_tween_type(
119 show
? gfx::Tween::EASE_IN
: gfx::Tween::EASE_IN_OUT
);
121 window
->layer()->GetAnimator()->ScheduleAnimation(
122 new ui::LayerAnimationSequence(transition
.release()));
124 // When hiding a window, turn off blending until the animation is 3 / 4 done
125 // to save bandwidth and reduce jank.
127 window
->layer()->GetAnimator()->SchedulePauseForProperties(
128 (duration
* 3) / 4, ui::LayerAnimationElement::OPACITY
, -1);
131 // Fade in and out quickly when the window is small to reduce jank.
132 float opacity
= show
? 1.0f
: 0.0f
;
133 window
->layer()->GetAnimator()->ScheduleAnimation(
134 new ui::LayerAnimationSequence(
135 ui::LayerAnimationElement::CreateOpacityElement(
136 opacity
, duration
/ 4)));
139 void AnimateShowWindow_Minimize(aura::Window
* window
) {
140 window
->layer()->set_delegate(window
);
141 window
->layer()->SetOpacity(kWindowAnimation_HideOpacity
);
142 AddLayerAnimationsForMinimize(window
, true);
144 // Now that the window has been restored, we need to clear its animation style
145 // to default so that normal animation applies.
146 views::corewm::SetWindowVisibilityAnimationType(
147 window
, views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT
);
150 void AnimateHideWindow_Minimize(aura::Window
* window
) {
151 window
->layer()->set_delegate(NULL
);
153 // Property sets within this scope will be implicitly animated.
154 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
155 base::TimeDelta duration
= base::TimeDelta::FromMilliseconds(
156 kLayerAnimationsForMinimizeDurationMS
);
157 settings
.SetTransitionDuration(duration
);
158 settings
.AddObserver(
159 views::corewm::CreateHidingWindowAnimationObserver(window
));
160 window
->layer()->SetVisible(false);
162 AddLayerAnimationsForMinimize(window
, false);
165 void AnimateShowHideWindowCommon_BrightnessGrayscale(aura::Window
* window
,
167 window
->layer()->set_delegate(window
);
169 float start_value
, end_value
;
171 start_value
= kWindowAnimation_HideBrightnessGrayscale
;
172 end_value
= kWindowAnimation_ShowBrightnessGrayscale
;
174 start_value
= kWindowAnimation_ShowBrightnessGrayscale
;
175 end_value
= kWindowAnimation_HideBrightnessGrayscale
;
178 window
->layer()->SetLayerBrightness(start_value
);
179 window
->layer()->SetLayerGrayscale(start_value
);
181 window
->layer()->SetOpacity(kWindowAnimation_ShowOpacity
);
182 window
->layer()->SetVisible(true);
185 base::TimeDelta duration
=
186 base::TimeDelta::FromMilliseconds(kBrightnessGrayscaleFadeDurationMs
);
188 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
189 settings
.SetTransitionDuration(duration
);
191 settings
.AddObserver(
192 views::corewm::CreateHidingWindowAnimationObserver(window
));
195 window
->layer()->GetAnimator()->
197 CreateBrightnessGrayscaleAnimationSequence(end_value
, duration
));
199 window
->layer()->SetOpacity(kWindowAnimation_HideOpacity
);
200 window
->layer()->SetVisible(false);
204 void AnimateShowWindow_BrightnessGrayscale(aura::Window
* window
) {
205 AnimateShowHideWindowCommon_BrightnessGrayscale(window
, true);
208 void AnimateHideWindow_BrightnessGrayscale(aura::Window
* window
) {
209 AnimateShowHideWindowCommon_BrightnessGrayscale(window
, false);
212 bool AnimateShowWindow(aura::Window
* window
) {
213 if (!views::corewm::HasWindowVisibilityAnimationTransition(
214 window
, views::corewm::ANIMATE_SHOW
)) {
218 switch (views::corewm::GetWindowVisibilityAnimationType(window
)) {
219 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
:
220 AnimateShowWindow_Minimize(window
);
222 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE
:
223 AnimateShowWindow_BrightnessGrayscale(window
);
231 bool AnimateHideWindow(aura::Window
* window
) {
232 if (!views::corewm::HasWindowVisibilityAnimationTransition(
233 window
, views::corewm::ANIMATE_HIDE
)) {
237 switch (views::corewm::GetWindowVisibilityAnimationType(window
)) {
238 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
:
239 AnimateHideWindow_Minimize(window
);
241 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE
:
242 AnimateHideWindow_BrightnessGrayscale(window
);
250 // Observer for a window cross-fade animation. If either the window closes or
251 // the layer's animation completes or compositing is aborted due to GPU crash,
252 // it deletes the layer and removes itself as an observer.
253 class CrossFadeObserver
: public ui::CompositorObserver
,
254 public aura::WindowObserver
,
255 public ui::ImplicitAnimationObserver
{
257 // Observes |window| for destruction, but does not take ownership.
258 // Takes ownership of |layer| and its child layers.
259 CrossFadeObserver(aura::Window
* window
, ui::Layer
* layer
)
262 window_
->AddObserver(this);
263 layer_
->GetCompositor()->AddObserver(this);
265 virtual ~CrossFadeObserver() {
266 window_
->RemoveObserver(this);
268 layer_
->GetCompositor()->RemoveObserver(this);
269 views::corewm::DeepDeleteLayers(layer_
);
273 // ui::CompositorObserver overrides:
274 virtual void OnCompositingDidCommit(ui::Compositor
* compositor
) OVERRIDE
{
276 virtual void OnCompositingStarted(ui::Compositor
* compositor
,
277 base::TimeTicks start_time
) OVERRIDE
{
279 virtual void OnCompositingEnded(ui::Compositor
* compositor
) OVERRIDE
{
281 virtual void OnCompositingAborted(ui::Compositor
* compositor
) OVERRIDE
{
282 // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
283 layer_
->GetAnimator()->StopAnimating();
285 virtual void OnCompositingLockStateChanged(
286 ui::Compositor
* compositor
) OVERRIDE
{
288 virtual void OnUpdateVSyncParameters(ui::Compositor
* compositor
,
289 base::TimeTicks timebase
,
290 base::TimeDelta interval
) OVERRIDE
{
293 // aura::WindowObserver overrides:
294 virtual void OnWindowDestroying(aura::Window
* window
) OVERRIDE
{
295 // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
296 layer_
->GetAnimator()->StopAnimating();
298 virtual void OnWindowRemovingFromRootWindow(aura::Window
* window
) OVERRIDE
{
299 layer_
->GetAnimator()->StopAnimating();
302 // ui::ImplicitAnimationObserver overrides:
303 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
308 aura::Window
* window_
; // not owned
309 ui::Layer
* layer_
; // owned
311 DISALLOW_COPY_AND_ASSIGN(CrossFadeObserver
);
314 // Implementation of cross fading. Window is the window being cross faded. It
315 // should be at the target bounds. |old_layer| the previous layer from |window|.
316 // This takes ownership of |old_layer| and deletes when the animation is done.
317 // |pause_duration| is the duration to pause at the current bounds before
318 // animating. Returns the duration of the fade.
319 base::TimeDelta
CrossFadeImpl(aura::Window
* window
,
320 ui::Layer
* old_layer
,
321 gfx::Tween::Type tween_type
) {
322 const gfx::Rect
old_bounds(old_layer
->bounds());
323 const gfx::Rect
new_bounds(window
->bounds());
324 const bool old_on_top
= (old_bounds
.width() > new_bounds
.width());
326 // Shorten the animation if there's not much visual movement.
327 const base::TimeDelta duration
= GetCrossFadeDuration(old_bounds
, new_bounds
);
329 // Scale up the old layer while translating to new position.
331 old_layer
->GetAnimator()->StopAnimating();
332 ui::ScopedLayerAnimationSettings
settings(old_layer
->GetAnimator());
334 // Animation observer owns the old layer and deletes itself.
335 settings
.AddObserver(new CrossFadeObserver(window
, old_layer
));
336 settings
.SetTransitionDuration(duration
);
337 settings
.SetTweenType(tween_type
);
338 gfx::Transform out_transform
;
339 float scale_x
= static_cast<float>(new_bounds
.width()) /
340 static_cast<float>(old_bounds
.width());
341 float scale_y
= static_cast<float>(new_bounds
.height()) /
342 static_cast<float>(old_bounds
.height());
343 out_transform
.Translate(new_bounds
.x() - old_bounds
.x(),
344 new_bounds
.y() - old_bounds
.y());
345 out_transform
.Scale(scale_x
, scale_y
);
346 old_layer
->SetTransform(out_transform
);
348 // The old layer is on top, and should fade out. The new layer below will
349 // stay opaque to block the desktop.
350 old_layer
->SetOpacity(kWindowAnimation_HideOpacity
);
352 // In tests |old_layer| is deleted here, as animations have zero duration.
356 // Set the new layer's current transform, such that the user sees a scaled
357 // version of the window with the original bounds at the original position.
358 gfx::Transform in_transform
;
359 const float scale_x
= static_cast<float>(old_bounds
.width()) /
360 static_cast<float>(new_bounds
.width());
361 const float scale_y
= static_cast<float>(old_bounds
.height()) /
362 static_cast<float>(new_bounds
.height());
363 in_transform
.Translate(old_bounds
.x() - new_bounds
.x(),
364 old_bounds
.y() - new_bounds
.y());
365 in_transform
.Scale(scale_x
, scale_y
);
366 window
->layer()->SetTransform(in_transform
);
368 // The new layer is on top and should fade in. The old layer below will
369 // stay opaque and block the desktop.
370 window
->layer()->SetOpacity(kWindowAnimation_HideOpacity
);
373 // Animate the new layer to the identity transform, so the window goes to
374 // its newly set bounds.
375 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
376 settings
.SetTransitionDuration(duration
);
377 settings
.SetTweenType(tween_type
);
378 window
->layer()->SetTransform(gfx::Transform());
380 // New layer is on top, fade it in.
381 window
->layer()->SetOpacity(kWindowAnimation_ShowOpacity
);
387 void CrossFadeToBounds(aura::Window
* window
, const gfx::Rect
& new_bounds
) {
388 // Some test results in invoking CrossFadeToBounds when window is not visible.
389 // No animation is necessary in that case, thus just change the bounds and
391 if (!window
->TargetVisibility()) {
392 window
->SetBounds(new_bounds
);
396 const gfx::Rect old_bounds
= window
->bounds();
398 // Create fresh layers for the window and all its children to paint into.
399 // Takes ownership of the old layer and all its children, which will be
400 // cleaned up after the animation completes.
401 // Specify |set_bounds| to true here to keep the old bounds in the child
402 // windows of |window|.
403 ui::Layer
* old_layer
= views::corewm::RecreateWindowLayers(window
, true);
404 ui::Layer
* new_layer
= window
->layer();
406 // Resize the window to the new size, which will force a layout and paint.
407 window
->SetBounds(new_bounds
);
409 // Ensure the higher-resolution layer is on top.
410 bool old_on_top
= (old_bounds
.width() > new_bounds
.width());
412 old_layer
->parent()->StackBelow(new_layer
, old_layer
);
414 old_layer
->parent()->StackAbove(new_layer
, old_layer
);
416 CrossFadeImpl(window
, old_layer
, gfx::Tween::EASE_OUT
);
419 void CrossFadeWindowBetweenWorkspaces(aura::Window
* new_workspace
,
420 aura::Window
* window
,
421 ui::Layer
* old_layer
) {
422 ui::Layer
* layer_parent
= new_workspace
->layer()->parent();
423 layer_parent
->Add(old_layer
);
424 const bool restoring
= old_layer
->bounds().width() > window
->bounds().width();
426 layer_parent
->StackAbove(old_layer
, new_workspace
->layer());
428 layer_parent
->StackBelow(old_layer
, new_workspace
->layer());
430 CrossFadeImpl(window
, old_layer
, kCrossFadeTweenType
);
433 base::TimeDelta
GetCrossFadeDuration(const gfx::Rect
& old_bounds
,
434 const gfx::Rect
& new_bounds
) {
435 if (views::corewm::WindowAnimationsDisabled(NULL
))
436 return base::TimeDelta();
438 int old_area
= old_bounds
.width() * old_bounds
.height();
439 int new_area
= new_bounds
.width() * new_bounds
.height();
440 int max_area
= std::max(old_area
, new_area
);
441 // Avoid divide by zero.
443 return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS
);
445 int delta_area
= std::abs(old_area
- new_area
);
446 // If the area didn't change, the animation is instantaneous.
448 return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS
);
451 static_cast<float>(delta_area
) / static_cast<float>(max_area
);
452 const float kRange
= kCrossFadeDurationMaxMs
- kCrossFadeDurationMinMs
;
453 return base::TimeDelta::FromMilliseconds(
454 Round64(kCrossFadeDurationMinMs
+ (factor
* kRange
)));
457 bool AnimateOnChildWindowVisibilityChanged(aura::Window
* window
, bool visible
) {
458 if (views::corewm::WindowAnimationsDisabled(window
))
461 // Attempt to run CoreWm supplied animation types.
462 if (views::corewm::AnimateOnChildWindowVisibilityChanged(window
, visible
))
465 // Otherwise try to run an Ash-specific animation.
467 return AnimateShowWindow(window
);
468 // Don't start hiding the window again if it's already being hidden.
469 return window
->layer()->GetTargetOpacity() != 0.0f
&&
470 AnimateHideWindow(window
);
473 std::vector
<ui::LayerAnimationSequence
*>
474 CreateBrightnessGrayscaleAnimationSequence(float target_value
,
475 base::TimeDelta duration
) {
476 gfx::Tween::Type animation_type
= gfx::Tween::EASE_OUT
;
477 scoped_ptr
<ui::LayerAnimationSequence
> brightness_sequence(
478 new ui::LayerAnimationSequence());
479 scoped_ptr
<ui::LayerAnimationSequence
> grayscale_sequence(
480 new ui::LayerAnimationSequence());
482 scoped_ptr
<ui::LayerAnimationElement
> brightness_element(
483 ui::LayerAnimationElement::CreateBrightnessElement(
484 target_value
, duration
));
485 brightness_element
->set_tween_type(animation_type
);
486 brightness_sequence
->AddElement(brightness_element
.release());
488 scoped_ptr
<ui::LayerAnimationElement
> grayscale_element(
489 ui::LayerAnimationElement::CreateGrayscaleElement(
490 target_value
, duration
));
491 grayscale_element
->set_tween_type(animation_type
);
492 grayscale_sequence
->AddElement(grayscale_element
.release());
494 std::vector
<ui::LayerAnimationSequence
*> animations
;
495 animations
.push_back(brightness_sequence
.release());
496 animations
.push_back(grayscale_sequence
.release());
501 // Returns scale related to the specified AshWindowScaleType.
502 void SetTransformForScaleAnimation(ui::Layer
* layer
,
503 LayerScaleAnimationDirection type
) {
505 type
== LAYER_SCALE_ANIMATION_ABOVE
? kLayerScaleAboveSize
:
506 kLayerScaleBelowSize
;
507 gfx::Transform transform
;
508 transform
.Translate(-layer
->bounds().width() * (scale
- 1.0f
) / 2,
509 -layer
->bounds().height() * (scale
- 1.0f
) / 2);
510 transform
.Scale(scale
, scale
);
511 layer
->SetTransform(transform
);
514 gfx::Rect
GetMinimizeAnimationTargetBoundsInScreen(aura::Window
* window
) {
515 Launcher
* launcher
= Launcher::ForWindow(window
);
516 // Shelf is created lazily and can be NULL.
519 gfx::Rect item_rect
= launcher
->GetScreenBoundsOfItemIconForWindow(window
);
521 // The launcher item is visible and has an icon.
522 if (!item_rect
.IsEmpty())
525 // If both the icon width and height are 0, then there is no icon in the
526 // launcher for |window| or the icon is hidden in the overflow menu. If the
527 // launcher is auto hidden, one of the height or width will be 0 but the
528 // position in the launcher and the major dimension are still reported
529 // correctly and the window can be animated to the launcher item's light
531 if (item_rect
.width() != 0 || item_rect
.height() != 0) {
532 internal::ShelfLayoutManager
* layout_manager
=
533 internal::ShelfLayoutManager::ForLauncher(window
);
534 if (layout_manager
->visibility_state() == SHELF_AUTO_HIDE
) {
535 gfx::Rect shelf_bounds
=
536 launcher
->shelf_widget()->GetWindowBoundsInScreen();
537 switch (layout_manager
->GetAlignment()) {
538 case SHELF_ALIGNMENT_BOTTOM
:
539 item_rect
.set_y(shelf_bounds
.y());
541 case SHELF_ALIGNMENT_LEFT
:
542 item_rect
.set_x(shelf_bounds
.right());
544 case SHELF_ALIGNMENT_RIGHT
:
545 item_rect
.set_x(shelf_bounds
.x());
547 case SHELF_ALIGNMENT_TOP
:
548 item_rect
.set_y(shelf_bounds
.bottom());
555 // Assume the launcher is overflowed, zoom off to the bottom right of the
557 gfx::Rect work_area
=
558 Shell::GetScreen()->GetDisplayNearestWindow(window
).work_area();
559 return gfx::Rect(work_area
.right(), work_area
.bottom(), 0, 0);