Disable Vsync when Aero Glass is enabled.
[chromium-blink-merge.git] / ash / wm / window_animations.cc
blob13abd4a8c2307b640cd2e3c58b1a17da6cd1046c
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 <math.h>
9 #include <algorithm>
10 #include <vector>
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"
42 namespace ash {
43 namespace {
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);
73 } // namespace
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);
82 target_bounds =
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(
94 gfx::Point(),
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.
126 if (!show) {
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,
166 bool show) {
167 window->layer()->set_delegate(window);
169 float start_value, end_value;
170 if (show) {
171 start_value = kWindowAnimation_HideBrightnessGrayscale;
172 end_value = kWindowAnimation_ShowBrightnessGrayscale;
173 } else {
174 start_value = kWindowAnimation_ShowBrightnessGrayscale;
175 end_value = kWindowAnimation_HideBrightnessGrayscale;
178 window->layer()->SetLayerBrightness(start_value);
179 window->layer()->SetLayerGrayscale(start_value);
180 if (show) {
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);
190 if (!show) {
191 settings.AddObserver(
192 views::corewm::CreateHidingWindowAnimationObserver(window));
195 window->layer()->GetAnimator()->
196 ScheduleTogether(
197 CreateBrightnessGrayscaleAnimationSequence(end_value, duration));
198 if (!show) {
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)) {
215 return false;
218 switch (views::corewm::GetWindowVisibilityAnimationType(window)) {
219 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
220 AnimateShowWindow_Minimize(window);
221 return true;
222 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
223 AnimateShowWindow_BrightnessGrayscale(window);
224 return true;
225 default:
226 NOTREACHED();
227 return false;
231 bool AnimateHideWindow(aura::Window* window) {
232 if (!views::corewm::HasWindowVisibilityAnimationTransition(
233 window, views::corewm::ANIMATE_HIDE)) {
234 return false;
237 switch (views::corewm::GetWindowVisibilityAnimationType(window)) {
238 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
239 AnimateHideWindow_Minimize(window);
240 return true;
241 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
242 AnimateHideWindow_BrightnessGrayscale(window);
243 return true;
244 default:
245 NOTREACHED();
246 return false;
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 {
256 public:
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)
260 : window_(window),
261 layer_(layer) {
262 window_->AddObserver(this);
263 layer_->GetCompositor()->AddObserver(this);
265 virtual ~CrossFadeObserver() {
266 window_->RemoveObserver(this);
267 window_ = NULL;
268 layer_->GetCompositor()->RemoveObserver(this);
269 views::corewm::DeepDeleteLayers(layer_);
270 layer_ = NULL;
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 {
304 delete this;
307 private:
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);
347 if (old_on_top) {
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.
353 old_layer = NULL;
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);
367 if (!old_on_top) {
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());
379 if (!old_on_top) {
380 // New layer is on top, fade it in.
381 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
384 return duration;
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
390 // quit.
391 if (!window->TargetVisibility()) {
392 window->SetBounds(new_bounds);
393 return;
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());
411 if (old_on_top)
412 old_layer->parent()->StackBelow(new_layer, old_layer);
413 else
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();
425 if (restoring)
426 layer_parent->StackAbove(old_layer, new_workspace->layer());
427 else
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.
442 if (max_area == 0)
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.
447 if (delta_area == 0)
448 return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS);
450 float factor =
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))
459 return false;
461 // Attempt to run CoreWm supplied animation types.
462 if (views::corewm::AnimateOnChildWindowVisibilityChanged(window, visible))
463 return true;
465 // Otherwise try to run an Ash-specific animation.
466 if (visible)
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());
498 return animations;
501 // Returns scale related to the specified AshWindowScaleType.
502 void SetTransformForScaleAnimation(ui::Layer* layer,
503 LayerScaleAnimationDirection type) {
504 const float scale =
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.
517 if (!launcher)
518 return gfx::Rect();
519 gfx::Rect item_rect = launcher->GetScreenBoundsOfItemIconForWindow(window);
521 // The launcher item is visible and has an icon.
522 if (!item_rect.IsEmpty())
523 return item_rect;
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
530 // bar.
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());
540 break;
541 case SHELF_ALIGNMENT_LEFT:
542 item_rect.set_x(shelf_bounds.right());
543 break;
544 case SHELF_ALIGNMENT_RIGHT:
545 item_rect.set_x(shelf_bounds.x());
546 break;
547 case SHELF_ALIGNMENT_TOP:
548 item_rect.set_y(shelf_bounds.bottom());
549 break;
551 return item_rect;
555 // Assume the launcher is overflowed, zoom off to the bottom right of the
556 // work area.
557 gfx::Rect work_area =
558 Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
559 return gfx::Rect(work_area.right(), work_area.bottom(), 0, 0);
562 } // namespace ash