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