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