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"
7 #include "ash/ash_switches.h"
8 #include "ash/launcher/launcher.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/logging.h"
13 #include "base/message_loop.h"
14 #include "base/stl_util.h"
15 #include "base/time.h"
16 #include "ui/aura/client/aura_constants.h"
17 #include "ui/aura/window.h"
18 #include "ui/aura/window_observer.h"
19 #include "ui/aura/window_property.h"
20 #include "ui/gfx/compositor/layer_animation_observer.h"
21 #include "ui/gfx/compositor/layer_animation_sequence.h"
22 #include "ui/gfx/compositor/layer_animator.h"
23 #include "ui/gfx/compositor/scoped_layer_animation_settings.h"
24 #include "ui/gfx/interpolated_transform.h"
25 #include "ui/gfx/screen.h"
27 DECLARE_WINDOW_PROPERTY_TYPE(int)
28 DECLARE_WINDOW_PROPERTY_TYPE(ash::WindowVisibilityAnimationType
)
29 DECLARE_WINDOW_PROPERTY_TYPE(ash::WindowVisibilityAnimationTransition
)
31 using base::TimeDelta
;
35 DEFINE_WINDOW_PROPERTY_KEY(WindowVisibilityAnimationType
,
36 kWindowVisibilityAnimationTypeKey
,
37 WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT
);
38 DEFINE_WINDOW_PROPERTY_KEY(int, kWindowVisibilityAnimationDurationKey
, 0);
39 DEFINE_WINDOW_PROPERTY_KEY(WindowVisibilityAnimationTransition
,
40 kWindowVisibilityAnimationTransitionKey
,
42 } // namespace internal
44 void SetWindowVisibilityAnimationType(aura::Window
* window
,
45 WindowVisibilityAnimationType type
) {
46 window
->SetProperty(internal::kWindowVisibilityAnimationTypeKey
, type
);
49 void SetWindowVisibilityAnimationTransition(
51 WindowVisibilityAnimationTransition transition
) {
52 window
->SetProperty(internal::kWindowVisibilityAnimationTransitionKey
,
56 void SetWindowVisibilityAnimationDuration(aura::Window
* window
,
57 const TimeDelta
& duration
) {
58 window
->SetProperty(internal::kWindowVisibilityAnimationDurationKey
,
59 static_cast<int>(duration
.ToInternalValue()));
62 bool HasWindowVisibilityAnimationTransition(
64 WindowVisibilityAnimationTransition transition
) {
65 WindowVisibilityAnimationTransition prop
= window
->GetProperty(
66 internal::kWindowVisibilityAnimationTransitionKey
);
67 return (prop
& transition
) != 0;
73 const float kWindowAnimation_HideOpacity
= 0.f
;
74 const float kWindowAnimation_ShowOpacity
= 1.f
;
75 const float kWindowAnimation_TranslateFactor
= -0.025f
;
76 const float kWindowAnimation_ScaleFactor
= 1.05f
;
77 const float kWindowAnimation_MinimizeRotate
= -5.f
;
79 const float kWindowAnimation_Vertical_TranslateY
= 15.f
;
81 // Amount windows are scaled during workspace animations.
82 const float kWorkspaceScale
= .95f
;
84 // Gets/sets the WindowVisibilityAnimationType associated with a window.
85 WindowVisibilityAnimationType
GetWindowVisibilityAnimationType(
86 aura::Window
* window
) {
87 WindowVisibilityAnimationType type
=
88 window
->GetProperty(kWindowVisibilityAnimationTypeKey
);
89 if (type
== WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT
) {
90 return (window
->type() == aura::client::WINDOW_TYPE_MENU
||
91 window
->type() == aura::client::WINDOW_TYPE_TOOLTIP
) ?
92 WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
:
93 WINDOW_VISIBILITY_ANIMATION_TYPE_DROP
;
98 // Observes a hide animation.
99 // A window can be hidden for a variety of reasons. Sometimes, Hide() will be
100 // called and life is simple. Sometimes, the window is actually bound to a
101 // views::Widget and that Widget is closed, and life is a little more
102 // complicated. When a Widget is closed the aura::Window* is actually not
103 // destroyed immediately - it is actually just immediately hidden and then
104 // destroyed when the stack unwinds. To handle this case, we start the hide
105 // animation immediately when the window is hidden, then when the window is
106 // subsequently destroyed this object acquires ownership of the window's layer,
107 // so that it can continue animating it until the animation completes.
108 // Regardless of whether or not the window is destroyed, this object deletes
109 // itself when the animation completes.
110 class HidingWindowAnimationObserver
: public ui::ImplicitAnimationObserver
,
111 public aura::WindowObserver
{
113 explicit HidingWindowAnimationObserver(aura::Window
* window
)
115 window_
->AddObserver(this);
117 virtual ~HidingWindowAnimationObserver() {
118 STLDeleteElements(&layers_
);
122 // Overridden from ui::ImplicitAnimationObserver:
123 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
124 // Restore the correct visibility value (overridden for the duration of the
125 // animation in AnimateHideWindow()).
126 layer()->SetVisible(false);
127 // Window may have been destroyed by this point.
129 window_
->RemoveObserver(this);
130 MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
133 // Overridden from aura::WindowObserver:
134 virtual void OnWindowDestroying(aura::Window
* window
) OVERRIDE
{
135 DCHECK_EQ(window
, window_
);
136 DCHECK(layers_
.empty());
137 AcquireAllLayers(window_
);
138 window_
->RemoveObserver(this);
142 void AcquireAllLayers(aura::Window
* window
) {
143 ui::Layer
* layer
= window
->AcquireLayer();
145 layers_
.push_back(layer
);
146 for (aura::Window::Windows::const_iterator it
= window
->children().begin();
147 it
!= window
->children().end();
149 AcquireAllLayers(*it
);
152 ui::Layer
* layer() { return window_
? window_
->layer() : layers_
[0]; }
154 aura::Window
* window_
;
155 std::vector
<ui::Layer
*> layers_
;
157 DISALLOW_COPY_AND_ASSIGN(HidingWindowAnimationObserver
);
160 // ImplicitAnimationObserver used when switching workspaces. Resets the layer
161 // visibility to 'false' when done. This doesn't need the complexity of
162 // HidingWindowAnimationObserver as the window isn't closing, and if it does a
163 // HidingWindowAnimationObserver will be created.
164 class WorkspaceHidingWindowAnimationObserver
:
165 public ui::ImplicitAnimationObserver
{
167 explicit WorkspaceHidingWindowAnimationObserver(aura::Window
* window
)
168 : layer_(window
->layer()) {
170 virtual ~WorkspaceHidingWindowAnimationObserver() {
174 // Overridden from ui::ImplicitAnimationObserver:
175 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
176 // Restore the correct visibility value (overridden for the duration of the
177 // animation in AnimateHideWindow()).
178 layer_
->SetVisible(false);
183 DISALLOW_COPY_AND_ASSIGN(WorkspaceHidingWindowAnimationObserver
);
186 // Shows a window using an animation, animating its opacity from 0.f to 1.f, and
187 // its transform from |start_transform| to |end_transform|.
188 void AnimateShowWindowCommon(aura::Window
* window
,
189 const ui::Transform
& start_transform
,
190 const ui::Transform
& end_transform
) {
191 window
->layer()->set_delegate(window
);
192 window
->layer()->SetOpacity(kWindowAnimation_HideOpacity
);
193 window
->layer()->SetTransform(start_transform
);
196 // Property sets within this scope will be implicitly animated.
197 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
199 window
->GetProperty(internal::kWindowVisibilityAnimationDurationKey
);
201 settings
.SetTransitionDuration(TimeDelta::FromInternalValue(duration
));
203 window
->layer()->SetTransform(end_transform
);
204 window
->layer()->SetOpacity(kWindowAnimation_ShowOpacity
);
208 // Hides a window using an animation, animating its opacity from 1.f to 0.f, and
209 // its transform to |end_transform|.
210 void AnimateHideWindowCommon(aura::Window
* window
,
211 const ui::Transform
& end_transform
) {
212 window
->layer()->set_delegate(NULL
);
214 // Property sets within this scope will be implicitly animated.
215 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
216 settings
.AddObserver(new HidingWindowAnimationObserver(window
));
219 window
->GetProperty(internal::kWindowVisibilityAnimationDurationKey
);
221 settings
.SetTransitionDuration(TimeDelta::FromInternalValue(duration
));
223 window
->layer()->SetOpacity(kWindowAnimation_HideOpacity
);
224 window
->layer()->SetTransform(end_transform
);
227 // Show/Hide windows using a shrink animation.
228 void AnimateShowWindow_Drop(aura::Window
* window
) {
229 ui::Transform transform
;
230 transform
.ConcatScale(kWindowAnimation_ScaleFactor
,
231 kWindowAnimation_ScaleFactor
);
232 transform
.ConcatTranslate(
233 kWindowAnimation_TranslateFactor
* window
->bounds().width(),
234 kWindowAnimation_TranslateFactor
* window
->bounds().height());
235 AnimateShowWindowCommon(window
, transform
, ui::Transform());
238 void AnimateHideWindow_Drop(aura::Window
* window
) {
239 ui::Transform transform
;
240 transform
.ConcatScale(kWindowAnimation_ScaleFactor
,
241 kWindowAnimation_ScaleFactor
);
242 transform
.ConcatTranslate(
243 kWindowAnimation_TranslateFactor
* window
->bounds().width(),
244 kWindowAnimation_TranslateFactor
* window
->bounds().height());
245 AnimateHideWindowCommon(window
, transform
);
248 // Show/Hide windows using a vertical Glenimation.
249 void AnimateShowWindow_Vertical(aura::Window
* window
) {
250 ui::Transform transform
;
251 transform
.ConcatTranslate(0, kWindowAnimation_Vertical_TranslateY
);
252 AnimateShowWindowCommon(window
, transform
, ui::Transform());
255 void AnimateHideWindow_Vertical(aura::Window
* window
) {
256 ui::Transform transform
;
257 transform
.ConcatTranslate(0, kWindowAnimation_Vertical_TranslateY
);
258 AnimateHideWindowCommon(window
, transform
);
261 // Show/Hide windows using a fade.
262 void AnimateShowWindow_Fade(aura::Window
* window
) {
263 AnimateShowWindowCommon(window
, ui::Transform(), ui::Transform());
266 void AnimateHideWindow_Fade(aura::Window
* window
) {
267 AnimateHideWindowCommon(window
, ui::Transform());
270 // Builds the transform used when switching workspaces for the specified
272 ui::Transform
BuildWorkspaceSwitchTransform(aura::Window
* window
) {
273 // Animations for transitioning workspaces scale all windows. To give the
274 // effect of scaling from the center of the screen the windows are translated.
275 gfx::Rect
parent_bounds(window
->parent()->bounds());
276 float mid_x
= static_cast<float>(parent_bounds
.width()) / 2.0f
;
278 (static_cast<float>(window
->bounds().x()) - mid_x
) * kWorkspaceScale
+
280 float mid_y
= static_cast<float>(parent_bounds
.height()) / 2.0f
;
282 (static_cast<float>(window
->bounds().y()) - mid_y
) * kWorkspaceScale
+
285 ui::Transform transform
;
286 transform
.ConcatTranslate(
287 initial_x
- static_cast<float>(window
->bounds().x()),
288 initial_y
- static_cast<float>(window
->bounds().y()));
289 transform
.ConcatScale(kWorkspaceScale
, kWorkspaceScale
);
293 void AnimateShowWindow_Workspace(aura::Window
* window
) {
294 ui::Transform
transform(BuildWorkspaceSwitchTransform(window
));
295 window
->layer()->SetVisible(true);
296 window
->layer()->SetOpacity(0.0f
);
297 window
->layer()->SetTransform(transform
);
300 // Property sets within this scope will be implicitly animated.
301 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
303 window
->layer()->SetTransform(ui::Transform());
304 // Opacity animates only during the first half of the animation.
305 settings
.SetTransitionDuration(settings
.GetTransitionDuration() / 2);
306 window
->layer()->SetOpacity(1.0f
);
310 void AnimateHideWindow_Workspace(aura::Window
* window
) {
311 ui::Transform
transform(BuildWorkspaceSwitchTransform(window
));
312 window
->layer()->SetOpacity(1.0f
);
313 window
->layer()->SetTransform(ui::Transform());
315 // Opacity animates from 1 to 0 only over the second half of the animation. To
316 // get this functionality two animations are schedule for opacity, the first
317 // from 1 to 1 (which effectively does nothing) the second from 1 to 0.
318 // Because we're scheduling two animations of the same property we need to
319 // change the preemption strategy.
320 ui::LayerAnimator
* animator
= window
->layer()->GetAnimator();
321 animator
->set_preemption_strategy(ui::LayerAnimator::ENQUEUE_NEW_ANIMATION
);
323 // Property sets within this scope will be implicitly animated.
324 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
325 // Add an observer that sets visibility of the layer to false once animation
327 settings
.AddObserver(new WorkspaceHidingWindowAnimationObserver(window
));
328 window
->layer()->SetTransform(transform
);
329 settings
.SetTransitionDuration(settings
.GetTransitionDuration() / 2);
330 window
->layer()->SetOpacity(1.0f
);
331 window
->layer()->SetOpacity(0.0f
);
333 animator
->set_preemption_strategy(
334 ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET
);
337 gfx::Rect
GetMinimizeRectForWindow(aura::Window
* window
) {
338 gfx::Rect target_bounds
= Shell::GetInstance()->launcher()->
339 GetScreenBoundsOfItemIconForWindow(window
);
340 if (target_bounds
.IsEmpty()) {
341 // Assume the launcher is overflowed, zoom off to the bottom right of the
343 gfx::Rect work_area
= gfx::Screen::GetMonitorWorkAreaNearestWindow(window
);
344 target_bounds
.SetRect(work_area
.right(), work_area
.bottom(), 0, 0);
346 return target_bounds
;
349 void AddLayerAnimationsForMinimize(aura::Window
* window
, bool show
) {
350 // Recalculate the transform at restore time since the launcher item may have
351 // moved while the window was minimized.
352 gfx::Rect bounds
= window
->bounds();
353 gfx::Rect target_bounds
= GetMinimizeRectForWindow(window
);
354 float scale_x
= static_cast<float>(target_bounds
.height()) / bounds
.width();
355 float scale_y
= static_cast<float>(target_bounds
.width()) / bounds
.height();
357 scoped_ptr
<ui::InterpolatedTransform
> scale(
358 new ui::InterpolatedScale(gfx::Point3f(1, 1, 1),
359 gfx::Point3f(scale_x
, scale_y
, 1)));
361 scoped_ptr
<ui::InterpolatedTransform
> translation(
362 new ui::InterpolatedTranslation(
364 gfx::Point(target_bounds
.x() - bounds
.x(),
365 target_bounds
.y() - bounds
.y())));
367 scoped_ptr
<ui::InterpolatedTransform
> rotation(
368 new ui::InterpolatedRotation(0, kWindowAnimation_MinimizeRotate
));
370 scoped_ptr
<ui::InterpolatedTransform
> rotation_about_pivot(
371 new ui::InterpolatedTransformAboutPivot(
372 gfx::Point(bounds
.width() * 0.5, bounds
.height() * 0.5),
373 rotation
.release()));
375 scale
->SetChild(translation
.release());
376 rotation_about_pivot
->SetChild(scale
.release());
378 rotation_about_pivot
->SetReversed(show
);
380 base::TimeDelta duration
= base::TimeDelta::FromMilliseconds(350);
382 scoped_ptr
<ui::LayerAnimationElement
> transition(
383 ui::LayerAnimationElement::CreateInterpolatedTransformElement(
384 rotation_about_pivot
.release(), duration
));
386 transition
->set_tween_type(
387 show
? ui::Tween::EASE_IN
: ui::Tween::EASE_IN_OUT
);
389 window
->layer()->GetAnimator()->ScheduleAnimation(
390 new ui::LayerAnimationSequence(transition
.release()));
392 bool opacity
= show
? 1.0f
: 0.0f
;
393 window
->layer()->GetAnimator()->ScheduleAnimation(
394 new ui::LayerAnimationSequence(
395 ui::LayerAnimationElement::CreateOpacityElement(opacity
, duration
)));
398 void AnimateShowWindow_Minimize(aura::Window
* window
) {
399 window
->layer()->set_delegate(window
);
400 window
->layer()->SetOpacity(kWindowAnimation_HideOpacity
);
401 AddLayerAnimationsForMinimize(window
, true);
403 // Now that the window has been restored, we need to clear its animation style
404 // to default so that normal animation applies.
405 SetWindowVisibilityAnimationType(
406 window
, WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT
);
409 void AnimateHideWindow_Minimize(aura::Window
* window
) {
410 window
->layer()->set_delegate(NULL
);
412 // Property sets within this scope will be implicitly animated.
413 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
414 settings
.AddObserver(new HidingWindowAnimationObserver(window
));
416 AddLayerAnimationsForMinimize(window
, false);
419 bool AnimateShowWindow(aura::Window
* window
) {
420 if (!HasWindowVisibilityAnimationTransition(window
, ANIMATE_SHOW
))
423 switch (GetWindowVisibilityAnimationType(window
)) {
424 case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP
:
425 AnimateShowWindow_Drop(window
);
427 case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL
:
428 AnimateShowWindow_Vertical(window
);
430 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
:
431 AnimateShowWindow_Fade(window
);
433 case WINDOW_VISIBILITY_ANIMATION_TYPE_WORKSPACE_SHOW
:
434 AnimateShowWindow_Workspace(window
);
436 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
:
437 AnimateShowWindow_Minimize(window
);
445 bool AnimateHideWindow(aura::Window
* window
) {
446 if (!HasWindowVisibilityAnimationTransition(window
, ANIMATE_HIDE
))
449 switch (GetWindowVisibilityAnimationType(window
)) {
450 case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP
:
451 AnimateHideWindow_Drop(window
);
453 case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL
:
454 AnimateHideWindow_Vertical(window
);
456 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
:
457 AnimateHideWindow_Fade(window
);
459 case WINDOW_VISIBILITY_ANIMATION_TYPE_WORKSPACE_HIDE
:
460 AnimateHideWindow_Workspace(window
);
462 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
:
463 AnimateHideWindow_Minimize(window
);
473 ////////////////////////////////////////////////////////////////////////////////
474 // WindowAnimation, public:
476 bool AnimateOnChildWindowVisibilityChanged(aura::Window
* window
, bool visible
) {
477 if (window
->GetProperty(aura::client::kAnimationsDisabledKey
) ||
478 CommandLine::ForCurrentProcess()->HasSwitch(
479 switches::kAuraWindowAnimationsDisabled
)) {
483 return AnimateShowWindow(window
);
485 // Don't start hiding the window again if it's already being hidden.
486 return window
->layer()->GetTargetOpacity() != 0.0f
&&
487 AnimateHideWindow(window
);
491 } // namespace internal