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