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