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