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