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