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 "ui/views/corewm/window_animations.h"
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/stl_util.h"
17 #include "base/time/time.h"
18 #include "ui/aura/client/animation_host.h"
19 #include "ui/aura/client/aura_constants.h"
20 #include "ui/aura/window.h"
21 #include "ui/aura/window_delegate.h"
22 #include "ui/aura/window_observer.h"
23 #include "ui/aura/window_property.h"
24 #include "ui/compositor/compositor_observer.h"
25 #include "ui/compositor/layer.h"
26 #include "ui/compositor/layer_animation_observer.h"
27 #include "ui/compositor/layer_animation_sequence.h"
28 #include "ui/compositor/layer_animator.h"
29 #include "ui/compositor/scoped_layer_animation_settings.h"
30 #include "ui/gfx/animation/animation.h"
31 #include "ui/gfx/interpolated_transform.h"
32 #include "ui/gfx/rect_conversions.h"
33 #include "ui/gfx/screen.h"
34 #include "ui/gfx/vector2d.h"
35 #include "ui/gfx/vector3d_f.h"
36 #include "ui/views/corewm/corewm_switches.h"
37 #include "ui/views/corewm/window_util.h"
38 #include "ui/views/view.h"
39 #include "ui/views/widget/widget.h"
41 DECLARE_WINDOW_PROPERTY_TYPE(int)
42 DECLARE_WINDOW_PROPERTY_TYPE(views::corewm::WindowVisibilityAnimationType)
43 DECLARE_WINDOW_PROPERTY_TYPE(views::corewm::WindowVisibilityAnimationTransition)
44 DECLARE_WINDOW_PROPERTY_TYPE(float)
45 DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(VIEWS_EXPORT, bool)
48 using base::TimeDelta;
54 const float kWindowAnimation_Vertical_TranslateY = 15.f;
57 DEFINE_WINDOW_PROPERTY_KEY(int,
58 kWindowVisibilityAnimationTypeKey,
59 WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
60 DEFINE_WINDOW_PROPERTY_KEY(int, kWindowVisibilityAnimationDurationKey, 0);
61 DEFINE_WINDOW_PROPERTY_KEY(WindowVisibilityAnimationTransition,
62 kWindowVisibilityAnimationTransitionKey,
64 DEFINE_WINDOW_PROPERTY_KEY(float,
65 kWindowVisibilityAnimationVerticalPositionKey,
66 kWindowAnimation_Vertical_TranslateY);
70 const int kDefaultAnimationDurationForMenuMS = 150;
72 const float kWindowAnimation_HideOpacity = 0.f;
73 const float kWindowAnimation_ShowOpacity = 1.f;
74 const float kWindowAnimation_TranslateFactor = 0.5f;
75 const float kWindowAnimation_ScaleFactor = .95f;
77 const int kWindowAnimation_Rotate_DurationMS = 180;
78 const int kWindowAnimation_Rotate_OpacityDurationPercent = 90;
79 const float kWindowAnimation_Rotate_TranslateY = -20.f;
80 const float kWindowAnimation_Rotate_PerspectiveDepth = 500.f;
81 const float kWindowAnimation_Rotate_DegreesX = 5.f;
82 const float kWindowAnimation_Rotate_ScaleFactor = .99f;
84 const float kWindowAnimation_Bounce_Scale = 1.02f;
85 const int kWindowAnimation_Bounce_DurationMS = 180;
86 const int kWindowAnimation_Bounce_GrowShrinkDurationPercent = 40;
88 base::TimeDelta GetWindowVisibilityAnimationDuration(
89 const aura::Window& window) {
91 window.GetProperty(kWindowVisibilityAnimationDurationKey);
92 if (duration == 0 && window.type() == ui::wm::WINDOW_TYPE_MENU) {
93 return base::TimeDelta::FromMilliseconds(
94 kDefaultAnimationDurationForMenuMS);
96 return TimeDelta::FromInternalValue(duration);
99 // Gets/sets the WindowVisibilityAnimationType associated with a window.
100 // TODO(beng): redundant/fold into method on public api?
101 int GetWindowVisibilityAnimationType(aura::Window* window) {
102 int type = window->GetProperty(kWindowVisibilityAnimationTypeKey);
103 if (type == WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT) {
104 return (window->type() == ui::wm::WINDOW_TYPE_MENU ||
105 window->type() == ui::wm::WINDOW_TYPE_TOOLTIP)
106 ? WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
107 : WINDOW_VISIBILITY_ANIMATION_TYPE_DROP;
112 // Observes a hide animation.
113 // A window can be hidden for a variety of reasons. Sometimes, Hide() will be
114 // called and life is simple. Sometimes, the window is actually bound to a
115 // views::Widget and that Widget is closed, and life is a little more
116 // complicated. When a Widget is closed the aura::Window* is actually not
117 // destroyed immediately - it is actually just immediately hidden and then
118 // destroyed when the stack unwinds. To handle this case, we start the hide
119 // animation immediately when the window is hidden, then when the window is
120 // subsequently destroyed this object acquires ownership of the window's layer,
121 // so that it can continue animating it until the animation completes.
122 // Regardless of whether or not the window is destroyed, this object deletes
123 // itself when the animation completes.
124 class HidingWindowAnimationObserver : public ui::ImplicitAnimationObserver,
125 public aura::WindowObserver {
127 explicit HidingWindowAnimationObserver(aura::Window* window)
129 window_->AddObserver(this);
131 virtual ~HidingWindowAnimationObserver() {
132 STLDeleteElements(&layers_);
136 // Overridden from ui::ImplicitAnimationObserver:
137 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
138 // Window may have been destroyed by this point.
140 aura::client::AnimationHost* animation_host =
141 aura::client::GetAnimationHost(window_);
143 animation_host->OnWindowHidingAnimationCompleted();
144 window_->RemoveObserver(this);
149 // Overridden from aura::WindowObserver:
150 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
151 DCHECK_EQ(window, window_);
152 DCHECK(layers_.empty());
153 AcquireAllLayers(window_);
155 // If the Widget has views with layers, then it is necessary to take
156 // ownership of those layers too.
157 views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window_);
158 const views::Widget* const_widget = widget;
159 if (widget && const_widget->GetRootView() && widget->GetContentsView())
160 AcquireAllViewLayers(widget->GetContentsView());
161 window_->RemoveObserver(this);
165 void AcquireAllLayers(aura::Window* window) {
166 ui::Layer* layer = window->AcquireLayer();
168 layers_.push_back(layer);
169 for (aura::Window::Windows::const_iterator it = window->children().begin();
170 it != window->children().end();
172 AcquireAllLayers(*it);
175 void AcquireAllViewLayers(views::View* view) {
176 for (int i = 0; i < view->child_count(); ++i)
177 AcquireAllViewLayers(view->child_at(i));
179 ui::Layer* layer = view->RecreateLayer();
181 layer->SuppressPaint();
182 layers_.push_back(layer);
187 aura::Window* window_;
188 std::vector<ui::Layer*> layers_;
190 DISALLOW_COPY_AND_ASSIGN(HidingWindowAnimationObserver);
193 void GetTransformRelativeToRoot(ui::Layer* layer, gfx::Transform* transform) {
194 const Layer* root = layer;
195 while (root->parent())
196 root = root->parent();
197 layer->GetTargetTransformRelativeTo(root, transform);
200 gfx::Rect GetLayerWorldBoundsAfterTransform(ui::Layer* layer,
201 const gfx::Transform& transform) {
202 gfx::Transform in_world = transform;
203 GetTransformRelativeToRoot(layer, &in_world);
205 gfx::RectF transformed = layer->bounds();
206 in_world.TransformRect(&transformed);
208 return gfx::ToEnclosingRect(transformed);
211 // Augment the host window so that the enclosing bounds of the full
212 // animation will fit inside of it.
213 void AugmentWindowSize(aura::Window* window,
214 const gfx::Transform& end_transform) {
215 aura::client::AnimationHost* animation_host =
216 aura::client::GetAnimationHost(window);
220 const gfx::Rect& world_at_start = window->bounds();
221 gfx::Rect world_at_end =
222 GetLayerWorldBoundsAfterTransform(window->layer(), end_transform);
223 gfx::Rect union_in_window_space =
224 gfx::UnionRects(world_at_start, world_at_end);
226 // Calculate the top left and bottom right deltas to be added to the window
228 gfx::Vector2d top_left_delta(world_at_start.x() - union_in_window_space.x(),
229 world_at_start.y() - union_in_window_space.y());
231 gfx::Vector2d bottom_right_delta(
232 union_in_window_space.x() + union_in_window_space.width() -
233 (world_at_start.x() + world_at_start.width()),
234 union_in_window_space.y() + union_in_window_space.height() -
235 (world_at_start.y() + world_at_start.height()));
237 DCHECK(top_left_delta.x() >= 0 && top_left_delta.y() >= 0 &&
238 bottom_right_delta.x() >= 0 && bottom_right_delta.y() >= 0);
240 animation_host->SetHostTransitionOffsets(top_left_delta, bottom_right_delta);
243 // Shows a window using an animation, animating its opacity from 0.f to 1.f,
244 // its visibility to true, and its transform from |start_transform| to
246 void AnimateShowWindowCommon(aura::Window* window,
247 const gfx::Transform& start_transform,
248 const gfx::Transform& end_transform) {
249 window->layer()->set_delegate(window);
251 AugmentWindowSize(window, end_transform);
253 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
254 window->layer()->SetTransform(start_transform);
255 window->layer()->SetVisible(true);
258 // Property sets within this scope will be implicitly animated.
259 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
260 base::TimeDelta duration = GetWindowVisibilityAnimationDuration(*window);
261 if (duration.ToInternalValue() > 0)
262 settings.SetTransitionDuration(duration);
264 window->layer()->SetTransform(end_transform);
265 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
269 // Hides a window using an animation, animating its opacity from 1.f to 0.f,
270 // its visibility to false, and its transform to |end_transform|.
271 void AnimateHideWindowCommon(aura::Window* window,
272 const gfx::Transform& end_transform) {
273 AugmentWindowSize(window, end_transform);
274 window->layer()->set_delegate(NULL);
276 // Property sets within this scope will be implicitly animated.
277 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
278 settings.AddObserver(new HidingWindowAnimationObserver(window));
280 base::TimeDelta duration = GetWindowVisibilityAnimationDuration(*window);
281 if (duration.ToInternalValue() > 0)
282 settings.SetTransitionDuration(duration);
284 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
285 window->layer()->SetTransform(end_transform);
286 window->layer()->SetVisible(false);
289 static gfx::Transform GetScaleForWindow(aura::Window* window) {
290 gfx::Rect bounds = window->bounds();
291 gfx::Transform scale = gfx::GetScaleTransform(
292 gfx::Point(kWindowAnimation_TranslateFactor * bounds.width(),
293 kWindowAnimation_TranslateFactor * bounds.height()),
294 kWindowAnimation_ScaleFactor);
298 // Show/Hide windows using a shrink animation.
299 void AnimateShowWindow_Drop(aura::Window* window) {
300 AnimateShowWindowCommon(window, GetScaleForWindow(window), gfx::Transform());
303 void AnimateHideWindow_Drop(aura::Window* window) {
304 AnimateHideWindowCommon(window, GetScaleForWindow(window));
307 // Show/Hide windows using a vertical Glenimation.
308 void AnimateShowWindow_Vertical(aura::Window* window) {
309 gfx::Transform transform;
310 transform.Translate(0, window->GetProperty(
311 kWindowVisibilityAnimationVerticalPositionKey));
312 AnimateShowWindowCommon(window, transform, gfx::Transform());
315 void AnimateHideWindow_Vertical(aura::Window* window) {
316 gfx::Transform transform;
317 transform.Translate(0, window->GetProperty(
318 kWindowVisibilityAnimationVerticalPositionKey));
319 AnimateHideWindowCommon(window, transform);
322 // Show/Hide windows using a fade.
323 void AnimateShowWindow_Fade(aura::Window* window) {
324 AnimateShowWindowCommon(window, gfx::Transform(), gfx::Transform());
327 void AnimateHideWindow_Fade(aura::Window* window) {
328 AnimateHideWindowCommon(window, gfx::Transform());
331 ui::LayerAnimationElement* CreateGrowShrinkElement(
332 aura::Window* window, bool grow) {
333 scoped_ptr<ui::InterpolatedTransform> scale(new ui::InterpolatedScale(
334 gfx::Point3F(kWindowAnimation_Bounce_Scale,
335 kWindowAnimation_Bounce_Scale,
337 gfx::Point3F(1, 1, 1)));
338 scoped_ptr<ui::InterpolatedTransform> scale_about_pivot(
339 new ui::InterpolatedTransformAboutPivot(
340 gfx::Point(window->bounds().width() * 0.5,
341 window->bounds().height() * 0.5),
343 scale_about_pivot->SetReversed(grow);
344 scoped_ptr<ui::LayerAnimationElement> transition(
345 ui::LayerAnimationElement::CreateInterpolatedTransformElement(
346 scale_about_pivot.release(),
347 base::TimeDelta::FromMilliseconds(
348 kWindowAnimation_Bounce_DurationMS *
349 kWindowAnimation_Bounce_GrowShrinkDurationPercent / 100)));
350 transition->set_tween_type(grow ? gfx::Tween::EASE_OUT : gfx::Tween::EASE_IN);
351 return transition.release();
354 void AnimateBounce(aura::Window* window) {
355 ui::ScopedLayerAnimationSettings scoped_settings(
356 window->layer()->GetAnimator());
357 scoped_settings.SetPreemptionStrategy(
358 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
359 window->layer()->set_delegate(window);
360 scoped_ptr<ui::LayerAnimationSequence> sequence(
361 new ui::LayerAnimationSequence);
362 sequence->AddElement(CreateGrowShrinkElement(window, true));
363 sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement(
364 ui::LayerAnimationElement::BOUNDS,
365 base::TimeDelta::FromMilliseconds(
366 kWindowAnimation_Bounce_DurationMS *
367 (100 - 2 * kWindowAnimation_Bounce_GrowShrinkDurationPercent) /
369 sequence->AddElement(CreateGrowShrinkElement(window, false));
370 window->layer()->GetAnimator()->StartAnimation(sequence.release());
373 void AddLayerAnimationsForRotate(aura::Window* window, bool show) {
374 window->layer()->set_delegate(window);
376 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
378 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
379 kWindowAnimation_Rotate_DurationMS);
382 new HidingWindowAnimationObserver(window);
383 window->layer()->GetAnimator()->SchedulePauseForProperties(
384 duration * (100 - kWindowAnimation_Rotate_OpacityDurationPercent) / 100,
385 ui::LayerAnimationElement::OPACITY);
387 scoped_ptr<ui::LayerAnimationElement> opacity(
388 ui::LayerAnimationElement::CreateOpacityElement(
389 show ? kWindowAnimation_ShowOpacity : kWindowAnimation_HideOpacity,
390 duration * kWindowAnimation_Rotate_OpacityDurationPercent / 100));
391 opacity->set_tween_type(gfx::Tween::EASE_IN_OUT);
392 window->layer()->GetAnimator()->ScheduleAnimation(
393 new ui::LayerAnimationSequence(opacity.release()));
395 float xcenter = window->bounds().width() * 0.5;
397 gfx::Transform transform;
398 transform.Translate(xcenter, 0);
399 transform.ApplyPerspectiveDepth(kWindowAnimation_Rotate_PerspectiveDepth);
400 transform.Translate(-xcenter, 0);
401 scoped_ptr<ui::InterpolatedTransform> perspective(
402 new ui::InterpolatedConstantTransform(transform));
404 scoped_ptr<ui::InterpolatedTransform> scale(
405 new ui::InterpolatedScale(1, kWindowAnimation_Rotate_ScaleFactor));
406 scoped_ptr<ui::InterpolatedTransform> scale_about_pivot(
407 new ui::InterpolatedTransformAboutPivot(
408 gfx::Point(xcenter, kWindowAnimation_Rotate_TranslateY),
411 scoped_ptr<ui::InterpolatedTransform> translation(
412 new ui::InterpolatedTranslation(gfx::Point(), gfx::Point(
413 0, kWindowAnimation_Rotate_TranslateY)));
415 scoped_ptr<ui::InterpolatedTransform> rotation(
416 new ui::InterpolatedAxisAngleRotation(
417 gfx::Vector3dF(1, 0, 0), 0, kWindowAnimation_Rotate_DegreesX));
419 scale_about_pivot->SetChild(perspective.release());
420 translation->SetChild(scale_about_pivot.release());
421 rotation->SetChild(translation.release());
422 rotation->SetReversed(show);
424 scoped_ptr<ui::LayerAnimationElement> transition(
425 ui::LayerAnimationElement::CreateInterpolatedTransformElement(
426 rotation.release(), duration));
428 window->layer()->GetAnimator()->ScheduleAnimation(
429 new ui::LayerAnimationSequence(transition.release()));
432 void AnimateShowWindow_Rotate(aura::Window* window) {
433 AddLayerAnimationsForRotate(window, true);
436 void AnimateHideWindow_Rotate(aura::Window* window) {
437 AddLayerAnimationsForRotate(window, false);
440 bool AnimateShowWindow(aura::Window* window) {
441 if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) {
442 if (HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) {
443 // Since hide animation may have changed opacity and transform,
444 // reset them to show the window.
445 window->layer()->set_delegate(window);
446 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
447 window->layer()->SetTransform(gfx::Transform());
452 switch (GetWindowVisibilityAnimationType(window)) {
453 case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP:
454 AnimateShowWindow_Drop(window);
456 case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL:
457 AnimateShowWindow_Vertical(window);
459 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE:
460 AnimateShowWindow_Fade(window);
462 case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE:
463 AnimateShowWindow_Rotate(window);
470 bool AnimateHideWindow(aura::Window* window) {
471 if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) {
472 if (HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) {
473 // Since show animation may have changed opacity and transform,
474 // reset them, though the change should be hidden.
475 window->layer()->set_delegate(NULL);
476 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
477 window->layer()->SetTransform(gfx::Transform());
482 switch (GetWindowVisibilityAnimationType(window)) {
483 case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP:
484 AnimateHideWindow_Drop(window);
486 case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL:
487 AnimateHideWindow_Vertical(window);
489 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE:
490 AnimateHideWindow_Fade(window);
492 case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE:
493 AnimateHideWindow_Rotate(window);
502 ////////////////////////////////////////////////////////////////////////////////
503 // External interface
505 void SetWindowVisibilityAnimationType(aura::Window* window, int type) {
506 window->SetProperty(kWindowVisibilityAnimationTypeKey, type);
509 int GetWindowVisibilityAnimationType(aura::Window* window) {
510 return window->GetProperty(kWindowVisibilityAnimationTypeKey);
513 void SetWindowVisibilityAnimationTransition(
514 aura::Window* window,
515 WindowVisibilityAnimationTransition transition) {
516 window->SetProperty(kWindowVisibilityAnimationTransitionKey, transition);
519 bool HasWindowVisibilityAnimationTransition(
520 aura::Window* window,
521 WindowVisibilityAnimationTransition transition) {
522 WindowVisibilityAnimationTransition prop = window->GetProperty(
523 kWindowVisibilityAnimationTransitionKey);
524 return (prop & transition) != 0;
527 void SetWindowVisibilityAnimationDuration(aura::Window* window,
528 const TimeDelta& duration) {
529 window->SetProperty(kWindowVisibilityAnimationDurationKey,
530 static_cast<int>(duration.ToInternalValue()));
533 base::TimeDelta GetWindowVisibilityAnimationDuration(
534 const aura::Window& window) {
535 return base::TimeDelta::FromInternalValue(
536 window.GetProperty(kWindowVisibilityAnimationDurationKey));
539 void SetWindowVisibilityAnimationVerticalPosition(aura::Window* window,
541 window->SetProperty(kWindowVisibilityAnimationVerticalPositionKey, position);
544 ui::ImplicitAnimationObserver* CreateHidingWindowAnimationObserver(
545 aura::Window* window) {
546 return new HidingWindowAnimationObserver(window);
549 bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) {
550 if (WindowAnimationsDisabled(window))
553 return AnimateShowWindow(window);
554 // Don't start hiding the window again if it's already being hidden.
555 return window->layer()->GetTargetOpacity() != 0.0f &&
556 AnimateHideWindow(window);
559 bool AnimateWindow(aura::Window* window, WindowAnimationType type) {
561 case WINDOW_ANIMATION_TYPE_BOUNCE:
562 AnimateBounce(window);
570 bool WindowAnimationsDisabled(aura::Window* window) {
571 return (!gfx::Animation::ShouldRenderRichAnimation() || (window &&
572 window->GetProperty(aura::client::kAnimationsDisabledKey)) ||
573 CommandLine::ForCurrentProcess()->HasSwitch(
574 switches::kWindowAnimationsDisabled));
577 } // namespace corewm