Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / ash / wm / window_animations.cc
blob45348c751f31aa7bdcf49d5850fc5a65627274dd
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/ash_switches.h"
13 #include "ash/launcher/launcher.h"
14 #include "ash/screen_ash.h"
15 #include "ash/shell.h"
16 #include "ash/wm/window_util.h"
17 #include "ash/wm/workspace_controller.h"
18 #include "ash/wm/workspace/workspace_animations.h"
19 #include "base/command_line.h"
20 #include "base/compiler_specific.h"
21 #include "base/logging.h"
22 #include "base/message_loop.h"
23 #include "base/stl_util.h"
24 #include "base/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 ui::Tween::Type kCrossFadeTweenType = ui::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 gfx::Rect GetMinimizeRectForWindow(aura::Window* window) {
76 Launcher* launcher = Launcher::ForWindow(window);
77 // Launcher is created lazily and can be NULL.
78 if (!launcher)
79 return gfx::Rect();
80 gfx::Rect target_bounds = Launcher::ForWindow(window)->
81 GetScreenBoundsOfItemIconForWindow(window);
82 if (target_bounds.IsEmpty()) {
83 // Assume the launcher is overflowed, zoom off to the bottom right of the
84 // work area.
85 gfx::Rect work_area =
86 Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
87 target_bounds.SetRect(work_area.right(), work_area.bottom(), 0, 0);
89 target_bounds =
90 ScreenAsh::ConvertRectFromScreen(window->parent(), target_bounds);
91 return target_bounds;
94 void AddLayerAnimationsForMinimize(aura::Window* window, bool show) {
95 // Recalculate the transform at restore time since the launcher item may have
96 // moved while the window was minimized.
97 gfx::Rect bounds = window->bounds();
98 gfx::Rect target_bounds = GetMinimizeRectForWindow(window);
100 float scale_x = static_cast<float>(target_bounds.height()) / bounds.width();
101 float scale_y = static_cast<float>(target_bounds.width()) / bounds.height();
103 scoped_ptr<ui::InterpolatedTransform> scale(
104 new ui::InterpolatedScale(gfx::Point3F(1, 1, 1),
105 gfx::Point3F(scale_x, scale_y, 1)));
107 scoped_ptr<ui::InterpolatedTransform> translation(
108 new ui::InterpolatedTranslation(
109 gfx::Point(),
110 gfx::Point(target_bounds.x() - bounds.x(),
111 target_bounds.y() - bounds.y())));
113 scoped_ptr<ui::InterpolatedTransform> rotation(
114 new ui::InterpolatedRotation(0, kWindowAnimation_MinimizeRotate));
116 scoped_ptr<ui::InterpolatedTransform> rotation_about_pivot(
117 new ui::InterpolatedTransformAboutPivot(
118 gfx::Point(bounds.width() * 0.5, bounds.height() * 0.5),
119 rotation.release()));
121 scale->SetChild(translation.release());
122 rotation_about_pivot->SetChild(scale.release());
124 rotation_about_pivot->SetReversed(show);
126 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
127 kLayerAnimationsForMinimizeDurationMS);
129 scoped_ptr<ui::LayerAnimationElement> transition(
130 ui::LayerAnimationElement::CreateInterpolatedTransformElement(
131 rotation_about_pivot.release(), duration));
133 transition->set_tween_type(
134 show ? ui::Tween::EASE_IN : ui::Tween::EASE_IN_OUT);
136 window->layer()->GetAnimator()->ScheduleAnimation(
137 new ui::LayerAnimationSequence(transition.release()));
139 // When hiding a window, turn off blending until the animation is 3 / 4 done
140 // to save bandwidth and reduce jank.
141 if (!show) {
142 window->layer()->GetAnimator()->SchedulePauseForProperties(
143 (duration * 3) / 4, ui::LayerAnimationElement::OPACITY, -1);
146 // Fade in and out quickly when the window is small to reduce jank.
147 float opacity = show ? 1.0f : 0.0f;
148 window->layer()->GetAnimator()->ScheduleAnimation(
149 new ui::LayerAnimationSequence(
150 ui::LayerAnimationElement::CreateOpacityElement(
151 opacity, duration / 4)));
154 void AnimateShowWindow_Minimize(aura::Window* window) {
155 window->layer()->set_delegate(window);
156 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
157 AddLayerAnimationsForMinimize(window, true);
159 // Now that the window has been restored, we need to clear its animation style
160 // to default so that normal animation applies.
161 views::corewm::SetWindowVisibilityAnimationType(
162 window, views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
165 void AnimateHideWindow_Minimize(aura::Window* window) {
166 window->layer()->set_delegate(NULL);
168 // Property sets within this scope will be implicitly animated.
169 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
170 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
171 kLayerAnimationsForMinimizeDurationMS);
172 settings.SetTransitionDuration(duration);
173 settings.AddObserver(
174 views::corewm::CreateHidingWindowAnimationObserver(window));
175 window->layer()->SetVisible(false);
177 AddLayerAnimationsForMinimize(window, false);
180 void AnimateShowHideWindowCommon_BrightnessGrayscale(aura::Window* window,
181 bool show) {
182 window->layer()->set_delegate(window);
184 float start_value, end_value;
185 if (show) {
186 start_value = kWindowAnimation_HideBrightnessGrayscale;
187 end_value = kWindowAnimation_ShowBrightnessGrayscale;
188 } else {
189 start_value = kWindowAnimation_ShowBrightnessGrayscale;
190 end_value = kWindowAnimation_HideBrightnessGrayscale;
193 window->layer()->SetLayerBrightness(start_value);
194 window->layer()->SetLayerGrayscale(start_value);
195 if (show) {
196 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
197 window->layer()->SetVisible(true);
200 base::TimeDelta duration =
201 base::TimeDelta::FromMilliseconds(kBrightnessGrayscaleFadeDurationMs);
203 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
204 settings.SetTransitionDuration(duration);
205 if (!show) {
206 settings.AddObserver(
207 views::corewm::CreateHidingWindowAnimationObserver(window));
210 window->layer()->GetAnimator()->
211 ScheduleTogether(
212 CreateBrightnessGrayscaleAnimationSequence(end_value, duration));
213 if (!show) {
214 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
215 window->layer()->SetVisible(false);
219 void AnimateShowWindow_BrightnessGrayscale(aura::Window* window) {
220 AnimateShowHideWindowCommon_BrightnessGrayscale(window, true);
223 void AnimateHideWindow_BrightnessGrayscale(aura::Window* window) {
224 AnimateShowHideWindowCommon_BrightnessGrayscale(window, false);
227 bool AnimateShowWindow(aura::Window* window) {
228 if (!views::corewm::HasWindowVisibilityAnimationTransition(
229 window, views::corewm::ANIMATE_SHOW)) {
230 return false;
233 switch (views::corewm::GetWindowVisibilityAnimationType(window)) {
234 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
235 AnimateShowWindow_Minimize(window);
236 return true;
237 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
238 AnimateShowWindow_BrightnessGrayscale(window);
239 return true;
240 default:
241 NOTREACHED();
242 return false;
246 bool AnimateHideWindow(aura::Window* window) {
247 if (!views::corewm::HasWindowVisibilityAnimationTransition(
248 window, views::corewm::ANIMATE_HIDE)) {
249 return false;
252 switch (views::corewm::GetWindowVisibilityAnimationType(window)) {
253 case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
254 AnimateHideWindow_Minimize(window);
255 return true;
256 case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
257 AnimateHideWindow_BrightnessGrayscale(window);
258 return true;
259 default:
260 NOTREACHED();
261 return false;
265 // Observer for a window cross-fade animation. If either the window closes or
266 // the layer's animation completes or compositing is aborted due to GPU crash,
267 // it deletes the layer and removes itself as an observer.
268 class CrossFadeObserver : public ui::CompositorObserver,
269 public aura::WindowObserver,
270 public ui::ImplicitAnimationObserver {
271 public:
272 // Observes |window| for destruction, but does not take ownership.
273 // Takes ownership of |layer| and its child layers.
274 CrossFadeObserver(aura::Window* window, ui::Layer* layer)
275 : window_(window),
276 layer_(layer) {
277 window_->AddObserver(this);
278 layer_->GetCompositor()->AddObserver(this);
280 virtual ~CrossFadeObserver() {
281 window_->RemoveObserver(this);
282 window_ = NULL;
283 layer_->GetCompositor()->RemoveObserver(this);
284 views::corewm::DeepDeleteLayers(layer_);
285 layer_ = NULL;
288 // ui::CompositorObserver overrides:
289 virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {
291 virtual void OnCompositingStarted(ui::Compositor* compositor,
292 base::TimeTicks start_time) OVERRIDE {
294 virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE {
296 virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE {
297 // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
298 layer_->GetAnimator()->StopAnimating();
300 virtual void OnCompositingLockStateChanged(
301 ui::Compositor* compositor) OVERRIDE {
303 virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
304 base::TimeTicks timebase,
305 base::TimeDelta interval) OVERRIDE {
308 // aura::WindowObserver overrides:
309 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
310 // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
311 layer_->GetAnimator()->StopAnimating();
314 // ui::ImplicitAnimationObserver overrides:
315 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
316 delete this;
319 private:
320 aura::Window* window_; // not owned
321 ui::Layer* layer_; // owned
323 DISALLOW_COPY_AND_ASSIGN(CrossFadeObserver);
326 // Implementation of cross fading. Window is the window being cross faded. It
327 // should be at the target bounds. |old_layer| the previous layer from |window|.
328 // This takes ownership of |old_layer| and deletes when the animation is done.
329 // |pause_duration| is the duration to pause at the current bounds before
330 // animating. Returns the duration of the fade.
331 base::TimeDelta CrossFadeImpl(aura::Window* window,
332 ui::Layer* old_layer,
333 ui::Tween::Type tween_type) {
334 const gfx::Rect old_bounds(old_layer->bounds());
335 const gfx::Rect new_bounds(window->bounds());
336 const bool old_on_top = (old_bounds.width() > new_bounds.width());
338 // Shorten the animation if there's not much visual movement.
339 const base::TimeDelta duration = GetCrossFadeDuration(old_bounds, new_bounds);
341 // Scale up the old layer while translating to new position.
343 old_layer->GetAnimator()->StopAnimating();
344 ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
346 // Animation observer owns the old layer and deletes itself.
347 settings.AddObserver(new CrossFadeObserver(window, old_layer));
348 settings.SetTransitionDuration(duration);
349 settings.SetTweenType(tween_type);
350 gfx::Transform out_transform;
351 float scale_x = static_cast<float>(new_bounds.width()) /
352 static_cast<float>(old_bounds.width());
353 float scale_y = static_cast<float>(new_bounds.height()) /
354 static_cast<float>(old_bounds.height());
355 out_transform.Translate(new_bounds.x() - old_bounds.x(),
356 new_bounds.y() - old_bounds.y());
357 out_transform.Scale(scale_x, scale_y);
358 old_layer->SetTransform(out_transform);
359 if (old_on_top) {
360 // The old layer is on top, and should fade out. The new layer below will
361 // stay opaque to block the desktop.
362 old_layer->SetOpacity(kWindowAnimation_HideOpacity);
364 // In tests |old_layer| is deleted here, as animations have zero duration.
365 old_layer = NULL;
368 // Set the new layer's current transform, such that the user sees a scaled
369 // version of the window with the original bounds at the original position.
370 gfx::Transform in_transform;
371 const float scale_x = static_cast<float>(old_bounds.width()) /
372 static_cast<float>(new_bounds.width());
373 const float scale_y = static_cast<float>(old_bounds.height()) /
374 static_cast<float>(new_bounds.height());
375 in_transform.Translate(old_bounds.x() - new_bounds.x(),
376 old_bounds.y() - new_bounds.y());
377 in_transform.Scale(scale_x, scale_y);
378 window->layer()->SetTransform(in_transform);
379 if (!old_on_top) {
380 // The new layer is on top and should fade in. The old layer below will
381 // stay opaque and block the desktop.
382 window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
385 // Animate the new layer to the identity transform, so the window goes to
386 // its newly set bounds.
387 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
388 settings.SetTransitionDuration(duration);
389 settings.SetTweenType(tween_type);
390 window->layer()->SetTransform(gfx::Transform());
391 if (!old_on_top) {
392 // New layer is on top, fade it in.
393 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
396 return duration;
399 void CrossFadeToBounds(aura::Window* window, const gfx::Rect& new_bounds) {
400 DCHECK(window->TargetVisibility());
401 const gfx::Rect old_bounds = window->bounds();
403 // Create fresh layers for the window and all its children to paint into.
404 // Takes ownership of the old layer and all its children, which will be
405 // cleaned up after the animation completes.
406 ui::Layer* old_layer = views::corewm::RecreateWindowLayers(window, false);
407 ui::Layer* new_layer = window->layer();
409 // Resize the window to the new size, which will force a layout and paint.
410 window->SetBounds(new_bounds);
412 // Ensure the higher-resolution layer is on top.
413 bool old_on_top = (old_bounds.width() > new_bounds.width());
414 if (old_on_top)
415 old_layer->parent()->StackBelow(new_layer, old_layer);
416 else
417 old_layer->parent()->StackAbove(new_layer, old_layer);
419 CrossFadeImpl(window, old_layer, ui::Tween::EASE_OUT);
422 void CrossFadeWindowBetweenWorkspaces(aura::Window* new_workspace,
423 aura::Window* window,
424 ui::Layer* old_layer) {
425 ui::Layer* layer_parent = new_workspace->layer()->parent();
426 layer_parent->Add(old_layer);
427 const bool restoring = old_layer->bounds().width() > window->bounds().width();
428 if (restoring)
429 layer_parent->StackAbove(old_layer, new_workspace->layer());
430 else
431 layer_parent->StackBelow(old_layer, new_workspace->layer());
433 CrossFadeImpl(window, old_layer, kCrossFadeTweenType);
436 base::TimeDelta GetCrossFadeDuration(const gfx::Rect& old_bounds,
437 const gfx::Rect& new_bounds) {
438 if (views::corewm::WindowAnimationsDisabled(NULL))
439 return base::TimeDelta();
441 int old_area = old_bounds.width() * old_bounds.height();
442 int new_area = new_bounds.width() * new_bounds.height();
443 int max_area = std::max(old_area, new_area);
444 // Avoid divide by zero.
445 if (max_area == 0)
446 return base::TimeDelta::FromMilliseconds(internal::kWorkspaceSwitchTimeMS);
448 int delta_area = std::abs(old_area - new_area);
449 // If the area didn't change, the animation is instantaneous.
450 if (delta_area == 0)
451 return base::TimeDelta::FromMilliseconds(internal::kWorkspaceSwitchTimeMS);
453 float factor =
454 static_cast<float>(delta_area) / static_cast<float>(max_area);
455 const float kRange = kCrossFadeDurationMaxMs - kCrossFadeDurationMinMs;
456 return base::TimeDelta::FromMilliseconds(
457 Round64(kCrossFadeDurationMinMs + (factor * kRange)));
460 bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) {
461 if (views::corewm::WindowAnimationsDisabled(window))
462 return false;
464 // Attempt to run CoreWm supplied animation types.
465 if (views::corewm::AnimateOnChildWindowVisibilityChanged(window, visible))
466 return true;
468 // Otherwise try to run an Ash-specific animation.
469 if (visible)
470 return AnimateShowWindow(window);
471 // Don't start hiding the window again if it's already being hidden.
472 return window->layer()->GetTargetOpacity() != 0.0f &&
473 AnimateHideWindow(window);
476 std::vector<ui::LayerAnimationSequence*>
477 CreateBrightnessGrayscaleAnimationSequence(float target_value,
478 base::TimeDelta duration) {
479 ui::Tween::Type animation_type = ui::Tween::EASE_OUT;
480 if (CommandLine::ForCurrentProcess()->HasSwitch(
481 ash::switches::kAshBootAnimationFunction2)) {
482 animation_type = ui::Tween::EASE_OUT_2;
483 } else if (CommandLine::ForCurrentProcess()->HasSwitch(
484 ash::switches::kAshBootAnimationFunction3)) {
485 animation_type = ui::Tween::EASE_OUT_3;
487 scoped_ptr<ui::LayerAnimationSequence> brightness_sequence(
488 new ui::LayerAnimationSequence());
489 scoped_ptr<ui::LayerAnimationSequence> grayscale_sequence(
490 new ui::LayerAnimationSequence());
492 scoped_ptr<ui::LayerAnimationElement> brightness_element(
493 ui::LayerAnimationElement::CreateBrightnessElement(
494 target_value, duration));
495 brightness_element->set_tween_type(animation_type);
496 brightness_sequence->AddElement(brightness_element.release());
498 scoped_ptr<ui::LayerAnimationElement> grayscale_element(
499 ui::LayerAnimationElement::CreateGrayscaleElement(
500 target_value, duration));
501 grayscale_element->set_tween_type(animation_type);
502 grayscale_sequence->AddElement(grayscale_element.release());
504 std::vector<ui::LayerAnimationSequence*> animations;
505 animations.push_back(brightness_sequence.release());
506 animations.push_back(grayscale_sequence.release());
508 return animations;
511 // Returns scale related to the specified AshWindowScaleType.
512 void SetTransformForScaleAnimation(ui::Layer* layer,
513 LayerScaleAnimationDirection type) {
514 const float scale =
515 type == LAYER_SCALE_ANIMATION_ABOVE ? kLayerScaleAboveSize :
516 kLayerScaleBelowSize;
517 gfx::Transform transform;
518 transform.Translate(-layer->bounds().width() * (scale - 1.0f) / 2,
519 -layer->bounds().height() * (scale - 1.0f) / 2);
520 transform.Scale(scale, scale);
521 layer->SetTransform(transform);
524 } // namespace ash