Home | History | Annotate | Download | only in shelf
      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/shelf/shelf_layout_manager.h"
      6 
      7 #include <algorithm>
      8 #include <cmath>
      9 #include <cstring>
     10 #include <string>
     11 #include <vector>
     12 
     13 #include "ash/ash_switches.h"
     14 #include "ash/launcher/launcher.h"
     15 #include "ash/launcher/launcher_types.h"
     16 #include "ash/root_window_controller.h"
     17 #include "ash/screen_ash.h"
     18 #include "ash/session_state_delegate.h"
     19 #include "ash/shelf/shelf_bezel_event_filter.h"
     20 #include "ash/shelf/shelf_layout_manager_observer.h"
     21 #include "ash/shelf/shelf_widget.h"
     22 #include "ash/shell.h"
     23 #include "ash/shell_window_ids.h"
     24 #include "ash/system/status_area_widget.h"
     25 #include "ash/wm/gestures/shelf_gesture_handler.h"
     26 #include "ash/wm/mru_window_tracker.h"
     27 #include "ash/wm/property_util.h"
     28 #include "ash/wm/window_animations.h"
     29 #include "ash/wm/window_properties.h"
     30 #include "ash/wm/window_util.h"
     31 #include "ash/wm/workspace_controller.h"
     32 #include "base/auto_reset.h"
     33 #include "base/command_line.h"
     34 #include "base/command_line.h"
     35 #include "base/i18n/rtl.h"
     36 #include "base/strings/string_number_conversions.h"
     37 #include "base/strings/string_util.h"
     38 #include "ui/aura/client/activation_client.h"
     39 #include "ui/aura/client/cursor_client.h"
     40 #include "ui/aura/root_window.h"
     41 #include "ui/base/events/event.h"
     42 #include "ui/base/events/event_handler.h"
     43 #include "ui/base/ui_base_switches.h"
     44 #include "ui/compositor/layer.h"
     45 #include "ui/compositor/layer_animation_observer.h"
     46 #include "ui/compositor/layer_animator.h"
     47 #include "ui/compositor/scoped_layer_animation_settings.h"
     48 #include "ui/gfx/screen.h"
     49 #include "ui/views/widget/widget.h"
     50 
     51 namespace ash {
     52 namespace internal {
     53 
     54 namespace {
     55 
     56 // Delay before showing the launcher. This is after the mouse stops moving.
     57 const int kAutoHideDelayMS = 200;
     58 
     59 // To avoid hiding the shelf when the mouse transitions from a message bubble
     60 // into the shelf, the hit test area is enlarged by this amount of pixels to
     61 // keep the shelf from hiding.
     62 const int kNotificationBubbleGapHeight = 6;
     63 
     64 // The maximum size of the region on the display opposing the shelf managed by
     65 // this ShelfLayoutManager which can trigger showing the shelf.
     66 // For instance:
     67 // - Primary display is left of secondary display.
     68 // - Shelf is left aligned
     69 // - This ShelfLayoutManager manages the shelf for the secondary display.
     70 // |kMaxAutoHideShowShelfRegionSize| refers to the maximum size of the region
     71 // from the right edge of the primary display which can trigger showing the
     72 // auto hidden shelf. The region is used to make it easier to trigger showing
     73 // the auto hidden shelf when the shelf is on the boundary between displays.
     74 const int kMaxAutoHideShowShelfRegionSize = 10;
     75 
     76 // Const inset from the edget of the shelf to the edget of the status area.
     77 const int kStatusAreaInset = 3;
     78 
     79 ui::Layer* GetLayer(views::Widget* widget) {
     80   return widget->GetNativeView()->layer();
     81 }
     82 
     83 bool IsDraggingTrayEnabled() {
     84   static bool dragging_tray_allowed = CommandLine::ForCurrentProcess()->
     85       HasSwitch(ash::switches::kAshEnableTrayDragging);
     86   return dragging_tray_allowed;
     87 }
     88 
     89 }  // namespace
     90 
     91 // static
     92 const int ShelfLayoutManager::kWorkspaceAreaVisibleInset = 2;
     93 
     94 // static
     95 const int ShelfLayoutManager::kWorkspaceAreaAutoHideInset = 5;
     96 
     97 // static
     98 const int ShelfLayoutManager::kAutoHideSize = 3;
     99 
    100 // static
    101 const int ShelfLayoutManager::kShelfSize = 47;
    102 
    103 int ShelfLayoutManager::GetPreferredShelfSize() {
    104   return ash::switches::UseAlternateShelfLayout() ?
    105       ShelfLayoutManager::kShelfSize : kLauncherPreferredSize;
    106 }
    107 
    108 // ShelfLayoutManager::AutoHideEventFilter -------------------------------------
    109 
    110 // Notifies ShelfLayoutManager any time the mouse moves.
    111 class ShelfLayoutManager::AutoHideEventFilter : public ui::EventHandler {
    112  public:
    113   explicit AutoHideEventFilter(ShelfLayoutManager* shelf);
    114   virtual ~AutoHideEventFilter();
    115 
    116   // Returns true if the last mouse event was a mouse drag.
    117   bool in_mouse_drag() const { return in_mouse_drag_; }
    118 
    119   // Overridden from ui::EventHandler:
    120   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
    121   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
    122 
    123  private:
    124   ShelfLayoutManager* shelf_;
    125   bool in_mouse_drag_;
    126   ShelfGestureHandler gesture_handler_;
    127   DISALLOW_COPY_AND_ASSIGN(AutoHideEventFilter);
    128 };
    129 
    130 ShelfLayoutManager::AutoHideEventFilter::AutoHideEventFilter(
    131     ShelfLayoutManager* shelf)
    132     : shelf_(shelf),
    133       in_mouse_drag_(false) {
    134   Shell::GetInstance()->AddPreTargetHandler(this);
    135 }
    136 
    137 ShelfLayoutManager::AutoHideEventFilter::~AutoHideEventFilter() {
    138   Shell::GetInstance()->RemovePreTargetHandler(this);
    139 }
    140 
    141 void ShelfLayoutManager::AutoHideEventFilter::OnMouseEvent(
    142     ui::MouseEvent* event) {
    143   // This also checks IsShelfWindow() to make sure we don't attempt to hide the
    144   // shelf if the mouse down occurs on the shelf.
    145   in_mouse_drag_ = (event->type() == ui::ET_MOUSE_DRAGGED ||
    146                     (in_mouse_drag_ && event->type() != ui::ET_MOUSE_RELEASED &&
    147                      event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)) &&
    148       !shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()));
    149   if (event->type() == ui::ET_MOUSE_MOVED)
    150     shelf_->UpdateAutoHideState();
    151   return;
    152 }
    153 
    154 void ShelfLayoutManager::AutoHideEventFilter::OnGestureEvent(
    155     ui::GestureEvent* event) {
    156   if (shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()))) {
    157     if (gesture_handler_.ProcessGestureEvent(*event))
    158       event->StopPropagation();
    159   }
    160 }
    161 
    162 // ShelfLayoutManager:UpdateShelfObserver --------------------------------------
    163 
    164 // UpdateShelfObserver is used to delay updating the background until the
    165 // animation completes.
    166 class ShelfLayoutManager::UpdateShelfObserver
    167     : public ui::ImplicitAnimationObserver {
    168  public:
    169   explicit UpdateShelfObserver(ShelfLayoutManager* shelf) : shelf_(shelf) {
    170     shelf_->update_shelf_observer_ = this;
    171   }
    172 
    173   void Detach() {
    174     shelf_ = NULL;
    175   }
    176 
    177   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
    178     if (shelf_) {
    179       shelf_->UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
    180     }
    181     delete this;
    182   }
    183 
    184  private:
    185   virtual ~UpdateShelfObserver() {
    186     if (shelf_)
    187       shelf_->update_shelf_observer_ = NULL;
    188   }
    189 
    190   // Shelf we're in. NULL if deleted before we're deleted.
    191   ShelfLayoutManager* shelf_;
    192 
    193   DISALLOW_COPY_AND_ASSIGN(UpdateShelfObserver);
    194 };
    195 
    196 // ShelfLayoutManager ----------------------------------------------------------
    197 
    198 ShelfLayoutManager::ShelfLayoutManager(ShelfWidget* shelf)
    199     : root_window_(shelf->GetNativeView()->GetRootWindow()),
    200       in_layout_(false),
    201       auto_hide_behavior_(SHELF_AUTO_HIDE_BEHAVIOR_NEVER),
    202       alignment_(SHELF_ALIGNMENT_BOTTOM),
    203       shelf_(shelf),
    204       workspace_controller_(NULL),
    205       window_overlaps_shelf_(false),
    206       mouse_over_shelf_when_auto_hide_timer_started_(false),
    207       bezel_event_filter_(new ShelfBezelEventFilter(this)),
    208       gesture_drag_status_(GESTURE_DRAG_NONE),
    209       gesture_drag_amount_(0.f),
    210       gesture_drag_auto_hide_state_(SHELF_AUTO_HIDE_SHOWN),
    211       update_shelf_observer_(NULL) {
    212   Shell::GetInstance()->AddShellObserver(this);
    213   aura::client::GetActivationClient(root_window_)->AddObserver(this);
    214 }
    215 
    216 ShelfLayoutManager::~ShelfLayoutManager() {
    217   if (update_shelf_observer_)
    218     update_shelf_observer_->Detach();
    219 
    220   FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, WillDeleteShelf());
    221   Shell::GetInstance()->RemoveShellObserver(this);
    222   aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
    223 }
    224 
    225 void ShelfLayoutManager::SetAutoHideBehavior(ShelfAutoHideBehavior behavior) {
    226   if (auto_hide_behavior_ == behavior)
    227     return;
    228   auto_hide_behavior_ = behavior;
    229   UpdateVisibilityState();
    230   FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
    231                     OnAutoHideBehaviorChanged(root_window_,
    232                                               auto_hide_behavior_));
    233 }
    234 
    235 void ShelfLayoutManager::PrepareForShutdown() {
    236   // Clear all event filters, otherwise sometimes those filters may catch
    237   // synthesized mouse event and cause crashes during the shutdown.
    238   set_workspace_controller(NULL);
    239   auto_hide_event_filter_.reset();
    240   bezel_event_filter_.reset();
    241 }
    242 
    243 bool ShelfLayoutManager::IsVisible() const {
    244   // status_area_widget() may be NULL during the shutdown.
    245   return shelf_->status_area_widget() &&
    246       shelf_->status_area_widget()->IsVisible() &&
    247       (state_.visibility_state == SHELF_VISIBLE ||
    248        (state_.visibility_state == SHELF_AUTO_HIDE &&
    249         state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN));
    250 }
    251 
    252 bool ShelfLayoutManager::SetAlignment(ShelfAlignment alignment) {
    253   if (alignment_ == alignment)
    254     return false;
    255 
    256   alignment_ = alignment;
    257   shelf_->SetAlignment(alignment);
    258   LayoutShelf();
    259   return true;
    260 }
    261 
    262 gfx::Rect ShelfLayoutManager::GetIdealBounds() {
    263   gfx::Rect bounds(
    264       ScreenAsh::GetDisplayBoundsInParent(shelf_->GetNativeView()));
    265   int width = 0, height = 0;
    266   GetShelfSize(&width, &height);
    267   return SelectValueForShelfAlignment(
    268       gfx::Rect(bounds.x(), bounds.bottom() - height, bounds.width(), height),
    269       gfx::Rect(bounds.x(), bounds.y(), width, bounds.height()),
    270       gfx::Rect(bounds.right() - width, bounds.y(), width, bounds.height()),
    271       gfx::Rect(bounds.x(), bounds.y(), bounds.width(), height));
    272 }
    273 
    274 void ShelfLayoutManager::LayoutShelf() {
    275   base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
    276   StopAnimating();
    277   TargetBounds target_bounds;
    278   CalculateTargetBounds(state_, &target_bounds);
    279   GetLayer(shelf_)->SetOpacity(target_bounds.opacity);
    280   shelf_->SetWidgetBounds(
    281       ScreenAsh::ConvertRectToScreen(
    282           shelf_->GetNativeView()->parent(),
    283           target_bounds.shelf_bounds_in_root));
    284   if (shelf_->launcher())
    285     shelf_->launcher()->SetLauncherViewBounds(
    286         target_bounds.launcher_bounds_in_shelf);
    287   GetLayer(shelf_->status_area_widget())->SetOpacity(
    288       target_bounds.status_opacity);
    289   // TODO(harrym): Once status area widget is a child view of shelf
    290   // this can be simplified.
    291   gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
    292   status_bounds.set_x(status_bounds.x() +
    293                       target_bounds.shelf_bounds_in_root.x());
    294   status_bounds.set_y(status_bounds.y() +
    295                       target_bounds.shelf_bounds_in_root.y());
    296   shelf_->status_area_widget()->SetBounds(
    297       ScreenAsh::ConvertRectToScreen(
    298           shelf_->status_area_widget()->GetNativeView()->parent(),
    299           status_bounds));
    300   Shell::GetInstance()->SetDisplayWorkAreaInsets(
    301       root_window_, target_bounds.work_area_insets);
    302   UpdateHitTestBounds();
    303 }
    304 
    305 ShelfVisibilityState ShelfLayoutManager::CalculateShelfVisibility() {
    306   switch(auto_hide_behavior_) {
    307     case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
    308       return SHELF_AUTO_HIDE;
    309     case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
    310       return SHELF_VISIBLE;
    311     case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
    312       return SHELF_HIDDEN;
    313   }
    314   return SHELF_VISIBLE;
    315 }
    316 
    317 void ShelfLayoutManager::UpdateVisibilityState() {
    318   if (Shell::GetInstance()->session_state_delegate()->IsScreenLocked()) {
    319     SetState(SHELF_VISIBLE);
    320   } else {
    321     // TODO(zelidrag): Verify shelf drag animation still shows on the device
    322     // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN.
    323     WorkspaceWindowState window_state(workspace_controller_->GetWindowState());
    324     switch (window_state) {
    325       case WORKSPACE_WINDOW_STATE_FULL_SCREEN:
    326         if (FullscreenWithMinimalChrome()) {
    327           SetState(SHELF_AUTO_HIDE);
    328         } else {
    329           SetState(SHELF_HIDDEN);
    330         }
    331         break;
    332       case WORKSPACE_WINDOW_STATE_MAXIMIZED:
    333         SetState(CalculateShelfVisibility());
    334         break;
    335       case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF:
    336       case WORKSPACE_WINDOW_STATE_DEFAULT:
    337         SetState(CalculateShelfVisibility());
    338         SetWindowOverlapsShelf(window_state ==
    339                                WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF);
    340         break;
    341     }
    342   }
    343 }
    344 
    345 void ShelfLayoutManager::UpdateAutoHideState() {
    346   ShelfAutoHideState auto_hide_state =
    347       CalculateAutoHideState(state_.visibility_state);
    348   if (auto_hide_state != state_.auto_hide_state) {
    349     if (auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
    350       // Hides happen immediately.
    351       SetState(state_.visibility_state);
    352     } else {
    353       if (!auto_hide_timer_.IsRunning()) {
    354         mouse_over_shelf_when_auto_hide_timer_started_ =
    355             shelf_->GetWindowBoundsInScreen().Contains(
    356                 Shell::GetScreen()->GetCursorScreenPoint());
    357       }
    358       auto_hide_timer_.Start(
    359           FROM_HERE,
    360           base::TimeDelta::FromMilliseconds(kAutoHideDelayMS),
    361           this, &ShelfLayoutManager::UpdateAutoHideStateNow);
    362     }
    363   } else {
    364     StopAutoHideTimer();
    365   }
    366 }
    367 
    368 void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) {
    369   window_overlaps_shelf_ = value;
    370   UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
    371 }
    372 
    373 void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) {
    374   observers_.AddObserver(observer);
    375 }
    376 
    377 void ShelfLayoutManager::RemoveObserver(ShelfLayoutManagerObserver* observer) {
    378   observers_.RemoveObserver(observer);
    379 }
    380 
    381 ////////////////////////////////////////////////////////////////////////////////
    382 // ShelfLayoutManager, Gesture dragging:
    383 
    384 void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) {
    385   gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS;
    386   gesture_drag_amount_ = 0.f;
    387   gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ?
    388       auto_hide_state() : SHELF_AUTO_HIDE_SHOWN;
    389   UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
    390 }
    391 
    392 ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag(
    393     const ui::GestureEvent& gesture) {
    394   bool horizontal = IsHorizontalAlignment();
    395   gesture_drag_amount_ += horizontal ? gesture.details().scroll_y() :
    396                                        gesture.details().scroll_x();
    397   LayoutShelf();
    398 
    399   // Start reveling the status menu when:
    400   //   - dragging up on an already visible shelf
    401   //   - dragging up on a hidden shelf, but it is currently completely visible.
    402   if (horizontal && gesture.details().scroll_y() < 0) {
    403     int min_height = 0;
    404     if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN && shelf_)
    405       min_height = shelf_->GetContentsView()->GetPreferredSize().height();
    406 
    407     if (min_height < shelf_->GetWindowBoundsInScreen().height() &&
    408         gesture.root_location().x() >=
    409         shelf_->status_area_widget()->GetWindowBoundsInScreen().x() &&
    410         IsDraggingTrayEnabled())
    411       return DRAG_TRAY;
    412   }
    413 
    414   return DRAG_SHELF;
    415 }
    416 
    417 void ShelfLayoutManager::CompleteGestureDrag(const ui::GestureEvent& gesture) {
    418   bool horizontal = IsHorizontalAlignment();
    419   bool should_change = false;
    420   if (gesture.type() == ui::ET_GESTURE_SCROLL_END) {
    421     // The visibility of the shelf changes only if the shelf was dragged X%
    422     // along the correct axis. If the shelf was already visible, then the
    423     // direction of the drag does not matter.
    424     const float kDragHideThreshold = 0.4f;
    425     gfx::Rect bounds = GetIdealBounds();
    426     float drag_ratio = fabs(gesture_drag_amount_) /
    427                        (horizontal ?  bounds.height() : bounds.width());
    428     if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
    429       should_change = drag_ratio > kDragHideThreshold;
    430     } else {
    431       bool correct_direction = false;
    432       switch (alignment_) {
    433         case SHELF_ALIGNMENT_BOTTOM:
    434         case SHELF_ALIGNMENT_RIGHT:
    435           correct_direction = gesture_drag_amount_ < 0;
    436           break;
    437         case SHELF_ALIGNMENT_LEFT:
    438         case SHELF_ALIGNMENT_TOP:
    439           correct_direction = gesture_drag_amount_ > 0;
    440           break;
    441       }
    442       should_change = correct_direction && drag_ratio > kDragHideThreshold;
    443     }
    444   } else if (gesture.type() == ui::ET_SCROLL_FLING_START) {
    445     if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
    446       should_change = horizontal ? fabs(gesture.details().velocity_y()) > 0 :
    447                                    fabs(gesture.details().velocity_x()) > 0;
    448     } else {
    449       should_change = SelectValueForShelfAlignment(
    450           gesture.details().velocity_y() < 0,
    451           gesture.details().velocity_x() > 0,
    452           gesture.details().velocity_x() < 0,
    453           gesture.details().velocity_y() > 0);
    454     }
    455   } else {
    456     NOTREACHED();
    457   }
    458 
    459   if (!should_change) {
    460     CancelGestureDrag();
    461     return;
    462   }
    463   if (shelf_) {
    464     shelf_->Deactivate();
    465     shelf_->status_area_widget()->Deactivate();
    466   }
    467   gesture_drag_auto_hide_state_ =
    468       gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
    469       SHELF_AUTO_HIDE_HIDDEN : SHELF_AUTO_HIDE_SHOWN;
    470   ShelfAutoHideBehavior new_auto_hide_behavior =
    471       gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
    472       SHELF_AUTO_HIDE_BEHAVIOR_NEVER : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
    473 
    474   // In fullscreen with minimal chrome, the auto hide behavior affects neither
    475   // the visibility state nor the auto hide state. Set |gesture_drag_status_|
    476   // to GESTURE_DRAG_COMPLETE_IN_PROGRESS to set the auto hide state to
    477   // |gesture_drag_auto_hide_state_|.
    478   gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS;
    479   if (auto_hide_behavior_ != new_auto_hide_behavior)
    480     SetAutoHideBehavior(new_auto_hide_behavior);
    481   else
    482     UpdateVisibilityState();
    483   gesture_drag_status_ = GESTURE_DRAG_NONE;
    484   LayoutShelf();
    485 }
    486 
    487 void ShelfLayoutManager::CancelGestureDrag() {
    488   gesture_drag_status_ = GESTURE_DRAG_NONE;
    489   ui::ScopedLayerAnimationSettings
    490       launcher_settings(GetLayer(shelf_)->GetAnimator()),
    491       status_settings(GetLayer(shelf_->status_area_widget())->GetAnimator());
    492   LayoutShelf();
    493   UpdateVisibilityState();
    494   UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
    495 }
    496 
    497 ////////////////////////////////////////////////////////////////////////////////
    498 // ShelfLayoutManager, aura::LayoutManager implementation:
    499 
    500 void ShelfLayoutManager::OnWindowResized() {
    501   LayoutShelf();
    502 }
    503 
    504 void ShelfLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
    505 }
    506 
    507 void ShelfLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
    508 }
    509 
    510 void ShelfLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
    511 }
    512 
    513 void ShelfLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
    514                                                         bool visible) {
    515 }
    516 
    517 void ShelfLayoutManager::SetChildBounds(aura::Window* child,
    518                                         const gfx::Rect& requested_bounds) {
    519   SetChildBoundsDirect(child, requested_bounds);
    520   // We may contain other widgets (such as frame maximize bubble) but they don't
    521   // effect the layout in anyway.
    522   if (!in_layout_ &&
    523       ((shelf_->GetNativeView() == child) ||
    524        (shelf_->status_area_widget()->GetNativeView() == child))) {
    525     LayoutShelf();
    526   }
    527 }
    528 
    529 void ShelfLayoutManager::OnLockStateChanged(bool locked) {
    530   UpdateVisibilityState();
    531 }
    532 
    533 void ShelfLayoutManager::OnWindowActivated(aura::Window* gained_active,
    534                                            aura::Window* lost_active) {
    535   UpdateAutoHideStateNow();
    536 }
    537 
    538 bool ShelfLayoutManager::IsHorizontalAlignment() const {
    539   return alignment_ == SHELF_ALIGNMENT_BOTTOM ||
    540          alignment_ == SHELF_ALIGNMENT_TOP;
    541 }
    542 
    543 bool ShelfLayoutManager::FullscreenWithMinimalChrome() const {
    544   RootWindowController* controller = GetRootWindowController(root_window_);
    545   if (!controller)
    546     return false;
    547   const aura::Window* window = controller->GetFullscreenWindow();
    548   if (!window)
    549     return false;
    550   if (!window->GetProperty(kFullscreenUsesMinimalChromeKey))
    551     return false;
    552   return true;
    553 }
    554 
    555 // static
    556 ShelfLayoutManager* ShelfLayoutManager::ForLauncher(aura::Window* window) {
    557   ShelfWidget* shelf = RootWindowController::ForLauncher(window)->shelf();
    558   return shelf ? shelf->shelf_layout_manager() : NULL;
    559 }
    560 
    561 ////////////////////////////////////////////////////////////////////////////////
    562 // ShelfLayoutManager, private:
    563 
    564 ShelfLayoutManager::TargetBounds::TargetBounds() : opacity(0.0f) {}
    565 ShelfLayoutManager::TargetBounds::~TargetBounds() {}
    566 
    567 void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
    568   if (!shelf_->GetNativeView())
    569     return;
    570 
    571   State state;
    572   state.visibility_state = visibility_state;
    573   state.auto_hide_state = CalculateAutoHideState(visibility_state);
    574   state.is_screen_locked =
    575       Shell::GetInstance()->session_state_delegate()->IsScreenLocked();
    576   state.window_state = workspace_controller_ ?
    577       workspace_controller_->GetWindowState() : WORKSPACE_WINDOW_STATE_DEFAULT;
    578 
    579   if (state_.Equals(state))
    580     return;  // Nothing changed.
    581 
    582   FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
    583                     WillChangeVisibilityState(visibility_state));
    584 
    585   if (state.visibility_state == SHELF_AUTO_HIDE) {
    586     // When state is SHELF_AUTO_HIDE we need to track when the mouse is over the
    587     // launcher to unhide the shelf. AutoHideEventFilter does that for us.
    588     if (!auto_hide_event_filter_)
    589       auto_hide_event_filter_.reset(new AutoHideEventFilter(this));
    590   } else {
    591     auto_hide_event_filter_.reset(NULL);
    592   }
    593 
    594   StopAutoHideTimer();
    595 
    596   State old_state = state_;
    597   state_ = state;
    598   TargetBounds target_bounds;
    599   CalculateTargetBounds(state_, &target_bounds);
    600 
    601   ui::ScopedLayerAnimationSettings launcher_animation_setter(
    602       GetLayer(shelf_)->GetAnimator());
    603   launcher_animation_setter.SetTransitionDuration(
    604       base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS));
    605   launcher_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
    606   launcher_animation_setter.SetPreemptionStrategy(
    607       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    608   GetLayer(shelf_)->SetBounds(
    609       target_bounds.shelf_bounds_in_root);
    610   GetLayer(shelf_)->SetOpacity(target_bounds.opacity);
    611   ui::ScopedLayerAnimationSettings status_animation_setter(
    612       GetLayer(shelf_->status_area_widget())->GetAnimator());
    613   status_animation_setter.SetTransitionDuration(
    614       base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS));
    615   status_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
    616   status_animation_setter.SetPreemptionStrategy(
    617       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    618 
    619   BackgroundAnimator::ChangeType change_type =
    620       BackgroundAnimator::CHANGE_ANIMATE;
    621   bool delay_background_change = false;
    622 
    623   // Do not animate the background when:
    624   // - Going from a hidden / auto hidden shelf in fullscreen to a visible shelf
    625   //   in maximized mode.
    626   // - Going from an auto hidden shelf in maximized mode to a visible shelf in
    627   //   maximized mode.
    628   if (state.visibility_state == SHELF_VISIBLE &&
    629       state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED &&
    630       old_state.visibility_state != SHELF_VISIBLE) {
    631     change_type = BackgroundAnimator::CHANGE_IMMEDIATE;
    632   } else {
    633     // Delay the animation when the shelf was hidden, and has just been made
    634     // visible (e.g. using a gesture-drag).
    635     if (state.visibility_state == SHELF_VISIBLE &&
    636         old_state.visibility_state == SHELF_AUTO_HIDE &&
    637         old_state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
    638       delay_background_change = true;
    639     }
    640   }
    641 
    642   if (delay_background_change) {
    643     if (update_shelf_observer_)
    644       update_shelf_observer_->Detach();
    645     // UpdateShelfBackground deletes itself when the animation is done.
    646     update_shelf_observer_ = new UpdateShelfObserver(this);
    647     status_animation_setter.AddObserver(update_shelf_observer_);
    648   } else {
    649     UpdateShelfBackground(change_type);
    650   }
    651 
    652   shelf_->SetDimsShelf(
    653       state.visibility_state == SHELF_VISIBLE &&
    654       state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED);
    655 
    656   ui::Layer* layer = GetLayer(shelf_->status_area_widget());
    657   // TODO(harrym): Remove when status_area is view (crbug.com/180422).
    658   gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
    659   status_bounds.set_x(status_bounds.x() +
    660                       target_bounds.shelf_bounds_in_root.x());
    661   status_bounds.set_y(status_bounds.y() +
    662                       target_bounds.shelf_bounds_in_root.y());
    663   layer->SetBounds(status_bounds);
    664   layer->SetOpacity(target_bounds.status_opacity);
    665   Shell::GetInstance()->SetDisplayWorkAreaInsets(
    666       root_window_, target_bounds.work_area_insets);
    667   UpdateHitTestBounds();
    668 
    669   // OnAutoHideStateChanged Should be emitted when:
    670   //  - firstly state changed to auto-hide from other state
    671   //  - or, auto_hide_state has changed
    672   if ((old_state.visibility_state != state_.visibility_state &&
    673        state_.visibility_state == SHELF_AUTO_HIDE) ||
    674       old_state.auto_hide_state != state_.auto_hide_state) {
    675     FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
    676                       OnAutoHideStateChanged(state_.auto_hide_state));
    677   }
    678 }
    679 
    680 void ShelfLayoutManager::StopAnimating() {
    681   GetLayer(shelf_)->GetAnimator()->StopAnimating();
    682   GetLayer(shelf_->status_area_widget())->GetAnimator()->StopAnimating();
    683 }
    684 
    685 void ShelfLayoutManager::GetShelfSize(int* width, int* height) {
    686   *width = *height = 0;
    687   gfx::Size status_size(
    688       shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
    689   if (IsHorizontalAlignment())
    690     *height = GetPreferredShelfSize();
    691   else
    692     *width = GetPreferredShelfSize();
    693 }
    694 
    695 void ShelfLayoutManager::AdjustBoundsBasedOnAlignment(int inset,
    696                                                       gfx::Rect* bounds) const {
    697   bounds->Inset(SelectValueForShelfAlignment(
    698       gfx::Insets(0, 0, inset, 0),
    699       gfx::Insets(0, inset, 0, 0),
    700       gfx::Insets(0, 0, 0, inset),
    701       gfx::Insets(inset, 0, 0, 0)));
    702 }
    703 
    704 void ShelfLayoutManager::CalculateTargetBounds(
    705     const State& state,
    706     TargetBounds* target_bounds) {
    707   const gfx::Rect available_bounds(GetAvailableBounds());
    708   gfx::Rect status_size(
    709       shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
    710   int shelf_width = 0, shelf_height = 0;
    711   GetShelfSize(&shelf_width, &shelf_height);
    712   if (IsHorizontalAlignment())
    713     shelf_width = available_bounds.width();
    714   else
    715     shelf_height = available_bounds.height();
    716 
    717   if (state.visibility_state == SHELF_AUTO_HIDE &&
    718       state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
    719     // Auto-hidden shelf always starts with the default size. If a gesture-drag
    720     // is in progress, then the call to UpdateTargetBoundsForGesture() below
    721     // takes care of setting the height properly.
    722     if (IsHorizontalAlignment())
    723       shelf_height = kAutoHideSize;
    724     else
    725       shelf_width = kAutoHideSize;
    726   } else if (state.visibility_state == SHELF_HIDDEN ||
    727       !keyboard_bounds_.IsEmpty()) {
    728     if (IsHorizontalAlignment())
    729       shelf_height = 0;
    730     else
    731       shelf_width = 0;
    732   }
    733 
    734   target_bounds->shelf_bounds_in_root = SelectValueForShelfAlignment(
    735       gfx::Rect(available_bounds.x(), available_bounds.bottom() - shelf_height,
    736                     available_bounds.width(), shelf_height),
    737       gfx::Rect(available_bounds.x(), available_bounds.y(),
    738                     shelf_width, available_bounds.height()),
    739       gfx::Rect(available_bounds.right() - shelf_width, available_bounds.y(),
    740                     shelf_width, available_bounds.height()),
    741       gfx::Rect(available_bounds.x(), available_bounds.y(),
    742                     available_bounds.width(), shelf_height));
    743 
    744   int status_inset = std::max(0, GetPreferredShelfSize() -
    745       PrimaryAxisValue(status_size.height(), status_size.width()));
    746 
    747   if (ash::switches::UseAlternateShelfLayout())
    748     status_inset = kStatusAreaInset;
    749 
    750   target_bounds->status_bounds_in_shelf = SelectValueForShelfAlignment(
    751       gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
    752                     status_inset, status_size.width(), status_size.height()),
    753       gfx::Rect(shelf_width - (status_size.width() + status_inset),
    754                     shelf_height - status_size.height(), status_size.width(),
    755                     status_size.height()),
    756       gfx::Rect(status_inset, shelf_height - status_size.height(),
    757                     status_size.width(), status_size.height()),
    758       gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
    759                     shelf_height - (status_size.height() + status_inset),
    760                     status_size.width(), status_size.height()));
    761 
    762   target_bounds->work_area_insets = SelectValueForShelfAlignment(
    763       gfx::Insets(0, 0, GetWorkAreaSize(state, shelf_height), 0),
    764       gfx::Insets(0, GetWorkAreaSize(state, shelf_width), 0, 0),
    765       gfx::Insets(0, 0, 0, GetWorkAreaSize(state, shelf_width)),
    766       gfx::Insets(GetWorkAreaSize(state, shelf_height), 0, 0, 0));
    767 
    768   // TODO(varkha): The functionality of managing insets for display areas
    769   // should probably be pushed to a separate component. This would simplify or
    770   // remove entirely the dependency on keyboard and dock.
    771 
    772   // Also push in the work area inset for the keyboard if it is visible.
    773   if (!keyboard_bounds_.IsEmpty()) {
    774     gfx::Insets keyboard_insets(0, 0, keyboard_bounds_.height(), 0);
    775     target_bounds->work_area_insets += keyboard_insets;
    776   }
    777 
    778   // Also push in the work area inset for the dock if it is visible.
    779   if (!dock_bounds_.IsEmpty()) {
    780     gfx::Insets dock_insets(
    781         0, (dock_bounds_.x() > 0 ? 0 : dock_bounds_.width()),
    782         0, (dock_bounds_.x() > 0 ? dock_bounds_.width() : 0));
    783     target_bounds->work_area_insets += dock_insets;
    784   }
    785 
    786   target_bounds->opacity =
    787       (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
    788        state.visibility_state == SHELF_VISIBLE ||
    789        state.visibility_state == SHELF_AUTO_HIDE) ? 1.0f : 0.0f;
    790   target_bounds->status_opacity =
    791       (state.visibility_state == SHELF_AUTO_HIDE &&
    792        state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
    793        gesture_drag_status_ != GESTURE_DRAG_IN_PROGRESS) ?
    794       0.0f : target_bounds->opacity;
    795 
    796   if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS)
    797     UpdateTargetBoundsForGesture(target_bounds);
    798 
    799   // This needs to happen after calling UpdateTargetBoundsForGesture(), because
    800   // that can change the size of the shelf.
    801   target_bounds->launcher_bounds_in_shelf = SelectValueForShelfAlignment(
    802       gfx::Rect(base::i18n::IsRTL() ? status_size.width() : 0, 0,
    803                     shelf_width - status_size.width(),
    804                     target_bounds->shelf_bounds_in_root.height()),
    805       gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
    806                 shelf_height - status_size.height()),
    807       gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
    808                 shelf_height - status_size.height()),
    809       gfx::Rect(base::i18n::IsRTL() ? status_size.width() : 0, 0,
    810                     shelf_width - status_size.width(),
    811                     target_bounds->shelf_bounds_in_root.height()));
    812 }
    813 
    814 void ShelfLayoutManager::UpdateTargetBoundsForGesture(
    815     TargetBounds* target_bounds) const {
    816   CHECK_EQ(GESTURE_DRAG_IN_PROGRESS, gesture_drag_status_);
    817   bool horizontal = IsHorizontalAlignment();
    818   const gfx::Rect& available_bounds(root_window_->bounds());
    819   int resistance_free_region = 0;
    820 
    821   if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
    822       visibility_state() == SHELF_AUTO_HIDE &&
    823       auto_hide_state() != SHELF_AUTO_HIDE_SHOWN) {
    824     // If the shelf was hidden when the drag started (and the state hasn't
    825     // changed since then, e.g. because the tray-menu was shown because of the
    826     // drag), then allow the drag some resistance-free region at first to make
    827     // sure the shelf sticks with the finger until the shelf is visible.
    828     resistance_free_region = GetPreferredShelfSize() - kAutoHideSize;
    829   }
    830 
    831   bool resist = SelectValueForShelfAlignment(
    832       gesture_drag_amount_ < -resistance_free_region,
    833       gesture_drag_amount_ > resistance_free_region,
    834       gesture_drag_amount_ < -resistance_free_region,
    835       gesture_drag_amount_ > resistance_free_region);
    836 
    837   float translate = 0.f;
    838   if (resist) {
    839     float diff = fabsf(gesture_drag_amount_) - resistance_free_region;
    840     diff = std::min(diff, sqrtf(diff));
    841     if (gesture_drag_amount_ < 0)
    842       translate = -resistance_free_region - diff;
    843     else
    844       translate = resistance_free_region + diff;
    845   } else {
    846     translate = gesture_drag_amount_;
    847   }
    848 
    849   if (horizontal) {
    850     // Move and size the launcher with the gesture.
    851     int shelf_height = target_bounds->shelf_bounds_in_root.height() - translate;
    852     shelf_height = std::max(shelf_height, kAutoHideSize);
    853     target_bounds->shelf_bounds_in_root.set_height(shelf_height);
    854     if (alignment_ == SHELF_ALIGNMENT_BOTTOM) {
    855       target_bounds->shelf_bounds_in_root.set_y(
    856           available_bounds.bottom() - shelf_height);
    857     }
    858 
    859     if (ash::switches::UseAlternateShelfLayout()) {
    860       target_bounds->status_bounds_in_shelf.set_y(kStatusAreaInset);
    861     } else {
    862       // The statusbar should be in the center of the shelf.
    863       gfx::Rect status_y = target_bounds->shelf_bounds_in_root;
    864       status_y.set_y(0);
    865       status_y.ClampToCenteredSize(
    866           target_bounds->status_bounds_in_shelf.size());
    867       target_bounds->status_bounds_in_shelf.set_y(status_y.y());
    868     }
    869   } else {
    870     // Move and size the launcher with the gesture.
    871     int shelf_width = target_bounds->shelf_bounds_in_root.width();
    872     if (alignment_ == SHELF_ALIGNMENT_RIGHT)
    873       shelf_width -= translate;
    874     else
    875       shelf_width += translate;
    876     shelf_width = std::max(shelf_width, kAutoHideSize);
    877     target_bounds->shelf_bounds_in_root.set_width(shelf_width);
    878     if (alignment_ == SHELF_ALIGNMENT_RIGHT) {
    879       target_bounds->shelf_bounds_in_root.set_x(
    880           available_bounds.right() - shelf_width);
    881     }
    882 
    883     if (ash::switches::UseAlternateShelfLayout()) {
    884       if (alignment_ == SHELF_ALIGNMENT_RIGHT) {
    885         target_bounds->shelf_bounds_in_root.set_x(
    886             available_bounds.right() - shelf_width + kStatusAreaInset);
    887       } else {
    888         target_bounds->shelf_bounds_in_root.set_x(kStatusAreaInset);
    889       }
    890     } else {
    891       // The statusbar should be in the center of the shelf.
    892       gfx::Rect status_x = target_bounds->shelf_bounds_in_root;
    893       status_x.set_x(0);
    894       status_x.ClampToCenteredSize(
    895           target_bounds->status_bounds_in_shelf.size());
    896       target_bounds->status_bounds_in_shelf.set_x(status_x.x());
    897     }
    898   }
    899 }
    900 
    901 void ShelfLayoutManager::UpdateShelfBackground(
    902     BackgroundAnimator::ChangeType type) {
    903   shelf_->SetPaintsBackground(GetShelfBackgroundType(), type);
    904 }
    905 
    906 ShelfBackgroundType ShelfLayoutManager::GetShelfBackgroundType() const {
    907   if (state_.visibility_state != SHELF_AUTO_HIDE &&
    908       state_.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED) {
    909     return SHELF_BACKGROUND_MAXIMIZED;
    910   }
    911 
    912   if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
    913       (!state_.is_screen_locked && window_overlaps_shelf_) ||
    914       (state_.visibility_state == SHELF_AUTO_HIDE)) {
    915     return SHELF_BACKGROUND_OVERLAP;
    916   }
    917 
    918   return SHELF_BACKGROUND_DEFAULT;
    919 }
    920 
    921 void ShelfLayoutManager::UpdateAutoHideStateNow() {
    922   SetState(state_.visibility_state);
    923 
    924   // If the state did not change, the auto hide timer may still be running.
    925   StopAutoHideTimer();
    926 }
    927 
    928 void ShelfLayoutManager::StopAutoHideTimer() {
    929   auto_hide_timer_.Stop();
    930   mouse_over_shelf_when_auto_hide_timer_started_ = false;
    931 }
    932 
    933 gfx::Rect ShelfLayoutManager::GetAutoHideShowShelfRegionInScreen() const {
    934   gfx::Rect shelf_bounds_in_screen = shelf_->GetWindowBoundsInScreen();
    935   gfx::Vector2d offset = SelectValueForShelfAlignment(
    936       gfx::Vector2d(0, shelf_bounds_in_screen.height()),
    937       gfx::Vector2d(-kMaxAutoHideShowShelfRegionSize, 0),
    938       gfx::Vector2d(shelf_bounds_in_screen.width(), 0),
    939       gfx::Vector2d(0, -kMaxAutoHideShowShelfRegionSize));
    940 
    941   gfx::Rect show_shelf_region_in_screen = shelf_bounds_in_screen;
    942   show_shelf_region_in_screen += offset;
    943   if (IsHorizontalAlignment())
    944     show_shelf_region_in_screen.set_height(kMaxAutoHideShowShelfRegionSize);
    945   else
    946     show_shelf_region_in_screen.set_width(kMaxAutoHideShowShelfRegionSize);
    947 
    948   // TODO: Figure out if we need any special handling when the keyboard is
    949   // visible.
    950   return show_shelf_region_in_screen;
    951 }
    952 
    953 ShelfAutoHideState ShelfLayoutManager::CalculateAutoHideState(
    954     ShelfVisibilityState visibility_state) const {
    955   if (visibility_state != SHELF_AUTO_HIDE || !shelf_)
    956     return SHELF_AUTO_HIDE_HIDDEN;
    957 
    958   Shell* shell = Shell::GetInstance();
    959   if (shell->GetAppListTargetVisibility())
    960     return SHELF_AUTO_HIDE_SHOWN;
    961 
    962   if (shelf_->status_area_widget() &&
    963       shelf_->status_area_widget()->ShouldShowLauncher())
    964     return SHELF_AUTO_HIDE_SHOWN;
    965 
    966   if (shelf_->launcher() && shelf_->launcher()->IsShowingMenu())
    967     return SHELF_AUTO_HIDE_SHOWN;
    968 
    969   if (shelf_->launcher() && shelf_->launcher()->IsShowingOverflowBubble())
    970     return SHELF_AUTO_HIDE_SHOWN;
    971 
    972   if (shelf_->IsActive() || shelf_->status_area_widget()->IsActive())
    973     return SHELF_AUTO_HIDE_SHOWN;
    974 
    975   const std::vector<aura::Window*> windows =
    976       ash::MruWindowTracker::BuildWindowList(false);
    977 
    978   // Process the window list and check if there are any visible windows.
    979   bool visible_window = false;
    980   for (size_t i = 0; i < windows.size(); ++i) {
    981     if (windows[i] && windows[i]->IsVisible() &&
    982         !ash::wm::IsWindowMinimized(windows[i]) &&
    983         root_window_ == windows[i]->GetRootWindow()) {
    984       visible_window = true;
    985       break;
    986     }
    987   }
    988   // If there are no visible windows do not hide the shelf.
    989   if (!visible_window)
    990     return SHELF_AUTO_HIDE_SHOWN;
    991 
    992   if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS)
    993     return gesture_drag_auto_hide_state_;
    994 
    995   // Don't show if the user is dragging the mouse.
    996   if (auto_hide_event_filter_.get() && auto_hide_event_filter_->in_mouse_drag())
    997     return SHELF_AUTO_HIDE_HIDDEN;
    998 
    999   // Ignore the mouse position if mouse events are disabled.
   1000   aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(
   1001       shelf_->GetNativeWindow()->GetRootWindow());
   1002   if (!cursor_client->IsMouseEventsEnabled())
   1003     return SHELF_AUTO_HIDE_HIDDEN;
   1004 
   1005   gfx::Rect shelf_region = shelf_->GetWindowBoundsInScreen();
   1006   if (shelf_->status_area_widget() &&
   1007       shelf_->status_area_widget()->IsMessageBubbleShown() &&
   1008       IsVisible()) {
   1009     // Increase the the hit test area to prevent the shelf from disappearing
   1010     // when the mouse is over the bubble gap.
   1011     shelf_region.Inset(alignment_ == SHELF_ALIGNMENT_RIGHT ?
   1012                            -kNotificationBubbleGapHeight : 0,
   1013                        alignment_ == SHELF_ALIGNMENT_BOTTOM ?
   1014                            -kNotificationBubbleGapHeight : 0,
   1015                        alignment_ == SHELF_ALIGNMENT_LEFT ?
   1016                            -kNotificationBubbleGapHeight : 0,
   1017                        alignment_ == SHELF_ALIGNMENT_TOP ?
   1018                            -kNotificationBubbleGapHeight : 0);
   1019   }
   1020 
   1021   gfx::Point cursor_position_in_screen =
   1022       Shell::GetScreen()->GetCursorScreenPoint();
   1023   if (shelf_region.Contains(cursor_position_in_screen))
   1024     return SHELF_AUTO_HIDE_SHOWN;
   1025 
   1026   // When the shelf is auto hidden and the shelf is on the boundary between two
   1027   // displays, it is hard to trigger showing the shelf. For instance, if a
   1028   // user's primary display is left of their secondary display, it is hard to
   1029   // unautohide a left aligned shelf on the secondary display.
   1030   // It is hard because:
   1031   // - It is hard to stop the cursor in the shelf "light bar" and not overshoot.
   1032   // - The cursor is warped to the other display if the cursor gets to the edge
   1033   //   of the display.
   1034   // Show the shelf if the cursor started on the shelf and the user overshot the
   1035   // shelf slightly to make it easier to show the shelf in this situation. We
   1036   // do not check |auto_hide_timer_|.IsRunning() because it returns false when
   1037   // the timer's task is running.
   1038   if ((state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN ||
   1039        mouse_over_shelf_when_auto_hide_timer_started_) &&
   1040       GetAutoHideShowShelfRegionInScreen().Contains(
   1041           cursor_position_in_screen)) {
   1042     return SHELF_AUTO_HIDE_SHOWN;
   1043   }
   1044 
   1045   return SHELF_AUTO_HIDE_HIDDEN;
   1046 }
   1047 
   1048 void ShelfLayoutManager::UpdateHitTestBounds() {
   1049   gfx::Insets mouse_insets;
   1050   gfx::Insets touch_insets;
   1051   if (state_.visibility_state == SHELF_VISIBLE) {
   1052     // Let clicks at the very top of the launcher through so windows can be
   1053     // resized with the bottom-right corner and bottom edge.
   1054     mouse_insets = GetInsetsForAlignment(kWorkspaceAreaVisibleInset);
   1055   } else if (state_.visibility_state == SHELF_AUTO_HIDE) {
   1056     // Extend the touch hit target out a bit to allow users to drag shelf out
   1057     // while hidden.
   1058     touch_insets = GetInsetsForAlignment(-kWorkspaceAreaAutoHideInset);
   1059   }
   1060 
   1061     if (shelf_ && shelf_->GetNativeWindow())
   1062       shelf_->GetNativeWindow()->SetHitTestBoundsOverrideOuter(mouse_insets,
   1063                                                                touch_insets);
   1064     shelf_->status_area_widget()->GetNativeWindow()->
   1065         SetHitTestBoundsOverrideOuter(mouse_insets, touch_insets);
   1066 }
   1067 
   1068 bool ShelfLayoutManager::IsShelfWindow(aura::Window* window) {
   1069   if (!window)
   1070     return false;
   1071   return (shelf_ && shelf_->GetNativeWindow()->Contains(window)) ||
   1072       (shelf_->status_area_widget() &&
   1073        shelf_->status_area_widget()->GetNativeWindow()->Contains(window));
   1074 }
   1075 
   1076 int ShelfLayoutManager::GetWorkAreaSize(const State& state, int size) const {
   1077   if (state.visibility_state == SHELF_VISIBLE)
   1078     return size;
   1079   if (state.visibility_state == SHELF_AUTO_HIDE)
   1080     return kAutoHideSize;
   1081   return 0;
   1082 }
   1083 
   1084 gfx::Rect ShelfLayoutManager::GetAvailableBounds() const {
   1085   gfx::Rect bounds(root_window_->bounds());
   1086   bounds.set_height(bounds.height() - keyboard_bounds_.height());
   1087   return bounds;
   1088 }
   1089 
   1090 void ShelfLayoutManager::OnKeyboardBoundsChanging(
   1091     const gfx::Rect& keyboard_bounds) {
   1092   keyboard_bounds_ = keyboard_bounds;
   1093   OnWindowResized();
   1094 }
   1095 
   1096 void ShelfLayoutManager::OnDockBoundsChanging(
   1097     const gfx::Rect& dock_bounds) {
   1098   if (dock_bounds_ != dock_bounds) {
   1099     dock_bounds_ = dock_bounds;
   1100     OnWindowResized();
   1101   }
   1102 }
   1103 
   1104 gfx::Insets ShelfLayoutManager::GetInsetsForAlignment(int distance) const {
   1105   switch (alignment_) {
   1106     case SHELF_ALIGNMENT_BOTTOM:
   1107       return gfx::Insets(distance, 0, 0, 0);
   1108     case SHELF_ALIGNMENT_LEFT:
   1109       return gfx::Insets(0, 0, 0, distance);
   1110     case SHELF_ALIGNMENT_RIGHT:
   1111       return gfx::Insets(0, distance, 0, 0);
   1112     case SHELF_ALIGNMENT_TOP:
   1113       return gfx::Insets(0, 0, distance, 0);
   1114   }
   1115   NOTREACHED();
   1116   return gfx::Insets();
   1117 }
   1118 
   1119 }  // namespace internal
   1120 }  // namespace ash
   1121