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