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/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"
     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       ScreenAsh::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, -1);
    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   virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
    290                                        base::TimeTicks timebase,
    291                                        base::TimeDelta interval) OVERRIDE {
    292   }
    293 
    294   // aura::WindowObserver overrides:
    295   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
    296     // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
    297     layer_->GetAnimator()->StopAnimating();
    298   }
    299   virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE {
    300     layer_->GetAnimator()->StopAnimating();
    301   }
    302 
    303   // ui::ImplicitAnimationObserver overrides:
    304   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
    305     delete this;
    306   }
    307 
    308  private:
    309   aura::Window* window_;  // not owned
    310   ui::Layer* layer_;  // owned
    311 
    312   DISALLOW_COPY_AND_ASSIGN(CrossFadeObserver);
    313 };
    314 
    315 // Implementation of cross fading. Window is the window being cross faded. It
    316 // should be at the target bounds. |old_layer| the previous layer from |window|.
    317 // This takes ownership of |old_layer| and deletes when the animation is done.
    318 // |pause_duration| is the duration to pause at the current bounds before
    319 // animating. Returns the duration of the fade.
    320 base::TimeDelta CrossFadeImpl(aura::Window* window,
    321                               ui::Layer* old_layer,
    322                               gfx::Tween::Type tween_type) {
    323   const gfx::Rect old_bounds(old_layer->bounds());
    324   const gfx::Rect new_bounds(window->bounds());
    325   const bool old_on_top = (old_bounds.width() > new_bounds.width());
    326 
    327   // Shorten the animation if there's not much visual movement.
    328   const base::TimeDelta duration = GetCrossFadeDuration(window,
    329                                                         old_bounds, new_bounds);
    330 
    331   // Scale up the old layer while translating to new position.
    332   {
    333     old_layer->GetAnimator()->StopAnimating();
    334     ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
    335 
    336     // Animation observer owns the old layer and deletes itself.
    337     settings.AddObserver(new CrossFadeObserver(window, old_layer));
    338     settings.SetTransitionDuration(duration);
    339     settings.SetTweenType(tween_type);
    340     gfx::Transform out_transform;
    341     float scale_x = static_cast<float>(new_bounds.width()) /
    342         static_cast<float>(old_bounds.width());
    343     float scale_y = static_cast<float>(new_bounds.height()) /
    344         static_cast<float>(old_bounds.height());
    345     out_transform.Translate(new_bounds.x() - old_bounds.x(),
    346                             new_bounds.y() - old_bounds.y());
    347     out_transform.Scale(scale_x, scale_y);
    348     old_layer->SetTransform(out_transform);
    349     if (old_on_top) {
    350       // The old layer is on top, and should fade out.  The new layer below will
    351       // stay opaque to block the desktop.
    352       old_layer->SetOpacity(kWindowAnimation_HideOpacity);
    353     }
    354     // In tests |old_layer| is deleted here, as animations have zero duration.
    355     old_layer = NULL;
    356   }
    357 
    358   // Set the new layer's current transform, such that the user sees a scaled
    359   // version of the window with the original bounds at the original position.
    360   gfx::Transform in_transform;
    361   const float scale_x = static_cast<float>(old_bounds.width()) /
    362       static_cast<float>(new_bounds.width());
    363   const float scale_y = static_cast<float>(old_bounds.height()) /
    364       static_cast<float>(new_bounds.height());
    365   in_transform.Translate(old_bounds.x() - new_bounds.x(),
    366                                old_bounds.y() - new_bounds.y());
    367   in_transform.Scale(scale_x, scale_y);
    368   window->layer()->SetTransform(in_transform);
    369   if (!old_on_top) {
    370     // The new layer is on top and should fade in.  The old layer below will
    371     // stay opaque and block the desktop.
    372     window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
    373   }
    374   {
    375     // Animate the new layer to the identity transform, so the window goes to
    376     // its newly set bounds.
    377     ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
    378     settings.SetTransitionDuration(duration);
    379     settings.SetTweenType(tween_type);
    380     window->layer()->SetTransform(gfx::Transform());
    381     if (!old_on_top) {
    382       // New layer is on top, fade it in.
    383       window->layer()->SetOpacity(kWindowAnimation_ShowOpacity);
    384     }
    385   }
    386   return duration;
    387 }
    388 
    389 void CrossFadeToBounds(aura::Window* window, const gfx::Rect& new_bounds) {
    390   // Some test results in invoking CrossFadeToBounds when window is not visible.
    391   // No animation is necessary in that case, thus just change the bounds and
    392   // quit.
    393   if (!window->TargetVisibility()) {
    394     window->SetBounds(new_bounds);
    395     return;
    396   }
    397 
    398   const gfx::Rect old_bounds = window->bounds();
    399 
    400   // Create fresh layers for the window and all its children to paint into.
    401   // Takes ownership of the old layer and all its children, which will be
    402   // cleaned up after the animation completes.
    403   // Specify |set_bounds| to true here to keep the old bounds in the child
    404   // windows of |window|.
    405   ui::Layer* old_layer = views::corewm::RecreateWindowLayers(window, true);
    406   ui::Layer* new_layer = window->layer();
    407 
    408   // Resize the window to the new size, which will force a layout and paint.
    409   window->SetBounds(new_bounds);
    410 
    411   // Ensure the higher-resolution layer is on top.
    412   bool old_on_top = (old_bounds.width() > new_bounds.width());
    413   if (old_on_top)
    414     old_layer->parent()->StackBelow(new_layer, old_layer);
    415   else
    416     old_layer->parent()->StackAbove(new_layer, old_layer);
    417 
    418   CrossFadeImpl(window, old_layer, gfx::Tween::EASE_OUT);
    419 }
    420 
    421 base::TimeDelta GetCrossFadeDuration(aura::Window* window,
    422                                      const gfx::Rect& old_bounds,
    423                                      const gfx::Rect& new_bounds) {
    424   if (views::corewm::WindowAnimationsDisabled(window))
    425     return base::TimeDelta();
    426 
    427   int old_area = old_bounds.width() * old_bounds.height();
    428   int new_area = new_bounds.width() * new_bounds.height();
    429   int max_area = std::max(old_area, new_area);
    430   // Avoid divide by zero.
    431   if (max_area == 0)
    432     return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS);
    433 
    434   int delta_area = std::abs(old_area - new_area);
    435   // If the area didn't change, the animation is instantaneous.
    436   if (delta_area == 0)
    437     return base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS);
    438 
    439   float factor =
    440       static_cast<float>(delta_area) / static_cast<float>(max_area);
    441   const float kRange = kCrossFadeDurationMaxMs - kCrossFadeDurationMinMs;
    442   return base::TimeDelta::FromMilliseconds(
    443       Round64(kCrossFadeDurationMinMs + (factor * kRange)));
    444 }
    445 
    446 bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) {
    447   if (views::corewm::WindowAnimationsDisabled(window))
    448     return false;
    449 
    450   // Attempt to run CoreWm supplied animation types.
    451   if (views::corewm::AnimateOnChildWindowVisibilityChanged(window, visible))
    452     return true;
    453 
    454   // Otherwise try to run an Ash-specific animation.
    455   if (visible)
    456     return AnimateShowWindow(window);
    457   // Don't start hiding the window again if it's already being hidden.
    458   return window->layer()->GetTargetOpacity() != 0.0f &&
    459       AnimateHideWindow(window);
    460 }
    461 
    462 std::vector<ui::LayerAnimationSequence*>
    463 CreateBrightnessGrayscaleAnimationSequence(float target_value,
    464                                            base::TimeDelta duration) {
    465   gfx::Tween::Type animation_type = gfx::Tween::EASE_OUT;
    466   scoped_ptr<ui::LayerAnimationSequence> brightness_sequence(
    467       new ui::LayerAnimationSequence());
    468   scoped_ptr<ui::LayerAnimationSequence> grayscale_sequence(
    469       new ui::LayerAnimationSequence());
    470 
    471   scoped_ptr<ui::LayerAnimationElement> brightness_element(
    472       ui::LayerAnimationElement::CreateBrightnessElement(
    473           target_value, duration));
    474   brightness_element->set_tween_type(animation_type);
    475   brightness_sequence->AddElement(brightness_element.release());
    476 
    477   scoped_ptr<ui::LayerAnimationElement> grayscale_element(
    478       ui::LayerAnimationElement::CreateGrayscaleElement(
    479           target_value, duration));
    480   grayscale_element->set_tween_type(animation_type);
    481   grayscale_sequence->AddElement(grayscale_element.release());
    482 
    483   std::vector<ui::LayerAnimationSequence*> animations;
    484   animations.push_back(brightness_sequence.release());
    485   animations.push_back(grayscale_sequence.release());
    486 
    487   return animations;
    488 }
    489 
    490 // Returns scale related to the specified AshWindowScaleType.
    491 void SetTransformForScaleAnimation(ui::Layer* layer,
    492                                    LayerScaleAnimationDirection type) {
    493   const float scale =
    494       type == LAYER_SCALE_ANIMATION_ABOVE ? kLayerScaleAboveSize :
    495           kLayerScaleBelowSize;
    496   gfx::Transform transform;
    497   transform.Translate(-layer->bounds().width() * (scale - 1.0f) / 2,
    498                       -layer->bounds().height() * (scale - 1.0f) / 2);
    499   transform.Scale(scale, scale);
    500   layer->SetTransform(transform);
    501 }
    502 
    503 gfx::Rect GetMinimizeAnimationTargetBoundsInScreen(aura::Window* window) {
    504   Launcher* launcher = Launcher::ForWindow(window);
    505   // Shelf is created lazily and can be NULL.
    506   if (!launcher)
    507     return gfx::Rect();
    508   gfx::Rect item_rect = launcher->GetScreenBoundsOfItemIconForWindow(window);
    509 
    510   // The launcher item is visible and has an icon.
    511   if (!item_rect.IsEmpty())
    512     return item_rect;
    513 
    514   // If both the icon width and height are 0, then there is no icon in the
    515   // launcher for |window| or the icon is hidden in the overflow menu. If the
    516   // launcher is auto hidden, one of the height or width will be 0 but the
    517   // position in the launcher and the major dimension are still reported
    518   // correctly and the window can be animated to the launcher item's light
    519   // bar.
    520   if (item_rect.width() != 0 || item_rect.height() != 0) {
    521     internal::ShelfLayoutManager* layout_manager =
    522         internal::ShelfLayoutManager::ForLauncher(window);
    523     if (layout_manager->visibility_state() == SHELF_AUTO_HIDE) {
    524       gfx::Rect shelf_bounds =
    525           launcher->shelf_widget()->GetWindowBoundsInScreen();
    526       switch (layout_manager->GetAlignment()) {
    527         case SHELF_ALIGNMENT_BOTTOM:
    528           item_rect.set_y(shelf_bounds.y());
    529           break;
    530         case SHELF_ALIGNMENT_LEFT:
    531           item_rect.set_x(shelf_bounds.right());
    532           break;
    533         case SHELF_ALIGNMENT_RIGHT:
    534           item_rect.set_x(shelf_bounds.x());
    535           break;
    536         case SHELF_ALIGNMENT_TOP:
    537           item_rect.set_y(shelf_bounds.bottom());
    538           break;
    539       }
    540       return item_rect;
    541     }
    542   }
    543 
    544   // Assume the launcher is overflowed, zoom off to the bottom right of the
    545   // work area.
    546   gfx::Rect work_area =
    547       Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
    548   return gfx::Rect(work_area.right(), work_area.bottom(), 0, 0);
    549 }
    550 
    551 }  // namespace ash
    552