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