Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ash / wm / window_animations.cc
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.
4
5 #include "ash/wm/window_animations.h"
6
7 #include <math.h>
8
9 #include <algorithm>
10 #include <vector>
11
12 #include "ash/screen_util.h"
13 #include "ash/shelf/shelf.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"
41
42 namespace ash {
43 namespace {
44 const int kLayerAnimationsForMinimizeDurationMS = 200;
45
46 // Durations for the cross-fade animation, in milliseconds.
47 const float kCrossFadeDurationMinMs = 200.f;
48 const float kCrossFadeDurationMaxMs = 400.f;
49
50 // Durations for the brightness/grayscale fade animation, in milliseconds.
51 const int kBrightnessGrayscaleFadeDurationMs = 1000;
52
53 // Brightness/grayscale values for hide/show window animations.
54 const float kWindowAnimation_HideBrightnessGrayscale = 1.f;
55 const float kWindowAnimation_ShowBrightnessGrayscale = 0.f;
56
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;
61
62 // Scales for AshWindow above/below current workspace.
63 const float kLayerScaleAboveSize = 1.1f;
64 const float kLayerScaleBelowSize = .9f;
65
66 int64 Round64(float f) {
67   return static_cast<int64>(f + 0.5f);
68 }
69
70 }  // namespace
71
72 const int kCrossFadeDurationMS = 200;
73
74 void AddLayerAnimationsForMinimize(aura::Window* window, bool show) {
75   // Recalculate the transform at restore time since the launcher item may have
76   // moved while the window was minimized.
77   gfx::Rect bounds = window->bounds();
78   gfx::Rect target_bounds = GetMinimizeAnimationTargetBoundsInScreen(window);
79   target_bounds =
80       ScreenUtil::ConvertRectFromScreen(window->parent(), target_bounds);
81
82   float scale_x = static_cast<float>(target_bounds.width()) / bounds.width();
83   float scale_y = static_cast<float>(target_bounds.height()) / bounds.height();
84
85   scoped_ptr<ui::InterpolatedTransform> scale(
86       new ui::InterpolatedScale(gfx::Point3F(1, 1, 1),
87                                 gfx::Point3F(scale_x, scale_y, 1)));
88
89   scoped_ptr<ui::InterpolatedTransform> translation(
90       new ui::InterpolatedTranslation(
91           gfx::Point(),
92           gfx::Point(target_bounds.x() - bounds.x(),
93                      target_bounds.y() - bounds.y())));
94
95   scoped_ptr<ui::InterpolatedTransform> rotation(
96       new ui::InterpolatedRotation(0, kWindowAnimation_MinimizeRotate));
97
98   scoped_ptr<ui::InterpolatedTransform> rotation_about_pivot(
99       new ui::InterpolatedTransformAboutPivot(
100           gfx::Point(bounds.width() * 0.5, bounds.height() * 0.5),
101           rotation.release()));
102
103   scale->SetChild(translation.release());
104   rotation_about_pivot->SetChild(scale.release());
105
106   rotation_about_pivot->SetReversed(show);
107
108   base::TimeDelta duration = window->layer()->GetAnimator()->
109       GetTransitionDuration();
110
111   scoped_ptr<ui::LayerAnimationElement> transition(
112       ui::LayerAnimationElement::CreateInterpolatedTransformElement(
113           rotation_about_pivot.release(), duration));
114
115   transition->set_tween_type(
116       show ? gfx::Tween::EASE_IN : gfx::Tween::EASE_IN_OUT);
117
118   window->layer()->GetAnimator()->ScheduleAnimation(
119       new ui::LayerAnimationSequence(transition.release()));
120
121   // When hiding a window, turn off blending until the animation is 3 / 4 done
122   // to save bandwidth and reduce jank.
123   if (!show) {
124     window->layer()->GetAnimator()->SchedulePauseForProperties(
125         (duration * 3) / 4, ui::LayerAnimationElement::OPACITY);
126   }
127
128   // Fade in and out quickly when the window is small to reduce jank.
129   float opacity = show ? 1.0f : 0.0f;
130   window->layer()->GetAnimator()->ScheduleAnimation(
131       new ui::LayerAnimationSequence(
132           ui::LayerAnimationElement::CreateOpacityElement(
133               opacity, duration / 4)));
134 }
135
136 void AnimateShowWindow_Minimize(aura::Window* window) {
137   window->layer()->set_delegate(window);
138   window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
139   ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
140   base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
141       kLayerAnimationsForMinimizeDurationMS);
142   settings.SetTransitionDuration(duration);
143   AddLayerAnimationsForMinimize(window, true);
144
145   // Now that the window has been restored, we need to clear its animation style
146   // to default so that normal animation applies.
147   views::corewm::SetWindowVisibilityAnimationType(
148       window, views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
149 }
150
151 void AnimateHideWindow_Minimize(aura::Window* window) {
152   window->layer()->set_delegate(NULL);
153
154   // Property sets within this scope will be implicitly animated.
155   ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
156   base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
157       kLayerAnimationsForMinimizeDurationMS);
158   settings.SetTransitionDuration(duration);
159   settings.AddObserver(
160       views::corewm::CreateHidingWindowAnimationObserver(window));
161   window->layer()->SetVisible(false);
162
163   AddLayerAnimationsForMinimize(window, false);
164 }
165
166 void AnimateShowHideWindowCommon_BrightnessGrayscale(aura::Window* window,
167                                                      bool show) {
168   window->layer()->set_delegate(window);
169
170   float start_value, end_value;
171   if (show) {
172     start_value = kWindowAnimation_HideBrightnessGrayscale;
173     end_value = kWindowAnimation_ShowBrightnessGrayscale;
174   } else {
175     start_value = kWindowAnimation_ShowBrightnessGrayscale;
176     end_value = kWindowAnimation_HideBrightnessGrayscale;
177   }
178
179   window->layer()->SetLayerBrightness(start_value);
180   window->layer()->SetLayerGrayscale(start_value);
181   if (show) {
182     window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
183     window->layer()->SetVisible(true);
184   }
185
186   base::TimeDelta duration =
187       base::TimeDelta::FromMilliseconds(kBrightnessGrayscaleFadeDurationMs);
188
189   ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
190   settings.SetTransitionDuration(duration);
191   if (!show) {
192     settings.AddObserver(
193         views::corewm::CreateHidingWindowAnimationObserver(window));
194   }
195
196   window->layer()->GetAnimator()->
197       ScheduleTogether(
198           CreateBrightnessGrayscaleAnimationSequence(end_value, duration));
199   if (!show) {
200     window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
201     window->layer()->SetVisible(false);
202   }
203 }
204
205 void AnimateShowWindow_BrightnessGrayscale(aura::Window* window) {
206   AnimateShowHideWindowCommon_BrightnessGrayscale(window, true);
207 }
208
209 void AnimateHideWindow_BrightnessGrayscale(aura::Window* window) {
210   AnimateShowHideWindowCommon_BrightnessGrayscale(window, false);
211 }
212
213 bool AnimateShowWindow(aura::Window* window) {
214   if (!views::corewm::HasWindowVisibilityAnimationTransition(
215           window, views::corewm::ANIMATE_SHOW)) {
216     return false;
217   }
218
219   switch (views::corewm::GetWindowVisibilityAnimationType(window)) {
220     case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
221       AnimateShowWindow_Minimize(window);
222       return true;
223     case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
224       AnimateShowWindow_BrightnessGrayscale(window);
225       return true;
226     default:
227       NOTREACHED();
228       return false;
229   }
230 }
231
232 bool AnimateHideWindow(aura::Window* window) {
233   if (!views::corewm::HasWindowVisibilityAnimationTransition(
234           window, views::corewm::ANIMATE_HIDE)) {
235     return false;
236   }
237
238   switch (views::corewm::GetWindowVisibilityAnimationType(window)) {
239     case WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE:
240       AnimateHideWindow_Minimize(window);
241       return true;
242     case WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE:
243       AnimateHideWindow_BrightnessGrayscale(window);
244       return true;
245     default:
246       NOTREACHED();
247       return false;
248   }
249 }
250
251 // Observer for a window cross-fade animation. If either the window closes or
252 // the layer's animation completes or compositing is aborted due to GPU crash,
253 // it deletes the layer and removes itself as an observer.
254 class CrossFadeObserver : public ui::CompositorObserver,
255                           public aura::WindowObserver,
256                           public ui::ImplicitAnimationObserver {
257  public:
258   // Observes |window| for destruction, but does not take ownership.
259   // Takes ownership of |layer| and its child layers.
260   CrossFadeObserver(aura::Window* window, ui::Layer* layer)
261       : window_(window),
262         layer_(layer) {
263     window_->AddObserver(this);
264     layer_->GetCompositor()->AddObserver(this);
265   }
266   virtual ~CrossFadeObserver() {
267     window_->RemoveObserver(this);
268     window_ = NULL;
269     layer_->GetCompositor()->RemoveObserver(this);
270     views::corewm::DeepDeleteLayers(layer_);
271     layer_ = NULL;
272   }
273
274   // ui::CompositorObserver overrides:
275   virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {
276   }
277   virtual void OnCompositingStarted(ui::Compositor* compositor,
278                                     base::TimeTicks start_time) OVERRIDE {
279   }
280   virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE {
281   }
282   virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE {
283     // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
284     layer_->GetAnimator()->StopAnimating();
285   }
286   virtual void OnCompositingLockStateChanged(
287       ui::Compositor* compositor) OVERRIDE {
288   }
289
290   // aura::WindowObserver overrides:
291   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
292     // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
293     layer_->GetAnimator()->StopAnimating();
294   }
295   virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE {
296     layer_->GetAnimator()->StopAnimating();
297   }
298
299   // ui::ImplicitAnimationObserver overrides:
300   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
301     delete this;
302   }
303
304  private:
305   aura::Window* window_;  // not owned
306   ui::Layer* layer_;  // owned
307
308   DISALLOW_COPY_AND_ASSIGN(CrossFadeObserver);
309 };
310
311 // Implementation of cross fading. Window is the window being cross faded. It
312 // should be at the target bounds. |old_layer| the previous layer from |window|.
313 // This takes ownership of |old_layer| and deletes when the animation is done.
314 // |pause_duration| is the duration to pause at the current bounds before
315 // animating. Returns the duration of the fade.
316 base::TimeDelta CrossFadeImpl(aura::Window* window,
317                               ui::Layer* old_layer,
318                               gfx::Tween::Type tween_type) {
319   const gfx::Rect old_bounds(old_layer->bounds());
320   const gfx::Rect new_bounds(window->bounds());
321   const bool old_on_top = (old_bounds.width() > new_bounds.width());
322
323   // Shorten the animation if there's not much visual movement.
324   const base::TimeDelta duration = GetCrossFadeDuration(window,
325                                                         old_bounds, new_bounds);
326
327   // Scale up the old layer while translating to new position.
328   {
329     old_layer->GetAnimator()->StopAnimating();
330     ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
331
332     // Animation observer owns the old layer and deletes itself.
333     settings.AddObserver(new CrossFadeObserver(window, old_layer));
334     settings.SetTransitionDuration(duration);
335     settings.SetTweenType(tween_type);
336     gfx::Transform out_transform;
337     float scale_x = static_cast<float>(new_bounds.width()) /
338         static_cast<float>(old_bounds.width());
339     float scale_y = static_cast<float>(new_bounds.height()) /
340         static_cast<float>(old_bounds.height());
341     out_transform.Translate(new_bounds.x() - old_bounds.x(),
342                             new_bounds.y() - old_bounds.y());
343     out_transform.Scale(scale_x, scale_y);
344     old_layer->SetTransform(out_transform);
345     if (old_on_top) {
346       // The old layer is on top, and should fade out.  The new layer below will
347       // stay opaque to block the desktop.
348       old_layer->SetOpacity(kWindowAnimation_HideOpacity);
349     }
350     // In tests |old_layer| is deleted here, as animations have zero duration.
351     old_layer = NULL;
352   }
353
354   // Set the new layer's current transform, such that the user sees a scaled
355   // version of the window with the original bounds at the original position.
356   gfx::Transform in_transform;
357   const float scale_x = static_cast<float>(old_bounds.width()) /
358       static_cast<float>(new_bounds.width());
359   const float scale_y = static_cast<float>(old_bounds.height()) /
360       static_cast<float>(new_bounds.height());
361   in_transform.Translate(old_bounds.x() - new_bounds.x(),
362                                old_bounds.y() - new_bounds.y());
363   in_transform.Scale(scale_x, scale_y);
364   window->layer()->SetTransform(in_transform);
365   if (!old_on_top) {
366     // The new layer is on top and should fade in.  The old layer below will
367     // stay opaque and block the desktop.
368     window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
369   }
370   {
371     // Animate the new layer to the identity transform, so the window goes to
372     // its newly set bounds.
373     ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
374     settings.SetTransitionDuration(duration);
375     settings.SetTweenType(tween_type);
376     window->layer()->SetTransform(gfx::Transform());
377     if (!old_on_top) {
378       // New layer is on top, fade it in.
379       window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
380     }
381   }
382   return duration;
383 }
384
385 void CrossFadeToBounds(aura::Window* window, const gfx::Rect& new_bounds) {
386   // Some test results in invoking CrossFadeToBounds when window is not visible.
387   // No animation is necessary in that case, thus just change the bounds and
388   // quit.
389   if (!window->TargetVisibility()) {
390     window->SetBounds(new_bounds);
391     return;
392   }
393
394   const gfx::Rect old_bounds = window->bounds();
395
396   // Create fresh layers for the window and all its children to paint into.
397   // Takes ownership of the old layer and all its children, which will be
398   // cleaned up after the animation completes.
399   // Specify |set_bounds| to true here to keep the old bounds in the child
400   // windows of |window|.
401   ui::Layer* old_layer = views::corewm::RecreateWindowLayers(window, true);
402   ui::Layer* new_layer = window->layer();
403
404   // Resize the window to the new size, which will force a layout and paint.
405   window->SetBounds(new_bounds);
406
407   // Ensure the higher-resolution layer is on top.
408   bool old_on_top = (old_bounds.width() > new_bounds.width());
409   if (old_on_top)
410     old_layer->parent()->StackBelow(new_layer, old_layer);
411   else
412     old_layer->parent()->StackAbove(new_layer, old_layer);
413
414   CrossFadeImpl(window, old_layer, gfx::Tween::EASE_OUT);
415 }
416
417 base::TimeDelta GetCrossFadeDuration(aura::Window* window,
418                                      const gfx::Rect& old_bounds,
419                                      const gfx::Rect& new_bounds) {
420   if (views::corewm::WindowAnimationsDisabled(window))
421     return base::TimeDelta();
422
423   int old_area = old_bounds.width() * old_bounds.height();
424   int new_area = new_bounds.width() * new_bounds.height();
425   int max_area = std::max(old_area, new_area);
426   // Avoid divide by zero.
427   if (max_area == 0)
428     return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS);
429
430   int delta_area = std::abs(old_area - new_area);
431   // If the area didn't change, the animation is instantaneous.
432   if (delta_area == 0)
433     return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS);
434
435   float factor =
436       static_cast<float>(delta_area) / static_cast<float>(max_area);
437   const float kRange = kCrossFadeDurationMaxMs - kCrossFadeDurationMinMs;
438   return base::TimeDelta::FromMilliseconds(
439       Round64(kCrossFadeDurationMinMs + (factor * kRange)));
440 }
441
442 bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) {
443   if (views::corewm::WindowAnimationsDisabled(window))
444     return false;
445
446   // Attempt to run CoreWm supplied animation types.
447   if (views::corewm::AnimateOnChildWindowVisibilityChanged(window, visible))
448     return true;
449
450   // Otherwise try to run an Ash-specific animation.
451   if (visible)
452     return AnimateShowWindow(window);
453   // Don't start hiding the window again if it's already being hidden.
454   return window->layer()->GetTargetOpacity() != 0.0f &&
455       AnimateHideWindow(window);
456 }
457
458 std::vector<ui::LayerAnimationSequence*>
459 CreateBrightnessGrayscaleAnimationSequence(float target_value,
460                                            base::TimeDelta duration) {
461   gfx::Tween::Type animation_type = gfx::Tween::EASE_OUT;
462   scoped_ptr<ui::LayerAnimationSequence> brightness_sequence(
463       new ui::LayerAnimationSequence());
464   scoped_ptr<ui::LayerAnimationSequence> grayscale_sequence(
465       new ui::LayerAnimationSequence());
466
467   scoped_ptr<ui::LayerAnimationElement> brightness_element(
468       ui::LayerAnimationElement::CreateBrightnessElement(
469           target_value, duration));
470   brightness_element->set_tween_type(animation_type);
471   brightness_sequence->AddElement(brightness_element.release());
472
473   scoped_ptr<ui::LayerAnimationElement> grayscale_element(
474       ui::LayerAnimationElement::CreateGrayscaleElement(
475           target_value, duration));
476   grayscale_element->set_tween_type(animation_type);
477   grayscale_sequence->AddElement(grayscale_element.release());
478
479   std::vector<ui::LayerAnimationSequence*> animations;
480   animations.push_back(brightness_sequence.release());
481   animations.push_back(grayscale_sequence.release());
482
483   return animations;
484 }
485
486 // Returns scale related to the specified AshWindowScaleType.
487 void SetTransformForScaleAnimation(ui::Layer* layer,
488                                    LayerScaleAnimationDirection type) {
489   const float scale =
490       type == LAYER_SCALE_ANIMATION_ABOVE ? kLayerScaleAboveSize :
491           kLayerScaleBelowSize;
492   gfx::Transform transform;
493   transform.Translate(-layer->bounds().width() * (scale - 1.0f) / 2,
494                       -layer->bounds().height() * (scale - 1.0f) / 2);
495   transform.Scale(scale, scale);
496   layer->SetTransform(transform);
497 }
498
499 gfx::Rect GetMinimizeAnimationTargetBoundsInScreen(aura::Window* window) {
500   Shelf* shelf = Shelf::ForWindow(window);
501   // Shelf is created lazily and can be NULL.
502   if (!shelf)
503     return gfx::Rect();
504   gfx::Rect item_rect = shelf->GetScreenBoundsOfItemIconForWindow(window);
505
506   // The launcher item is visible and has an icon.
507   if (!item_rect.IsEmpty())
508     return item_rect;
509
510   // If both the icon width and height are 0, then there is no icon in the
511   // launcher for |window| or the icon is hidden in the overflow menu. If the
512   // launcher is auto hidden, one of the height or width will be 0 but the
513   // position in the launcher and the major dimension are still reported
514   // correctly and the window can be animated to the launcher item's light
515   // bar.
516   if (item_rect.width() != 0 || item_rect.height() != 0) {
517     internal::ShelfLayoutManager* layout_manager =
518         internal::ShelfLayoutManager::ForShelf(window);
519     if (layout_manager->visibility_state() == SHELF_AUTO_HIDE) {
520       gfx::Rect shelf_bounds = shelf->shelf_widget()->GetWindowBoundsInScreen();
521       switch (layout_manager->GetAlignment()) {
522         case SHELF_ALIGNMENT_BOTTOM:
523           item_rect.set_y(shelf_bounds.y());
524           break;
525         case SHELF_ALIGNMENT_LEFT:
526           item_rect.set_x(shelf_bounds.right());
527           break;
528         case SHELF_ALIGNMENT_RIGHT:
529           item_rect.set_x(shelf_bounds.x());
530           break;
531         case SHELF_ALIGNMENT_TOP:
532           item_rect.set_y(shelf_bounds.bottom());
533           break;
534       }
535       return item_rect;
536     }
537   }
538
539   // Assume the shelf is overflowed, zoom off to the bottom right of the
540   // work area.
541   gfx::Rect work_area =
542       Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
543   return gfx::Rect(work_area.right(), work_area.bottom(), 0, 0);
544 }
545
546 }  // namespace ash