Home | History | Annotate | Download | only in wm
      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 
     65 int64 Round64(float f) {
     66   return static_cast<int64>(f + 0.5f);
     67 }
     68 
     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 
     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 
    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 
    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 
    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 
    218 void AnimateShowWindow_BrightnessGrayscale(aura::Window* window) {
    219   AnimateShowHideWindowCommon_BrightnessGrayscale(window, true);
    220 }
    221 
    222 void AnimateHideWindow_BrightnessGrayscale(aura::Window* window) {
    223   AnimateShowHideWindowCommon_BrightnessGrayscale(window, false);
    224 }
    225 
    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 
    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.
    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   }
    280   virtual ~CrossFadeObserver() {
    281     window_->RemoveObserver(this);
    282     window_ = NULL;
    283     layer_owner_->root()->GetCompositor()->RemoveObserver(this);
    284   }
    285 
    286   // ui::CompositorObserver overrides:
    287   virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {
    288   }
    289   virtual void OnCompositingStarted(ui::Compositor* compositor,
    290                                     base::TimeTicks start_time) OVERRIDE {
    291   }
    292   virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE {
    293   }
    294   virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE {
    295     // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
    296     layer_owner_->root()->GetAnimator()->StopAnimating();
    297   }
    298   virtual void OnCompositingLockStateChanged(
    299       ui::Compositor* compositor) OVERRIDE {
    300   }
    301 
    302   // aura::WindowObserver overrides:
    303   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
    304     // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
    305     layer_owner_->root()->GetAnimator()->StopAnimating();
    306   }
    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:
    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 
    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 
    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*>
    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.
    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 
    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