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.
5 #include "ash/shelf/shelf_layout_manager.h"
13 #include "ash/accelerators/accelerator_commands.h"
14 #include "ash/ash_switches.h"
15 #include "ash/root_window_controller.h"
16 #include "ash/screen_util.h"
17 #include "ash/session_state_delegate.h"
18 #include "ash/shelf/shelf.h"
19 #include "ash/shelf/shelf_bezel_event_filter.h"
20 #include "ash/shelf/shelf_constants.h"
21 #include "ash/shelf/shelf_layout_manager_observer.h"
22 #include "ash/shelf/shelf_widget.h"
23 #include "ash/shell.h"
24 #include "ash/shell_window_ids.h"
25 #include "ash/system/status_area_widget.h"
26 #include "ash/wm/gestures/shelf_gesture_handler.h"
27 #include "ash/wm/lock_state_controller.h"
28 #include "ash/wm/mru_window_tracker.h"
29 #include "ash/wm/window_animations.h"
30 #include "ash/wm/window_state.h"
31 #include "ash/wm/window_util.h"
32 #include "ash/wm/workspace_controller.h"
33 #include "base/auto_reset.h"
34 #include "base/command_line.h"
35 #include "base/command_line.h"
36 #include "base/i18n/rtl.h"
37 #include "base/strings/string_number_conversions.h"
38 #include "base/strings/string_util.h"
39 #include "ui/aura/client/activation_client.h"
40 #include "ui/aura/client/cursor_client.h"
41 #include "ui/aura/root_window.h"
42 #include "ui/base/ui_base_switches.h"
43 #include "ui/compositor/layer.h"
44 #include "ui/compositor/layer_animation_observer.h"
45 #include "ui/compositor/layer_animator.h"
46 #include "ui/compositor/scoped_layer_animation_settings.h"
47 #include "ui/events/event.h"
48 #include "ui/events/event_handler.h"
49 #include "ui/gfx/screen.h"
50 #include "ui/views/widget/widget.h"
57 // Delay before showing the shelf. This is after the mouse stops moving.
58 const int kAutoHideDelayMS = 200;
60 // To avoid hiding the shelf when the mouse transitions from a message bubble
61 // into the shelf, the hit test area is enlarged by this amount of pixels to
62 // keep the shelf from hiding.
63 const int kNotificationBubbleGapHeight = 6;
65 // The maximum size of the region on the display opposing the shelf managed by
66 // this ShelfLayoutManager which can trigger showing the shelf.
68 // - Primary display is left of secondary display.
69 // - Shelf is left aligned
70 // - This ShelfLayoutManager manages the shelf for the secondary display.
71 // |kMaxAutoHideShowShelfRegionSize| refers to the maximum size of the region
72 // from the right edge of the primary display which can trigger showing the
73 // auto hidden shelf. The region is used to make it easier to trigger showing
74 // the auto hidden shelf when the shelf is on the boundary between displays.
75 const int kMaxAutoHideShowShelfRegionSize = 10;
77 ui::Layer* GetLayer(views::Widget* widget) {
78 return widget->GetNativeView()->layer();
81 bool IsDraggingTrayEnabled() {
82 static bool dragging_tray_allowed = CommandLine::ForCurrentProcess()->
83 HasSwitch(ash::switches::kAshEnableTrayDragging);
84 return dragging_tray_allowed;
90 const int ShelfLayoutManager::kWorkspaceAreaVisibleInset = 2;
93 const int ShelfLayoutManager::kWorkspaceAreaAutoHideInset = 5;
96 const int ShelfLayoutManager::kAutoHideSize = 3;
99 const int ShelfLayoutManager::kShelfSize = 47;
102 const int ShelfLayoutManager::kShelfItemInset = 3;
104 int ShelfLayoutManager::GetPreferredShelfSize() {
105 return ash::switches::UseAlternateShelfLayout() ?
106 ShelfLayoutManager::kShelfSize : kShelfPreferredSize;
109 // ShelfLayoutManager::AutoHideEventFilter -------------------------------------
111 // Notifies ShelfLayoutManager any time the mouse moves.
112 class ShelfLayoutManager::AutoHideEventFilter : public ui::EventHandler {
114 explicit AutoHideEventFilter(ShelfLayoutManager* shelf);
115 virtual ~AutoHideEventFilter();
117 // Returns true if the last mouse event was a mouse drag.
118 bool in_mouse_drag() const { return in_mouse_drag_; }
120 // Overridden from ui::EventHandler:
121 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
122 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
125 ShelfLayoutManager* shelf_;
127 ShelfGestureHandler gesture_handler_;
128 DISALLOW_COPY_AND_ASSIGN(AutoHideEventFilter);
131 ShelfLayoutManager::AutoHideEventFilter::AutoHideEventFilter(
132 ShelfLayoutManager* shelf)
134 in_mouse_drag_(false) {
135 Shell::GetInstance()->AddPreTargetHandler(this);
138 ShelfLayoutManager::AutoHideEventFilter::~AutoHideEventFilter() {
139 Shell::GetInstance()->RemovePreTargetHandler(this);
142 void ShelfLayoutManager::AutoHideEventFilter::OnMouseEvent(
143 ui::MouseEvent* event) {
144 // This also checks IsShelfWindow() to make sure we don't attempt to hide the
145 // shelf if the mouse down occurs on the shelf.
146 in_mouse_drag_ = (event->type() == ui::ET_MOUSE_DRAGGED ||
147 (in_mouse_drag_ && event->type() != ui::ET_MOUSE_RELEASED &&
148 event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)) &&
149 !shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()));
150 if (event->type() == ui::ET_MOUSE_MOVED)
151 shelf_->UpdateAutoHideState();
155 void ShelfLayoutManager::AutoHideEventFilter::OnGestureEvent(
156 ui::GestureEvent* event) {
157 if (shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()))) {
158 if (gesture_handler_.ProcessGestureEvent(*event))
159 event->StopPropagation();
163 // ShelfLayoutManager:UpdateShelfObserver --------------------------------------
165 // UpdateShelfObserver is used to delay updating the background until the
166 // animation completes.
167 class ShelfLayoutManager::UpdateShelfObserver
168 : public ui::ImplicitAnimationObserver {
170 explicit UpdateShelfObserver(ShelfLayoutManager* shelf) : shelf_(shelf) {
171 shelf_->update_shelf_observer_ = this;
178 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
180 shelf_->UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
185 virtual ~UpdateShelfObserver() {
187 shelf_->update_shelf_observer_ = NULL;
190 // Shelf we're in. NULL if deleted before we're deleted.
191 ShelfLayoutManager* shelf_;
193 DISALLOW_COPY_AND_ASSIGN(UpdateShelfObserver);
196 // ShelfLayoutManager ----------------------------------------------------------
198 ShelfLayoutManager::ShelfLayoutManager(ShelfWidget* shelf)
199 : root_window_(shelf->GetNativeView()->GetRootWindow()),
200 updating_bounds_(false),
201 auto_hide_behavior_(SHELF_AUTO_HIDE_BEHAVIOR_NEVER),
202 alignment_(SHELF_ALIGNMENT_BOTTOM),
204 workspace_controller_(NULL),
205 window_overlaps_shelf_(false),
206 mouse_over_shelf_when_auto_hide_timer_started_(false),
207 bezel_event_filter_(new ShelfBezelEventFilter(this)),
208 gesture_drag_status_(GESTURE_DRAG_NONE),
209 gesture_drag_amount_(0.f),
210 gesture_drag_auto_hide_state_(SHELF_AUTO_HIDE_SHOWN),
211 update_shelf_observer_(NULL),
212 duration_override_in_ms_(0) {
213 Shell::GetInstance()->AddShellObserver(this);
214 Shell::GetInstance()->lock_state_controller()->AddObserver(this);
215 aura::client::GetActivationClient(root_window_)->AddObserver(this);
218 ShelfLayoutManager::~ShelfLayoutManager() {
219 if (update_shelf_observer_)
220 update_shelf_observer_->Detach();
222 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, WillDeleteShelf());
223 Shell::GetInstance()->RemoveShellObserver(this);
224 Shell::GetInstance()->lock_state_controller()->RemoveObserver(this);
225 aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
228 void ShelfLayoutManager::SetAutoHideBehavior(ShelfAutoHideBehavior behavior) {
229 if (auto_hide_behavior_ == behavior)
231 auto_hide_behavior_ = behavior;
232 UpdateVisibilityState();
233 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
234 OnAutoHideBehaviorChanged(root_window_,
235 auto_hide_behavior_));
238 void ShelfLayoutManager::PrepareForShutdown() {
239 // Clear all event filters, otherwise sometimes those filters may catch
240 // synthesized mouse event and cause crashes during the shutdown.
241 set_workspace_controller(NULL);
242 auto_hide_event_filter_.reset();
243 bezel_event_filter_.reset();
246 bool ShelfLayoutManager::IsVisible() const {
247 // status_area_widget() may be NULL during the shutdown.
248 return shelf_->status_area_widget() &&
249 shelf_->status_area_widget()->IsVisible() &&
250 (state_.visibility_state == SHELF_VISIBLE ||
251 (state_.visibility_state == SHELF_AUTO_HIDE &&
252 state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN));
255 bool ShelfLayoutManager::SetAlignment(ShelfAlignment alignment) {
256 if (alignment_ == alignment)
259 // This should not be called during the lock screen transitions.
260 DCHECK(!Shell::GetInstance()->session_state_delegate()->IsScreenLocked());
261 alignment_ = alignment;
262 shelf_->SetAlignment(alignment);
267 ShelfAlignment ShelfLayoutManager::GetAlignment() const {
268 // When the screen is locked, the shelf is forced into bottom alignment.
269 if (Shell::GetInstance()->session_state_delegate()->IsScreenLocked())
270 return SHELF_ALIGNMENT_BOTTOM;
274 gfx::Rect ShelfLayoutManager::GetIdealBounds() {
276 ScreenUtil::GetDisplayBoundsInParent(shelf_->GetNativeView()));
277 int width = 0, height = 0;
278 GetShelfSize(&width, &height);
279 return SelectValueForShelfAlignment(
280 gfx::Rect(bounds.x(), bounds.bottom() - height, bounds.width(), height),
281 gfx::Rect(bounds.x(), bounds.y(), width, bounds.height()),
282 gfx::Rect(bounds.right() - width, bounds.y(), width, bounds.height()),
283 gfx::Rect(bounds.x(), bounds.y(), bounds.width(), height));
286 void ShelfLayoutManager::LayoutShelf() {
287 TargetBounds target_bounds;
288 CalculateTargetBounds(state_, &target_bounds);
289 UpdateBoundsAndOpacity(target_bounds, false, NULL);
291 if (shelf_->shelf()) {
292 // This is not part of UpdateBoundsAndOpacity() because
293 // SetShelfViewBounds() sets the bounds immediately and does not animate.
294 // The height of the ShelfView for a horizontal shelf and the width of
295 // the ShelfView for a vertical shelf are set when |shelf_|'s bounds
296 // are changed via UpdateBoundsAndOpacity(). This sets the origin and the
297 // dimension in the other direction.
298 shelf_->shelf()->SetShelfViewBounds(
299 target_bounds.shelf_bounds_in_shelf);
303 ShelfVisibilityState ShelfLayoutManager::CalculateShelfVisibility() {
304 switch(auto_hide_behavior_) {
305 case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
306 return SHELF_AUTO_HIDE;
307 case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
308 return SHELF_VISIBLE;
309 case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
312 return SHELF_VISIBLE;
315 void ShelfLayoutManager::UpdateVisibilityState() {
316 // Bail out early when there is no |workspace_controller_|, which happens
317 // during shutdown after PrepareForShutdown.
318 if (!workspace_controller_)
321 if (Shell::GetInstance()->session_state_delegate()->IsScreenLocked()) {
322 SetState(SHELF_VISIBLE);
324 // TODO(zelidrag): Verify shelf drag animation still shows on the device
325 // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN.
326 WorkspaceWindowState window_state(workspace_controller_->GetWindowState());
327 switch (window_state) {
328 case WORKSPACE_WINDOW_STATE_FULL_SCREEN: {
329 const aura::Window* fullscreen_window = GetRootWindowController(
330 root_window_)->GetWindowForFullscreenMode();
331 if (fullscreen_window && wm::GetWindowState(fullscreen_window)->
332 hide_shelf_when_fullscreen()) {
333 SetState(SHELF_HIDDEN);
335 // The shelf is sometimes not hidden when in immersive fullscreen.
336 // Force the shelf to be auto hidden in this case.
337 SetState(SHELF_AUTO_HIDE);
342 case WORKSPACE_WINDOW_STATE_MAXIMIZED:
343 SetState(CalculateShelfVisibility());
346 case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF:
347 case WORKSPACE_WINDOW_STATE_DEFAULT:
348 SetState(CalculateShelfVisibility());
349 SetWindowOverlapsShelf(window_state ==
350 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF);
356 void ShelfLayoutManager::UpdateAutoHideState() {
357 ShelfAutoHideState auto_hide_state =
358 CalculateAutoHideState(state_.visibility_state);
359 if (auto_hide_state != state_.auto_hide_state) {
360 if (auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
361 // Hides happen immediately.
362 SetState(state_.visibility_state);
364 if (!auto_hide_timer_.IsRunning()) {
365 mouse_over_shelf_when_auto_hide_timer_started_ =
366 shelf_->GetWindowBoundsInScreen().Contains(
367 Shell::GetScreen()->GetCursorScreenPoint());
369 auto_hide_timer_.Start(
371 base::TimeDelta::FromMilliseconds(kAutoHideDelayMS),
372 this, &ShelfLayoutManager::UpdateAutoHideStateNow);
379 void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) {
380 window_overlaps_shelf_ = value;
381 UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
384 void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) {
385 observers_.AddObserver(observer);
388 void ShelfLayoutManager::RemoveObserver(ShelfLayoutManagerObserver* observer) {
389 observers_.RemoveObserver(observer);
392 ////////////////////////////////////////////////////////////////////////////////
393 // ShelfLayoutManager, Gesture functions:
395 void ShelfLayoutManager::OnGestureEdgeSwipe(const ui::GestureEvent& gesture) {
396 // Edge swipe should exit fullscreen, show the tray, and disable auto-hide.
398 if (workspace_controller_->GetWindowState() ==
399 WORKSPACE_WINDOW_STATE_FULL_SCREEN) {
400 accelerators::ToggleFullscreen();
403 ShelfVisibilityState visibility = CalculateShelfVisibility();
404 if (visibility == SHELF_AUTO_HIDE &&
405 CalculateAutoHideState(visibility) == SHELF_AUTO_HIDE_HIDDEN) {
406 SetState(SHELF_VISIBLE);
407 SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
411 void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) {
412 gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS;
413 gesture_drag_amount_ = 0.f;
414 gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ?
415 auto_hide_state() : SHELF_AUTO_HIDE_SHOWN;
416 UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
419 ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag(
420 const ui::GestureEvent& gesture) {
421 bool horizontal = IsHorizontalAlignment();
422 gesture_drag_amount_ += horizontal ? gesture.details().scroll_y() :
423 gesture.details().scroll_x();
426 // Start reveling the status menu when:
427 // - dragging up on an already visible shelf
428 // - dragging up on a hidden shelf, but it is currently completely visible.
429 if (horizontal && gesture.details().scroll_y() < 0) {
431 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN && shelf_)
432 min_height = shelf_->GetContentsView()->GetPreferredSize().height();
434 if (min_height < shelf_->GetWindowBoundsInScreen().height() &&
435 gesture.root_location().x() >=
436 shelf_->status_area_widget()->GetWindowBoundsInScreen().x() &&
437 IsDraggingTrayEnabled())
444 void ShelfLayoutManager::CompleteGestureDrag(const ui::GestureEvent& gesture) {
445 bool horizontal = IsHorizontalAlignment();
446 bool should_change = false;
447 if (gesture.type() == ui::ET_GESTURE_SCROLL_END) {
448 // The visibility of the shelf changes only if the shelf was dragged X%
449 // along the correct axis. If the shelf was already visible, then the
450 // direction of the drag does not matter.
451 const float kDragHideThreshold = 0.4f;
452 gfx::Rect bounds = GetIdealBounds();
453 float drag_ratio = fabs(gesture_drag_amount_) /
454 (horizontal ? bounds.height() : bounds.width());
455 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
456 should_change = drag_ratio > kDragHideThreshold;
458 bool correct_direction = false;
459 switch (GetAlignment()) {
460 case SHELF_ALIGNMENT_BOTTOM:
461 case SHELF_ALIGNMENT_RIGHT:
462 correct_direction = gesture_drag_amount_ < 0;
464 case SHELF_ALIGNMENT_LEFT:
465 case SHELF_ALIGNMENT_TOP:
466 correct_direction = gesture_drag_amount_ > 0;
469 should_change = correct_direction && drag_ratio > kDragHideThreshold;
471 } else if (gesture.type() == ui::ET_SCROLL_FLING_START) {
472 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
473 should_change = horizontal ? fabs(gesture.details().velocity_y()) > 0 :
474 fabs(gesture.details().velocity_x()) > 0;
476 should_change = SelectValueForShelfAlignment(
477 gesture.details().velocity_y() < 0,
478 gesture.details().velocity_x() > 0,
479 gesture.details().velocity_x() < 0,
480 gesture.details().velocity_y() > 0);
486 if (!should_change) {
491 shelf_->Deactivate();
492 shelf_->status_area_widget()->Deactivate();
494 gesture_drag_auto_hide_state_ =
495 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
496 SHELF_AUTO_HIDE_HIDDEN : SHELF_AUTO_HIDE_SHOWN;
497 ShelfAutoHideBehavior new_auto_hide_behavior =
498 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
499 SHELF_AUTO_HIDE_BEHAVIOR_NEVER : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
501 // When in fullscreen and the shelf is forced to be auto hidden, the auto hide
502 // behavior affects neither the visibility state nor the auto hide state. Set
503 // |gesture_drag_status_| to GESTURE_DRAG_COMPLETE_IN_PROGRESS to set the auto
504 // hide state to |gesture_drag_auto_hide_state_|.
505 gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS;
506 if (auto_hide_behavior_ != new_auto_hide_behavior)
507 SetAutoHideBehavior(new_auto_hide_behavior);
509 UpdateVisibilityState();
510 gesture_drag_status_ = GESTURE_DRAG_NONE;
513 void ShelfLayoutManager::CancelGestureDrag() {
514 gesture_drag_status_ = GESTURE_DRAG_CANCEL_IN_PROGRESS;
515 UpdateVisibilityState();
516 gesture_drag_status_ = GESTURE_DRAG_NONE;
519 void ShelfLayoutManager::SetAnimationDurationOverride(
520 int duration_override_in_ms) {
521 duration_override_in_ms_ = duration_override_in_ms;
524 ////////////////////////////////////////////////////////////////////////////////
525 // ShelfLayoutManager, aura::LayoutManager implementation:
527 void ShelfLayoutManager::OnWindowResized() {
531 void ShelfLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
534 void ShelfLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
537 void ShelfLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
540 void ShelfLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
544 void ShelfLayoutManager::SetChildBounds(aura::Window* child,
545 const gfx::Rect& requested_bounds) {
546 SetChildBoundsDirect(child, requested_bounds);
547 // We may contain other widgets (such as frame maximize bubble) but they don't
548 // effect the layout in anyway.
549 if (!updating_bounds_ &&
550 ((shelf_->GetNativeView() == child) ||
551 (shelf_->status_area_widget()->GetNativeView() == child))) {
556 void ShelfLayoutManager::OnLockStateChanged(bool locked) {
557 // Force the shelf to layout for alignment (bottom if locked, restore
558 // the previous alignment otherwise).
559 shelf_->SetAlignment(locked ? SHELF_ALIGNMENT_BOTTOM : alignment_);
560 UpdateVisibilityState();
564 void ShelfLayoutManager::OnWindowActivated(aura::Window* gained_active,
565 aura::Window* lost_active) {
566 UpdateAutoHideStateNow();
569 bool ShelfLayoutManager::IsHorizontalAlignment() const {
570 return GetAlignment() == SHELF_ALIGNMENT_BOTTOM ||
571 GetAlignment() == SHELF_ALIGNMENT_TOP;
575 ShelfLayoutManager* ShelfLayoutManager::ForShelf(aura::Window* window) {
576 ShelfWidget* shelf = RootWindowController::ForShelf(window)->shelf();
577 return shelf ? shelf->shelf_layout_manager() : NULL;
580 ////////////////////////////////////////////////////////////////////////////////
581 // ShelfLayoutManager, private:
583 ShelfLayoutManager::TargetBounds::TargetBounds() : opacity(0.0f) {}
584 ShelfLayoutManager::TargetBounds::~TargetBounds() {}
586 void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
587 if (!shelf_->GetNativeView())
591 state.visibility_state = visibility_state;
592 state.auto_hide_state = CalculateAutoHideState(visibility_state);
593 state.is_screen_locked =
594 Shell::GetInstance()->session_state_delegate()->IsScreenLocked();
595 state.window_state = workspace_controller_ ?
596 workspace_controller_->GetWindowState() : WORKSPACE_WINDOW_STATE_DEFAULT;
598 // Force an update because gesture drags affect the shelf bounds and we
599 // should animate back to the normal bounds at the end of a gesture.
601 (gesture_drag_status_ == GESTURE_DRAG_CANCEL_IN_PROGRESS ||
602 gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS);
604 if (!force_update && state_.Equals(state))
605 return; // Nothing changed.
607 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
608 WillChangeVisibilityState(visibility_state));
610 if (state.visibility_state == SHELF_AUTO_HIDE) {
611 // When state is SHELF_AUTO_HIDE we need to track when the mouse is over the
612 // shelf to unhide it. AutoHideEventFilter does that for us.
613 if (!auto_hide_event_filter_)
614 auto_hide_event_filter_.reset(new AutoHideEventFilter(this));
616 auto_hide_event_filter_.reset(NULL);
621 State old_state = state_;
624 BackgroundAnimatorChangeType change_type = BACKGROUND_CHANGE_ANIMATE;
625 bool delay_background_change = false;
627 // Do not animate the background when:
628 // - Going from a hidden / auto hidden shelf in fullscreen to a visible shelf
629 // in maximized mode.
630 // - Going from an auto hidden shelf in maximized mode to a visible shelf in
632 if (state.visibility_state == SHELF_VISIBLE &&
633 state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED &&
634 old_state.visibility_state != SHELF_VISIBLE) {
635 change_type = BACKGROUND_CHANGE_IMMEDIATE;
637 // Delay the animation when the shelf was hidden, and has just been made
638 // visible (e.g. using a gesture-drag).
639 if (state.visibility_state == SHELF_VISIBLE &&
640 old_state.visibility_state == SHELF_AUTO_HIDE &&
641 old_state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
642 delay_background_change = true;
646 if (delay_background_change) {
647 if (update_shelf_observer_)
648 update_shelf_observer_->Detach();
649 // UpdateShelfBackground deletes itself when the animation is done.
650 update_shelf_observer_ = new UpdateShelfObserver(this);
652 UpdateShelfBackground(change_type);
655 shelf_->SetDimsShelf(
656 state.visibility_state == SHELF_VISIBLE &&
657 state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED);
659 TargetBounds target_bounds;
660 CalculateTargetBounds(state_, &target_bounds);
661 UpdateBoundsAndOpacity(target_bounds, true,
662 delay_background_change ? update_shelf_observer_ : NULL);
664 // OnAutoHideStateChanged Should be emitted when:
665 // - firstly state changed to auto-hide from other state
666 // - or, auto_hide_state has changed
667 if ((old_state.visibility_state != state_.visibility_state &&
668 state_.visibility_state == SHELF_AUTO_HIDE) ||
669 old_state.auto_hide_state != state_.auto_hide_state) {
670 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
671 OnAutoHideStateChanged(state_.auto_hide_state));
675 void ShelfLayoutManager::UpdateBoundsAndOpacity(
676 const TargetBounds& target_bounds,
678 ui::ImplicitAnimationObserver* observer) {
679 base::AutoReset<bool> auto_reset_updating_bounds(&updating_bounds_, true);
681 ui::ScopedLayerAnimationSettings shelf_animation_setter(
682 GetLayer(shelf_)->GetAnimator());
683 ui::ScopedLayerAnimationSettings status_animation_setter(
684 GetLayer(shelf_->status_area_widget())->GetAnimator());
686 int duration = duration_override_in_ms_ ? duration_override_in_ms_ :
687 kCrossFadeDurationMS;
688 shelf_animation_setter.SetTransitionDuration(
689 base::TimeDelta::FromMilliseconds(duration));
690 shelf_animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
691 shelf_animation_setter.SetPreemptionStrategy(
692 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
693 status_animation_setter.SetTransitionDuration(
694 base::TimeDelta::FromMilliseconds(duration));
695 status_animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
696 status_animation_setter.SetPreemptionStrategy(
697 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
700 shelf_animation_setter.SetTransitionDuration(base::TimeDelta());
701 status_animation_setter.SetTransitionDuration(base::TimeDelta());
704 status_animation_setter.AddObserver(observer);
706 GetLayer(shelf_)->SetOpacity(target_bounds.opacity);
707 shelf_->SetBounds(ScreenUtil::ConvertRectToScreen(
708 shelf_->GetNativeView()->parent(),
709 target_bounds.shelf_bounds_in_root));
711 GetLayer(shelf_->status_area_widget())->SetOpacity(
712 target_bounds.status_opacity);
713 // TODO(harrym): Once status area widget is a child view of shelf
714 // this can be simplified.
715 gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
716 status_bounds.set_x(status_bounds.x() +
717 target_bounds.shelf_bounds_in_root.x());
718 status_bounds.set_y(status_bounds.y() +
719 target_bounds.shelf_bounds_in_root.y());
720 shelf_->status_area_widget()->SetBounds(
721 ScreenUtil::ConvertRectToScreen(
722 shelf_->status_area_widget()->GetNativeView()->parent(),
724 Shell::GetInstance()->SetDisplayWorkAreaInsets(
725 root_window_, target_bounds.work_area_insets);
728 void ShelfLayoutManager::StopAnimating() {
729 GetLayer(shelf_)->GetAnimator()->StopAnimating();
730 GetLayer(shelf_->status_area_widget())->GetAnimator()->StopAnimating();
733 void ShelfLayoutManager::GetShelfSize(int* width, int* height) {
734 *width = *height = 0;
735 gfx::Size status_size(
736 shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
737 if (IsHorizontalAlignment())
738 *height = GetPreferredShelfSize();
740 *width = GetPreferredShelfSize();
743 void ShelfLayoutManager::AdjustBoundsBasedOnAlignment(int inset,
744 gfx::Rect* bounds) const {
745 bounds->Inset(SelectValueForShelfAlignment(
746 gfx::Insets(0, 0, inset, 0),
747 gfx::Insets(0, inset, 0, 0),
748 gfx::Insets(0, 0, 0, inset),
749 gfx::Insets(inset, 0, 0, 0)));
752 void ShelfLayoutManager::CalculateTargetBounds(
754 TargetBounds* target_bounds) {
755 const gfx::Rect available_bounds(GetAvailableBounds());
756 gfx::Rect status_size(
757 shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
758 int shelf_width = 0, shelf_height = 0;
759 GetShelfSize(&shelf_width, &shelf_height);
760 if (IsHorizontalAlignment())
761 shelf_width = available_bounds.width();
763 shelf_height = available_bounds.height();
765 if (state.visibility_state == SHELF_AUTO_HIDE &&
766 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
767 // Auto-hidden shelf always starts with the default size. If a gesture-drag
768 // is in progress, then the call to UpdateTargetBoundsForGesture() below
769 // takes care of setting the height properly.
770 if (IsHorizontalAlignment())
771 shelf_height = kAutoHideSize;
773 shelf_width = kAutoHideSize;
774 } else if (state.visibility_state == SHELF_HIDDEN ||
775 !keyboard_bounds_.IsEmpty()) {
776 if (IsHorizontalAlignment())
782 target_bounds->shelf_bounds_in_root = SelectValueForShelfAlignment(
783 gfx::Rect(available_bounds.x(), available_bounds.bottom() - shelf_height,
784 available_bounds.width(), shelf_height),
785 gfx::Rect(available_bounds.x(), available_bounds.y(),
786 shelf_width, available_bounds.height()),
787 gfx::Rect(available_bounds.right() - shelf_width, available_bounds.y(),
788 shelf_width, available_bounds.height()),
789 gfx::Rect(available_bounds.x(), available_bounds.y(),
790 available_bounds.width(), shelf_height));
792 int status_inset = std::max(0, GetPreferredShelfSize() -
793 PrimaryAxisValue(status_size.height(), status_size.width()));
795 if (ash::switches::UseAlternateShelfLayout()) {
797 if (IsHorizontalAlignment())
798 status_size.set_height(kShelfSize);
800 status_size.set_width(kShelfSize);
803 target_bounds->status_bounds_in_shelf = SelectValueForShelfAlignment(
804 gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
805 status_inset, status_size.width(), status_size.height()),
806 gfx::Rect(shelf_width - (status_size.width() + status_inset),
807 shelf_height - status_size.height(), status_size.width(),
808 status_size.height()),
809 gfx::Rect(status_inset, shelf_height - status_size.height(),
810 status_size.width(), status_size.height()),
811 gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
812 shelf_height - (status_size.height() + status_inset),
813 status_size.width(), status_size.height()));
815 target_bounds->work_area_insets = SelectValueForShelfAlignment(
816 gfx::Insets(0, 0, GetWorkAreaSize(state, shelf_height), 0),
817 gfx::Insets(0, GetWorkAreaSize(state, shelf_width), 0, 0),
818 gfx::Insets(0, 0, 0, GetWorkAreaSize(state, shelf_width)),
819 gfx::Insets(GetWorkAreaSize(state, shelf_height), 0, 0, 0));
821 // TODO(varkha): The functionality of managing insets for display areas
822 // should probably be pushed to a separate component. This would simplify or
823 // remove entirely the dependency on keyboard and dock.
825 // Also push in the work area inset for the keyboard if it is visible.
826 if (!keyboard_bounds_.IsEmpty()) {
827 gfx::Insets keyboard_insets(0, 0, keyboard_bounds_.height(), 0);
828 target_bounds->work_area_insets += keyboard_insets;
831 // Also push in the work area inset for the dock if it is visible.
832 if (!dock_bounds_.IsEmpty()) {
833 gfx::Insets dock_insets(
834 0, (dock_bounds_.x() > 0 ? 0 : dock_bounds_.width()),
835 0, (dock_bounds_.x() > 0 ? dock_bounds_.width() : 0));
836 target_bounds->work_area_insets += dock_insets;
839 target_bounds->opacity =
840 (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
841 state.visibility_state == SHELF_VISIBLE ||
842 state.visibility_state == SHELF_AUTO_HIDE) ? 1.0f : 0.0f;
843 target_bounds->status_opacity =
844 (state.visibility_state == SHELF_AUTO_HIDE &&
845 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
846 gesture_drag_status_ != GESTURE_DRAG_IN_PROGRESS) ?
847 0.0f : target_bounds->opacity;
849 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS)
850 UpdateTargetBoundsForGesture(target_bounds);
852 // This needs to happen after calling UpdateTargetBoundsForGesture(), because
853 // that can change the size of the shelf.
854 target_bounds->shelf_bounds_in_shelf = SelectValueForShelfAlignment(
856 shelf_width - status_size.width(),
857 target_bounds->shelf_bounds_in_root.height()),
858 gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
859 shelf_height - status_size.height()),
860 gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
861 shelf_height - status_size.height()),
863 shelf_width - status_size.width(),
864 target_bounds->shelf_bounds_in_root.height()));
867 void ShelfLayoutManager::UpdateTargetBoundsForGesture(
868 TargetBounds* target_bounds) const {
869 CHECK_EQ(GESTURE_DRAG_IN_PROGRESS, gesture_drag_status_);
870 bool horizontal = IsHorizontalAlignment();
871 const gfx::Rect& available_bounds(root_window_->bounds());
872 int resistance_free_region = 0;
874 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
875 visibility_state() == SHELF_AUTO_HIDE &&
876 auto_hide_state() != SHELF_AUTO_HIDE_SHOWN) {
877 // If the shelf was hidden when the drag started (and the state hasn't
878 // changed since then, e.g. because the tray-menu was shown because of the
879 // drag), then allow the drag some resistance-free region at first to make
880 // sure the shelf sticks with the finger until the shelf is visible.
881 resistance_free_region = GetPreferredShelfSize() - kAutoHideSize;
884 bool resist = SelectValueForShelfAlignment(
885 gesture_drag_amount_ < -resistance_free_region,
886 gesture_drag_amount_ > resistance_free_region,
887 gesture_drag_amount_ < -resistance_free_region,
888 gesture_drag_amount_ > resistance_free_region);
890 float translate = 0.f;
892 float diff = fabsf(gesture_drag_amount_) - resistance_free_region;
893 diff = std::min(diff, sqrtf(diff));
894 if (gesture_drag_amount_ < 0)
895 translate = -resistance_free_region - diff;
897 translate = resistance_free_region + diff;
899 translate = gesture_drag_amount_;
903 // Move and size the shelf with the gesture.
904 int shelf_height = target_bounds->shelf_bounds_in_root.height() - translate;
905 shelf_height = std::max(shelf_height, kAutoHideSize);
906 target_bounds->shelf_bounds_in_root.set_height(shelf_height);
907 if (GetAlignment() == SHELF_ALIGNMENT_BOTTOM) {
908 target_bounds->shelf_bounds_in_root.set_y(
909 available_bounds.bottom() - shelf_height);
912 if (ash::switches::UseAlternateShelfLayout()) {
913 target_bounds->status_bounds_in_shelf.set_y(0);
915 // The statusbar should be in the center of the shelf.
916 gfx::Rect status_y = target_bounds->shelf_bounds_in_root;
918 status_y.ClampToCenteredSize(
919 target_bounds->status_bounds_in_shelf.size());
920 target_bounds->status_bounds_in_shelf.set_y(status_y.y());
923 // Move and size the shelf with the gesture.
924 int shelf_width = target_bounds->shelf_bounds_in_root.width();
925 bool right_aligned = GetAlignment() == SHELF_ALIGNMENT_RIGHT;
927 shelf_width -= translate;
929 shelf_width += translate;
930 shelf_width = std::max(shelf_width, kAutoHideSize);
931 target_bounds->shelf_bounds_in_root.set_width(shelf_width);
933 target_bounds->shelf_bounds_in_root.set_x(
934 available_bounds.right() - shelf_width);
937 if (ash::switches::UseAlternateShelfLayout()) {
939 target_bounds->status_bounds_in_shelf.set_x(0);
941 target_bounds->status_bounds_in_shelf.set_x(
942 target_bounds->shelf_bounds_in_root.width() -
945 // The statusbar should be in the center of the shelf.
946 gfx::Rect status_x = target_bounds->shelf_bounds_in_root;
948 status_x.ClampToCenteredSize(
949 target_bounds->status_bounds_in_shelf.size());
950 target_bounds->status_bounds_in_shelf.set_x(status_x.x());
955 void ShelfLayoutManager::UpdateShelfBackground(
956 BackgroundAnimatorChangeType type) {
957 const ShelfBackgroundType background_type(GetShelfBackgroundType());
958 shelf_->SetPaintsBackground(background_type, type);
959 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
960 OnBackgroundUpdated(background_type, type));
963 ShelfBackgroundType ShelfLayoutManager::GetShelfBackgroundType() const {
964 if (state_.visibility_state != SHELF_AUTO_HIDE &&
965 state_.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED) {
966 return SHELF_BACKGROUND_MAXIMIZED;
969 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
970 (!state_.is_screen_locked && window_overlaps_shelf_) ||
971 (state_.visibility_state == SHELF_AUTO_HIDE)) {
972 return SHELF_BACKGROUND_OVERLAP;
975 return SHELF_BACKGROUND_DEFAULT;
978 void ShelfLayoutManager::UpdateAutoHideStateNow() {
979 SetState(state_.visibility_state);
981 // If the state did not change, the auto hide timer may still be running.
985 void ShelfLayoutManager::StopAutoHideTimer() {
986 auto_hide_timer_.Stop();
987 mouse_over_shelf_when_auto_hide_timer_started_ = false;
990 gfx::Rect ShelfLayoutManager::GetAutoHideShowShelfRegionInScreen() const {
991 gfx::Rect shelf_bounds_in_screen = shelf_->GetWindowBoundsInScreen();
992 gfx::Vector2d offset = SelectValueForShelfAlignment(
993 gfx::Vector2d(0, shelf_bounds_in_screen.height()),
994 gfx::Vector2d(-kMaxAutoHideShowShelfRegionSize, 0),
995 gfx::Vector2d(shelf_bounds_in_screen.width(), 0),
996 gfx::Vector2d(0, -kMaxAutoHideShowShelfRegionSize));
998 gfx::Rect show_shelf_region_in_screen = shelf_bounds_in_screen;
999 show_shelf_region_in_screen += offset;
1000 if (IsHorizontalAlignment())
1001 show_shelf_region_in_screen.set_height(kMaxAutoHideShowShelfRegionSize);
1003 show_shelf_region_in_screen.set_width(kMaxAutoHideShowShelfRegionSize);
1005 // TODO: Figure out if we need any special handling when the keyboard is
1007 return show_shelf_region_in_screen;
1010 ShelfAutoHideState ShelfLayoutManager::CalculateAutoHideState(
1011 ShelfVisibilityState visibility_state) const {
1012 if (visibility_state != SHELF_AUTO_HIDE || !shelf_)
1013 return SHELF_AUTO_HIDE_HIDDEN;
1015 Shell* shell = Shell::GetInstance();
1016 if (shell->GetAppListTargetVisibility())
1017 return SHELF_AUTO_HIDE_SHOWN;
1019 if (shelf_->status_area_widget() &&
1020 shelf_->status_area_widget()->ShouldShowShelf())
1021 return SHELF_AUTO_HIDE_SHOWN;
1023 if (shelf_->shelf() && shelf_->shelf()->IsShowingMenu())
1024 return SHELF_AUTO_HIDE_SHOWN;
1026 if (shelf_->shelf() && shelf_->shelf()->IsShowingOverflowBubble())
1027 return SHELF_AUTO_HIDE_SHOWN;
1029 if (shelf_->IsActive() || shelf_->status_area_widget()->IsActive())
1030 return SHELF_AUTO_HIDE_SHOWN;
1032 const std::vector<aura::Window*> windows =
1033 ash::MruWindowTracker::BuildWindowList(false);
1035 // Process the window list and check if there are any visible windows.
1036 bool visible_window = false;
1037 for (size_t i = 0; i < windows.size(); ++i) {
1038 if (windows[i] && windows[i]->IsVisible() &&
1039 !wm::GetWindowState(windows[i])->IsMinimized() &&
1040 root_window_ == windows[i]->GetRootWindow()) {
1041 visible_window = true;
1045 // If there are no visible windows do not hide the shelf.
1046 if (!visible_window)
1047 return SHELF_AUTO_HIDE_SHOWN;
1049 if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS)
1050 return gesture_drag_auto_hide_state_;
1052 // Don't show if the user is dragging the mouse.
1053 if (auto_hide_event_filter_.get() && auto_hide_event_filter_->in_mouse_drag())
1054 return SHELF_AUTO_HIDE_HIDDEN;
1056 // Ignore the mouse position if mouse events are disabled.
1057 aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(
1058 shelf_->GetNativeWindow()->GetRootWindow());
1059 if (!cursor_client->IsMouseEventsEnabled())
1060 return SHELF_AUTO_HIDE_HIDDEN;
1062 gfx::Rect shelf_region = shelf_->GetWindowBoundsInScreen();
1063 if (shelf_->status_area_widget() &&
1064 shelf_->status_area_widget()->IsMessageBubbleShown() &&
1066 // Increase the the hit test area to prevent the shelf from disappearing
1067 // when the mouse is over the bubble gap.
1068 ShelfAlignment alignment = GetAlignment();
1069 shelf_region.Inset(alignment == SHELF_ALIGNMENT_RIGHT ?
1070 -kNotificationBubbleGapHeight : 0,
1071 alignment == SHELF_ALIGNMENT_BOTTOM ?
1072 -kNotificationBubbleGapHeight : 0,
1073 alignment == SHELF_ALIGNMENT_LEFT ?
1074 -kNotificationBubbleGapHeight : 0,
1075 alignment == SHELF_ALIGNMENT_TOP ?
1076 -kNotificationBubbleGapHeight : 0);
1079 gfx::Point cursor_position_in_screen =
1080 Shell::GetScreen()->GetCursorScreenPoint();
1081 if (shelf_region.Contains(cursor_position_in_screen))
1082 return SHELF_AUTO_HIDE_SHOWN;
1084 // When the shelf is auto hidden and the shelf is on the boundary between two
1085 // displays, it is hard to trigger showing the shelf. For instance, if a
1086 // user's primary display is left of their secondary display, it is hard to
1087 // unautohide a left aligned shelf on the secondary display.
1088 // It is hard because:
1089 // - It is hard to stop the cursor in the shelf "light bar" and not overshoot.
1090 // - The cursor is warped to the other display if the cursor gets to the edge
1092 // Show the shelf if the cursor started on the shelf and the user overshot the
1093 // shelf slightly to make it easier to show the shelf in this situation. We
1094 // do not check |auto_hide_timer_|.IsRunning() because it returns false when
1095 // the timer's task is running.
1096 if ((state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN ||
1097 mouse_over_shelf_when_auto_hide_timer_started_) &&
1098 GetAutoHideShowShelfRegionInScreen().Contains(
1099 cursor_position_in_screen)) {
1100 return SHELF_AUTO_HIDE_SHOWN;
1103 return SHELF_AUTO_HIDE_HIDDEN;
1106 bool ShelfLayoutManager::IsShelfWindow(aura::Window* window) {
1109 return (shelf_ && shelf_->GetNativeWindow()->Contains(window)) ||
1110 (shelf_->status_area_widget() &&
1111 shelf_->status_area_widget()->GetNativeWindow()->Contains(window));
1114 int ShelfLayoutManager::GetWorkAreaSize(const State& state, int size) const {
1115 if (state.visibility_state == SHELF_VISIBLE)
1117 if (state.visibility_state == SHELF_AUTO_HIDE)
1118 return kAutoHideSize;
1122 gfx::Rect ShelfLayoutManager::GetAvailableBounds() const {
1123 gfx::Rect bounds(root_window_->bounds());
1124 bounds.set_height(bounds.height() - keyboard_bounds_.height());
1128 void ShelfLayoutManager::OnKeyboardBoundsChanging(
1129 const gfx::Rect& keyboard_bounds) {
1130 keyboard_bounds_ = keyboard_bounds;
1134 void ShelfLayoutManager::OnDockBoundsChanging(
1135 const gfx::Rect& dock_bounds,
1136 DockedWindowLayoutManagerObserver::Reason reason) {
1137 // Skip shelf layout in case docked notification originates from this class.
1138 if (reason == DISPLAY_INSETS_CHANGED)
1140 if (dock_bounds_ != dock_bounds) {
1141 dock_bounds_ = dock_bounds;
1143 UpdateVisibilityState();
1144 UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
1148 void ShelfLayoutManager::OnLockStateEvent(LockStateObserver::EventType event) {
1149 if (event == EVENT_LOCK_ANIMATION_STARTED) {
1150 // Hide the status area widget (using auto hide animation).
1151 base::AutoReset<ShelfVisibilityState> state(&state_.visibility_state,
1153 TargetBounds target_bounds;
1154 CalculateTargetBounds(state_, &target_bounds);
1155 UpdateBoundsAndOpacity(target_bounds, true, NULL);
1156 UpdateVisibilityState();
1160 gfx::Insets ShelfLayoutManager::GetInsetsForAlignment(int distance) const {
1161 switch (GetAlignment()) {
1162 case SHELF_ALIGNMENT_BOTTOM:
1163 return gfx::Insets(distance, 0, 0, 0);
1164 case SHELF_ALIGNMENT_LEFT:
1165 return gfx::Insets(0, 0, 0, distance);
1166 case SHELF_ALIGNMENT_RIGHT:
1167 return gfx::Insets(0, distance, 0, 0);
1168 case SHELF_ALIGNMENT_TOP:
1169 return gfx::Insets(0, 0, distance, 0);
1172 return gfx::Insets();
1175 } // namespace internal