Adds a new layer animation element which owns an interpolated transform. This allows...
[chromium-blink-merge.git] / ash / wm / window_animations.cc
blobb1a1a895275897016a8a7078433889c8320b4061
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"
9 #include "ash/shell.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;
33 namespace ash {
34 namespace internal {
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,
41 ANIMATE_BOTH);
42 } // namespace internal
44 void SetWindowVisibilityAnimationType(aura::Window* window,
45 WindowVisibilityAnimationType type) {
46 window->SetProperty(internal::kWindowVisibilityAnimationTypeKey, type);
49 void SetWindowVisibilityAnimationTransition(
50 aura::Window* window,
51 WindowVisibilityAnimationTransition transition) {
52 window->SetProperty(internal::kWindowVisibilityAnimationTransitionKey,
53 transition);
56 void SetWindowVisibilityAnimationDuration(aura::Window* window,
57 const TimeDelta& duration) {
58 window->SetProperty(internal::kWindowVisibilityAnimationDurationKey,
59 static_cast<int>(duration.ToInternalValue()));
62 bool HasWindowVisibilityAnimationTransition(
63 aura::Window* window,
64 WindowVisibilityAnimationTransition transition) {
65 WindowVisibilityAnimationTransition prop = window->GetProperty(
66 internal::kWindowVisibilityAnimationTransitionKey);
67 return (prop & transition) != 0;
70 namespace internal {
71 namespace {
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;
95 return type;
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 {
112 public:
113 explicit HidingWindowAnimationObserver(aura::Window* window)
114 : window_(window) {
115 window_->AddObserver(this);
117 virtual ~HidingWindowAnimationObserver() {
118 STLDeleteElements(&layers_);
121 private:
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.
128 if (window_)
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);
139 window_ = NULL;
142 void AcquireAllLayers(aura::Window* window) {
143 ui::Layer* layer = window->AcquireLayer();
144 DCHECK(layer);
145 layers_.push_back(layer);
146 for (aura::Window::Windows::const_iterator it = window->children().begin();
147 it != window->children().end();
148 ++it)
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 {
166 public:
167 explicit WorkspaceHidingWindowAnimationObserver(aura::Window* window)
168 : layer_(window->layer()) {
170 virtual ~WorkspaceHidingWindowAnimationObserver() {
173 private:
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);
181 ui::Layer* layer_;
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());
198 int duration =
199 window->GetProperty(internal::kWindowVisibilityAnimationDurationKey);
200 if (duration > 0)
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));
218 int duration =
219 window->GetProperty(internal::kWindowVisibilityAnimationDurationKey);
220 if (duration > 0)
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
271 // window.
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;
277 float initial_x =
278 (static_cast<float>(window->bounds().x()) - mid_x) * kWorkspaceScale +
279 mid_x;
280 float mid_y = static_cast<float>(parent_bounds.height()) / 2.0f;
281 float initial_y =
282 (static_cast<float>(window->bounds().y()) - mid_y) * kWorkspaceScale +
283 mid_y;
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);
290 return transform;
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
326 // completes.
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
342 // work area.
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(
363 gfx::Point(),
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))
421 return false;
423 switch (GetWindowVisibilityAnimationType(window)) {
424 case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP:
425 AnimateShowWindow_Drop(window);
426 return true;
427 case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL:
428 AnimateShowWindow_Vertical(window);
429 return true;
430 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE:
431 AnimateShowWindow_Fade(window);
432 return true;
433 case WINDOW_VISIBILITY_ANIMATION_TYPE_WORKSPACE_SHOW:
434 AnimateShowWindow_Workspace(window);
435 return true;
436 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
437 AnimateShowWindow_Minimize(window);
438 return true;
439 default:
440 NOTREACHED();
441 return false;
445 bool AnimateHideWindow(aura::Window* window) {
446 if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE))
447 return false;
449 switch (GetWindowVisibilityAnimationType(window)) {
450 case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP:
451 AnimateHideWindow_Drop(window);
452 return true;
453 case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL:
454 AnimateHideWindow_Vertical(window);
455 return true;
456 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE:
457 AnimateHideWindow_Fade(window);
458 return true;
459 case WINDOW_VISIBILITY_ANIMATION_TYPE_WORKSPACE_HIDE:
460 AnimateHideWindow_Workspace(window);
461 return true;
462 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
463 AnimateHideWindow_Minimize(window);
464 return true;
465 default:
466 NOTREACHED();
467 return false;
471 } // namespace
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)) {
480 return false;
482 if (visible) {
483 return AnimateShowWindow(window);
484 } else {
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
492 } // namespace ash