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/screen_util.h"
13 #include "ash/shelf/shelf.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/layer_tree_owner.h"
35 #include "ui/compositor/scoped_layer_animation_settings.h"
36 #include "ui/gfx/interpolated_transform.h"
37 #include "ui/gfx/screen.h"
38 #include "ui/gfx/vector3d_f.h"
39 #include "ui/views/view.h"
40 #include "ui/views/widget/widget.h"
41 #include "ui/wm/core/window_util.h"
45 const int kLayerAnimationsForMinimizeDurationMS
= 200;
47 // Durations for the cross-fade animation, in milliseconds.
48 const float kCrossFadeDurationMinMs
= 200.f
;
49 const float kCrossFadeDurationMaxMs
= 400.f
;
51 // Durations for the brightness/grayscale fade animation, in milliseconds.
52 const int kBrightnessGrayscaleFadeDurationMs
= 1000;
54 // Brightness/grayscale values for hide/show window animations.
55 const float kWindowAnimation_HideBrightnessGrayscale
= 1.f
;
56 const float kWindowAnimation_ShowBrightnessGrayscale
= 0.f
;
58 const float kWindowAnimation_HideOpacity
= 0.f
;
59 const float kWindowAnimation_ShowOpacity
= 1.f
;
61 // Scales for AshWindow above/below current workspace.
62 const float kLayerScaleAboveSize
= 1.1f
;
63 const float kLayerScaleBelowSize
= .9f
;
65 int64
Round64(float f
) {
66 return static_cast<int64
>(f
+ 0.5f
);
69 base::TimeDelta
GetCrossFadeDuration(aura::Window
* window
,
70 const gfx::Rect
& old_bounds
,
71 const gfx::Rect
& new_bounds
) {
72 if (::wm::WindowAnimationsDisabled(window
))
73 return base::TimeDelta();
75 int old_area
= old_bounds
.width() * old_bounds
.height();
76 int new_area
= new_bounds
.width() * new_bounds
.height();
77 int max_area
= std::max(old_area
, new_area
);
78 // Avoid divide by zero.
80 return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS
);
82 int delta_area
= std::abs(old_area
- new_area
);
83 // If the area didn't change, the animation is instantaneous.
85 return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS
);
88 static_cast<float>(delta_area
) / static_cast<float>(max_area
);
89 const float kRange
= kCrossFadeDurationMaxMs
- kCrossFadeDurationMinMs
;
90 return base::TimeDelta::FromMilliseconds(
91 Round64(kCrossFadeDurationMinMs
+ (factor
* kRange
)));
96 const int kCrossFadeDurationMS
= 200;
98 void AddLayerAnimationsForMinimize(aura::Window
* window
, bool show
) {
99 // Recalculate the transform at restore time since the launcher item may have
100 // moved while the window was minimized.
101 gfx::Rect bounds
= window
->bounds();
102 gfx::Rect target_bounds
= GetMinimizeAnimationTargetBoundsInScreen(window
);
104 ScreenUtil::ConvertRectFromScreen(window
->parent(), target_bounds
);
106 float scale_x
= static_cast<float>(target_bounds
.width()) / bounds
.width();
107 float scale_y
= static_cast<float>(target_bounds
.height()) / bounds
.height();
109 scoped_ptr
<ui::InterpolatedTransform
> scale(
110 new ui::InterpolatedScale(gfx::Point3F(1, 1, 1),
111 gfx::Point3F(scale_x
, scale_y
, 1)));
113 scoped_ptr
<ui::InterpolatedTransform
> translation(
114 new ui::InterpolatedTranslation(
116 gfx::Point(target_bounds
.x() - bounds
.x(),
117 target_bounds
.y() - bounds
.y())));
119 scale
->SetChild(translation
.release());
120 scale
->SetReversed(show
);
122 base::TimeDelta duration
= window
->layer()->GetAnimator()->
123 GetTransitionDuration();
125 scoped_ptr
<ui::LayerAnimationElement
> transition(
126 ui::LayerAnimationElement::CreateInterpolatedTransformElement(
127 scale
.release(), duration
));
129 transition
->set_tween_type(
130 show
? gfx::Tween::EASE_IN
: gfx::Tween::EASE_IN_OUT
);
132 window
->layer()->GetAnimator()->ScheduleAnimation(
133 new ui::LayerAnimationSequence(transition
.release()));
135 // When hiding a window, turn off blending until the animation is 3 / 4 done
136 // to save bandwidth and reduce jank.
138 window
->layer()->GetAnimator()->SchedulePauseForProperties(
139 (duration
* 3) / 4, ui::LayerAnimationElement::OPACITY
);
142 // Fade in and out quickly when the window is small to reduce jank.
143 float opacity
= show
? 1.0f
: 0.0f
;
144 window
->layer()->GetAnimator()->ScheduleAnimation(
145 new ui::LayerAnimationSequence(
146 ui::LayerAnimationElement::CreateOpacityElement(
147 opacity
, duration
/ 4)));
149 // Reset the transform to identity when the minimize animation is completed.
150 window
->layer()->GetAnimator()->ScheduleAnimation(
151 new ui::LayerAnimationSequence(
152 ui::LayerAnimationElement::CreateTransformElement(
154 base::TimeDelta())));
157 void AnimateShowWindow_Minimize(aura::Window
* window
) {
158 window
->layer()->SetOpacity(kWindowAnimation_HideOpacity
);
159 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
160 base::TimeDelta duration
= base::TimeDelta::FromMilliseconds(
161 kLayerAnimationsForMinimizeDurationMS
);
162 settings
.SetTransitionDuration(duration
);
163 AddLayerAnimationsForMinimize(window
, true);
165 // Now that the window has been restored, we need to clear its animation style
166 // to default so that normal animation applies.
167 ::wm::SetWindowVisibilityAnimationType(
168 window
, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT
);
171 void AnimateHideWindow_Minimize(aura::Window
* window
) {
172 // Property sets within this scope will be implicitly animated.
173 ::wm::ScopedHidingAnimationSettings
hiding_settings(window
);
174 base::TimeDelta duration
= base::TimeDelta::FromMilliseconds(
175 kLayerAnimationsForMinimizeDurationMS
);
176 hiding_settings
.layer_animation_settings()->SetTransitionDuration(duration
);
177 window
->layer()->SetVisible(false);
179 AddLayerAnimationsForMinimize(window
, false);
182 void AnimateShowHideWindowCommon_BrightnessGrayscale(aura::Window
* window
,
184 float start_value
, end_value
;
186 start_value
= kWindowAnimation_HideBrightnessGrayscale
;
187 end_value
= kWindowAnimation_ShowBrightnessGrayscale
;
189 start_value
= kWindowAnimation_ShowBrightnessGrayscale
;
190 end_value
= kWindowAnimation_HideBrightnessGrayscale
;
193 window
->layer()->SetLayerBrightness(start_value
);
194 window
->layer()->SetLayerGrayscale(start_value
);
196 window
->layer()->SetOpacity(kWindowAnimation_ShowOpacity
);
197 window
->layer()->SetVisible(true);
200 base::TimeDelta duration
=
201 base::TimeDelta::FromMilliseconds(kBrightnessGrayscaleFadeDurationMs
);
204 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
205 window
->layer()->GetAnimator()->
207 CreateBrightnessGrayscaleAnimationSequence(end_value
, duration
));
209 ::wm::ScopedHidingAnimationSettings
hiding_settings(window
);
210 window
->layer()->GetAnimator()->
212 CreateBrightnessGrayscaleAnimationSequence(end_value
, duration
));
213 window
->layer()->SetOpacity(kWindowAnimation_HideOpacity
);
214 window
->layer()->SetVisible(false);
218 void AnimateShowWindow_BrightnessGrayscale(aura::Window
* window
) {
219 AnimateShowHideWindowCommon_BrightnessGrayscale(window
, true);
222 void AnimateHideWindow_BrightnessGrayscale(aura::Window
* window
) {
223 AnimateShowHideWindowCommon_BrightnessGrayscale(window
, false);
226 bool AnimateShowWindow(aura::Window
* window
) {
227 if (!::wm::HasWindowVisibilityAnimationTransition(
228 window
, ::wm::ANIMATE_SHOW
)) {
232 switch (::wm::GetWindowVisibilityAnimationType(window
)) {
233 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
:
234 AnimateShowWindow_Minimize(window
);
236 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE
:
237 AnimateShowWindow_BrightnessGrayscale(window
);
245 bool AnimateHideWindow(aura::Window
* window
) {
246 if (!::wm::HasWindowVisibilityAnimationTransition(
247 window
, ::wm::ANIMATE_HIDE
)) {
251 switch (::wm::GetWindowVisibilityAnimationType(window
)) {
252 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
:
253 AnimateHideWindow_Minimize(window
);
255 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE
:
256 AnimateHideWindow_BrightnessGrayscale(window
);
264 // Observer for a window cross-fade animation. If either the window closes or
265 // the layer's animation completes or compositing is aborted due to GPU crash,
266 // it deletes the layer and removes itself as an observer.
267 class CrossFadeObserver
: public ui::CompositorObserver
,
268 public aura::WindowObserver
,
269 public ui::ImplicitAnimationObserver
{
271 // Observes |window| for destruction, but does not take ownership.
272 // Takes ownership of |layer| and its child layers.
273 CrossFadeObserver(aura::Window
* window
,
274 scoped_ptr
<ui::LayerTreeOwner
> layer_owner
)
276 layer_owner_(layer_owner
.Pass()) {
277 window_
->AddObserver(this);
278 layer_owner_
->root()->GetCompositor()->AddObserver(this);
280 virtual ~CrossFadeObserver() {
281 window_
->RemoveObserver(this);
283 layer_owner_
->root()->GetCompositor()->RemoveObserver(this);
286 // ui::CompositorObserver overrides:
287 virtual void OnCompositingDidCommit(ui::Compositor
* compositor
) OVERRIDE
{
289 virtual void OnCompositingStarted(ui::Compositor
* compositor
,
290 base::TimeTicks start_time
) OVERRIDE
{
292 virtual void OnCompositingEnded(ui::Compositor
* compositor
) OVERRIDE
{
294 virtual void OnCompositingAborted(ui::Compositor
* compositor
) OVERRIDE
{
295 // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
296 layer_owner_
->root()->GetAnimator()->StopAnimating();
298 virtual void OnCompositingLockStateChanged(
299 ui::Compositor
* compositor
) OVERRIDE
{
302 // aura::WindowObserver overrides:
303 virtual void OnWindowDestroying(aura::Window
* window
) OVERRIDE
{
304 // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
305 layer_owner_
->root()->GetAnimator()->StopAnimating();
307 virtual void OnWindowRemovingFromRootWindow(aura::Window
* window
,
308 aura::Window
* new_root
) OVERRIDE
{
309 layer_owner_
->root()->GetAnimator()->StopAnimating();
312 // ui::ImplicitAnimationObserver overrides:
313 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
318 aura::Window
* window_
; // not owned
319 scoped_ptr
<ui::LayerTreeOwner
> layer_owner_
;
321 DISALLOW_COPY_AND_ASSIGN(CrossFadeObserver
);
324 base::TimeDelta
CrossFadeAnimation(
325 aura::Window
* window
,
326 scoped_ptr
<ui::LayerTreeOwner
> old_layer_owner
,
327 gfx::Tween::Type tween_type
) {
328 DCHECK(old_layer_owner
->root());
329 const gfx::Rect
old_bounds(old_layer_owner
->root()->bounds());
330 const gfx::Rect
new_bounds(window
->bounds());
331 const bool old_on_top
= (old_bounds
.width() > new_bounds
.width());
333 // Shorten the animation if there's not much visual movement.
334 const base::TimeDelta duration
= GetCrossFadeDuration(window
,
335 old_bounds
, new_bounds
);
337 // Scale up the old layer while translating to new position.
339 ui::Layer
* old_layer
= old_layer_owner
->root();
340 old_layer
->GetAnimator()->StopAnimating();
341 ui::ScopedLayerAnimationSettings
settings(old_layer
->GetAnimator());
343 // Animation observer owns the old layer and deletes itself.
344 settings
.AddObserver(new CrossFadeObserver(window
, old_layer_owner
.Pass()));
345 settings
.SetTransitionDuration(duration
);
346 settings
.SetTweenType(tween_type
);
347 gfx::Transform out_transform
;
348 float scale_x
= static_cast<float>(new_bounds
.width()) /
349 static_cast<float>(old_bounds
.width());
350 float scale_y
= static_cast<float>(new_bounds
.height()) /
351 static_cast<float>(old_bounds
.height());
352 out_transform
.Translate(new_bounds
.x() - old_bounds
.x(),
353 new_bounds
.y() - old_bounds
.y());
354 out_transform
.Scale(scale_x
, scale_y
);
355 old_layer
->SetTransform(out_transform
);
357 // The old layer is on top, and should fade out. The new layer below will
358 // stay opaque to block the desktop.
359 old_layer
->SetOpacity(kWindowAnimation_HideOpacity
);
361 // In tests |old_layer| is deleted here, as animations have zero duration.
365 // Set the new layer's current transform, such that the user sees a scaled
366 // version of the window with the original bounds at the original position.
367 gfx::Transform in_transform
;
368 const float scale_x
= static_cast<float>(old_bounds
.width()) /
369 static_cast<float>(new_bounds
.width());
370 const float scale_y
= static_cast<float>(old_bounds
.height()) /
371 static_cast<float>(new_bounds
.height());
372 in_transform
.Translate(old_bounds
.x() - new_bounds
.x(),
373 old_bounds
.y() - new_bounds
.y());
374 in_transform
.Scale(scale_x
, scale_y
);
375 window
->layer()->SetTransform(in_transform
);
377 // The new layer is on top and should fade in. The old layer below will
378 // stay opaque and block the desktop.
379 window
->layer()->SetOpacity(kWindowAnimation_HideOpacity
);
382 // Animate the new layer to the identity transform, so the window goes to
383 // its newly set bounds.
384 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
385 settings
.SetTransitionDuration(duration
);
386 settings
.SetTweenType(tween_type
);
387 window
->layer()->SetTransform(gfx::Transform());
389 // New layer is on top, fade it in.
390 window
->layer()->SetOpacity(kWindowAnimation_ShowOpacity
);
396 bool AnimateOnChildWindowVisibilityChanged(aura::Window
* window
, bool visible
) {
397 if (::wm::WindowAnimationsDisabled(window
))
400 // Attempt to run CoreWm supplied animation types.
401 if (::wm::AnimateOnChildWindowVisibilityChanged(window
, visible
))
404 // Otherwise try to run an Ash-specific animation.
406 return AnimateShowWindow(window
);
407 // Don't start hiding the window again if it's already being hidden.
408 return window
->layer()->GetTargetOpacity() != 0.0f
&&
409 AnimateHideWindow(window
);
412 std::vector
<ui::LayerAnimationSequence
*>
413 CreateBrightnessGrayscaleAnimationSequence(float target_value
,
414 base::TimeDelta duration
) {
415 gfx::Tween::Type animation_type
= gfx::Tween::EASE_OUT
;
416 scoped_ptr
<ui::LayerAnimationSequence
> brightness_sequence(
417 new ui::LayerAnimationSequence());
418 scoped_ptr
<ui::LayerAnimationSequence
> grayscale_sequence(
419 new ui::LayerAnimationSequence());
421 scoped_ptr
<ui::LayerAnimationElement
> brightness_element(
422 ui::LayerAnimationElement::CreateBrightnessElement(
423 target_value
, duration
));
424 brightness_element
->set_tween_type(animation_type
);
425 brightness_sequence
->AddElement(brightness_element
.release());
427 scoped_ptr
<ui::LayerAnimationElement
> grayscale_element(
428 ui::LayerAnimationElement::CreateGrayscaleElement(
429 target_value
, duration
));
430 grayscale_element
->set_tween_type(animation_type
);
431 grayscale_sequence
->AddElement(grayscale_element
.release());
433 std::vector
<ui::LayerAnimationSequence
*> animations
;
434 animations
.push_back(brightness_sequence
.release());
435 animations
.push_back(grayscale_sequence
.release());
440 // Returns scale related to the specified AshWindowScaleType.
441 void SetTransformForScaleAnimation(ui::Layer
* layer
,
442 LayerScaleAnimationDirection type
) {
444 type
== LAYER_SCALE_ANIMATION_ABOVE
? kLayerScaleAboveSize
:
445 kLayerScaleBelowSize
;
446 gfx::Transform transform
;
447 transform
.Translate(-layer
->bounds().width() * (scale
- 1.0f
) / 2,
448 -layer
->bounds().height() * (scale
- 1.0f
) / 2);
449 transform
.Scale(scale
, scale
);
450 layer
->SetTransform(transform
);
453 gfx::Rect
GetMinimizeAnimationTargetBoundsInScreen(aura::Window
* window
) {
454 Shelf
* shelf
= Shelf::ForWindow(window
);
455 // Shelf is created lazily and can be NULL.
458 gfx::Rect item_rect
= shelf
->GetScreenBoundsOfItemIconForWindow(window
);
460 // The launcher item is visible and has an icon.
461 if (!item_rect
.IsEmpty())
464 // If both the icon width and height are 0, then there is no icon in the
465 // launcher for |window|. If the launcher is auto hidden, one of the height or
466 // width will be 0 but the position in the launcher and the major dimension
467 // are still reported correctly and the window can be animated to the launcher
469 ShelfLayoutManager
* layout_manager
= ShelfLayoutManager::ForShelf(window
);
470 if (item_rect
.width() != 0 || item_rect
.height() != 0) {
471 if (layout_manager
->visibility_state() == SHELF_AUTO_HIDE
) {
472 gfx::Rect shelf_bounds
= shelf
->shelf_widget()->GetWindowBoundsInScreen();
473 switch (layout_manager
->GetAlignment()) {
474 case SHELF_ALIGNMENT_BOTTOM
:
475 item_rect
.set_y(shelf_bounds
.y());
477 case SHELF_ALIGNMENT_LEFT
:
478 item_rect
.set_x(shelf_bounds
.right());
480 case SHELF_ALIGNMENT_RIGHT
:
481 item_rect
.set_x(shelf_bounds
.x());
483 case SHELF_ALIGNMENT_TOP
:
484 item_rect
.set_y(shelf_bounds
.bottom());
491 // Coming here, there is no visible icon of that shelf item and we zoom back
492 // to the location of the application launcher (which is fixed as first item
494 gfx::Rect work_area
=
495 Shell::GetScreen()->GetDisplayNearestWindow(window
).work_area();
496 int ltr_adjusted_x
= base::i18n::IsRTL() ? work_area
.right() : work_area
.x();
497 switch (layout_manager
->GetAlignment()) {
498 case SHELF_ALIGNMENT_BOTTOM
:
499 return gfx::Rect(ltr_adjusted_x
, work_area
.bottom(), 0, 0);
500 case SHELF_ALIGNMENT_TOP
:
501 return gfx::Rect(ltr_adjusted_x
, work_area
.y(), 0, 0);
502 case SHELF_ALIGNMENT_LEFT
:
503 return gfx::Rect(work_area
.x(), work_area
.y(), 0, 0);
504 case SHELF_ALIGNMENT_RIGHT
:
505 return gfx::Rect(work_area
.right(), work_area
.y(), 0, 0);