blob: 5eb491a8edcfec8325d70a6d4994fc2bd49450c1 [file] [log] [blame]
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001// 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
10#include "ash/ash_switches.h"
11#include "ash/launcher/launcher.h"
12#include "ash/launcher/launcher_types.h"
13#include "ash/root_window_controller.h"
14#include "ash/screen_ash.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010015#include "ash/session_state_delegate.h"
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +010016#include "ash/shelf/shelf_layout_manager_observer.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000017#include "ash/shelf/shelf_widget.h"
18#include "ash/shell.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000019#include "ash/shell_window_ids.h"
20#include "ash/system/status_area_widget.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010021#include "ash/wm/gestures/shelf_gesture_handler.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000022#include "ash/wm/property_util.h"
23#include "ash/wm/window_cycle_controller.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010024#include "ash/wm/window_properties.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000025#include "ash/wm/window_util.h"
26#include "ash/wm/workspace_controller.h"
27#include "ash/wm/workspace/workspace_animations.h"
28#include "base/auto_reset.h"
29#include "base/command_line.h"
30#include "base/i18n/rtl.h"
31#include "ui/aura/client/activation_client.h"
32#include "ui/aura/root_window.h"
33#include "ui/base/events/event.h"
34#include "ui/base/events/event_handler.h"
35#include "ui/compositor/layer.h"
36#include "ui/compositor/layer_animation_observer.h"
37#include "ui/compositor/layer_animator.h"
38#include "ui/compositor/scoped_layer_animation_settings.h"
39#include "ui/gfx/screen.h"
40#include "ui/views/widget/widget.h"
41
42namespace ash {
43namespace internal {
44
45namespace {
46
47// Delay before showing the launcher. This is after the mouse stops moving.
48const int kAutoHideDelayMS = 200;
49
50// To avoid hiding the shelf when the mouse transitions from a message bubble
51// into the shelf, the hit test area is enlarged by this amount of pixels to
52// keep the shelf from hiding.
53const int kNotificationBubbleGapHeight = 6;
54
55ui::Layer* GetLayer(views::Widget* widget) {
56 return widget->GetNativeView()->layer();
57}
58
59bool IsDraggingTrayEnabled() {
60 static bool dragging_tray_allowed = CommandLine::ForCurrentProcess()->
61 HasSwitch(ash::switches::kAshEnableTrayDragging);
62 return dragging_tray_allowed;
63}
64
65} // namespace
66
67// static
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010068const int ShelfLayoutManager::kWorkspaceAreaVisibleInset = 2;
69
70// static
71const int ShelfLayoutManager::kWorkspaceAreaAutoHideInset = 5;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000072
73// static
74const int ShelfLayoutManager::kAutoHideSize = 3;
75
76// ShelfLayoutManager::AutoHideEventFilter -------------------------------------
77
78// Notifies ShelfLayoutManager any time the mouse moves.
79class ShelfLayoutManager::AutoHideEventFilter : public ui::EventHandler {
80 public:
81 explicit AutoHideEventFilter(ShelfLayoutManager* shelf);
82 virtual ~AutoHideEventFilter();
83
84 // Returns true if the last mouse event was a mouse drag.
85 bool in_mouse_drag() const { return in_mouse_drag_; }
86
87 // Overridden from ui::EventHandler:
88 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010089 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000090
91 private:
92 ShelfLayoutManager* shelf_;
93 bool in_mouse_drag_;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010094 ShelfGestureHandler gesture_handler_;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000095
96 DISALLOW_COPY_AND_ASSIGN(AutoHideEventFilter);
97};
98
99ShelfLayoutManager::AutoHideEventFilter::AutoHideEventFilter(
100 ShelfLayoutManager* shelf)
101 : shelf_(shelf),
102 in_mouse_drag_(false) {
103 Shell::GetInstance()->AddPreTargetHandler(this);
104}
105
106ShelfLayoutManager::AutoHideEventFilter::~AutoHideEventFilter() {
107 Shell::GetInstance()->RemovePreTargetHandler(this);
108}
109
110void ShelfLayoutManager::AutoHideEventFilter::OnMouseEvent(
111 ui::MouseEvent* event) {
112 // This also checks IsShelfWindow() to make sure we don't attempt to hide the
113 // shelf if the mouse down occurs on the shelf.
114 in_mouse_drag_ = (event->type() == ui::ET_MOUSE_DRAGGED ||
115 (in_mouse_drag_ && event->type() != ui::ET_MOUSE_RELEASED &&
116 event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)) &&
117 !shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()));
118 if (event->type() == ui::ET_MOUSE_MOVED)
119 shelf_->UpdateAutoHideState();
120 return;
121}
122
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100123void ShelfLayoutManager::AutoHideEventFilter::OnGestureEvent(
124 ui::GestureEvent* event) {
125 if (gesture_handler_.ProcessGestureEvent(*event))
126 event->StopPropagation();
127}
128
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000129// ShelfLayoutManager:UpdateShelfObserver --------------------------------------
130
131// UpdateShelfObserver is used to delay updating the background until the
132// animation completes.
133class ShelfLayoutManager::UpdateShelfObserver
134 : public ui::ImplicitAnimationObserver {
135 public:
136 explicit UpdateShelfObserver(ShelfLayoutManager* shelf) : shelf_(shelf) {
137 shelf_->update_shelf_observer_ = this;
138 }
139
140 void Detach() {
141 shelf_ = NULL;
142 }
143
144 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
145 if (shelf_) {
146 shelf_->UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
147 }
148 delete this;
149 }
150
151 private:
152 virtual ~UpdateShelfObserver() {
153 if (shelf_)
154 shelf_->update_shelf_observer_ = NULL;
155 }
156
157 // Shelf we're in. NULL if deleted before we're deleted.
158 ShelfLayoutManager* shelf_;
159
160 DISALLOW_COPY_AND_ASSIGN(UpdateShelfObserver);
161};
162
163// ShelfLayoutManager ----------------------------------------------------------
164
165ShelfLayoutManager::ShelfLayoutManager(ShelfWidget* shelf)
166 : root_window_(shelf->GetNativeView()->GetRootWindow()),
167 in_layout_(false),
168 auto_hide_behavior_(SHELF_AUTO_HIDE_BEHAVIOR_NEVER),
169 alignment_(SHELF_ALIGNMENT_BOTTOM),
170 shelf_(shelf),
171 workspace_controller_(NULL),
172 window_overlaps_shelf_(false),
173 gesture_drag_status_(GESTURE_DRAG_NONE),
174 gesture_drag_amount_(0.f),
175 gesture_drag_auto_hide_state_(SHELF_AUTO_HIDE_SHOWN),
176 update_shelf_observer_(NULL) {
177 Shell::GetInstance()->AddShellObserver(this);
178 aura::client::GetActivationClient(root_window_)->AddObserver(this);
179}
180
181ShelfLayoutManager::~ShelfLayoutManager() {
182 if (update_shelf_observer_)
183 update_shelf_observer_->Detach();
184
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100185 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, WillDeleteShelf());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000186 Shell::GetInstance()->RemoveShellObserver(this);
187 aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
188}
189
190void ShelfLayoutManager::SetAutoHideBehavior(ShelfAutoHideBehavior behavior) {
191 if (auto_hide_behavior_ == behavior)
192 return;
193 auto_hide_behavior_ = behavior;
194 UpdateVisibilityState();
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100195 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000196 OnAutoHideStateChanged(state_.auto_hide_state));
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100197 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100198 OnAutoHideBehaviorChanged(auto_hide_behavior_));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000199}
200
201bool ShelfLayoutManager::IsVisible() const {
202 return shelf_->status_area_widget()->IsVisible() &&
203 (state_.visibility_state == SHELF_VISIBLE ||
204 (state_.visibility_state == SHELF_AUTO_HIDE &&
205 state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN));
206}
207
208bool ShelfLayoutManager::SetAlignment(ShelfAlignment alignment) {
209 if (alignment_ == alignment)
210 return false;
211
212 alignment_ = alignment;
213 if (shelf_->launcher())
214 shelf_->launcher()->SetAlignment(alignment);
215 shelf_->status_area_widget()->SetShelfAlignment(alignment);
216 LayoutShelf();
217 return true;
218}
219
220gfx::Rect ShelfLayoutManager::GetIdealBounds() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000221 gfx::Rect bounds(
222 ScreenAsh::GetDisplayBoundsInParent(shelf_->GetNativeView()));
223 int width = 0, height = 0;
224 GetShelfSize(&width, &height);
225 return SelectValueForShelfAlignment(
226 gfx::Rect(bounds.x(), bounds.bottom() - height, bounds.width(), height),
227 gfx::Rect(bounds.x(), bounds.y(), width, bounds.height()),
228 gfx::Rect(bounds.right() - width, bounds.y(), width, bounds.height()),
229 gfx::Rect(bounds.x(), bounds.y(), bounds.width(), height));
230}
231
232void ShelfLayoutManager::LayoutShelf() {
233 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
234 StopAnimating();
235 TargetBounds target_bounds;
236 CalculateTargetBounds(state_, &target_bounds);
237 GetLayer(shelf_)->SetOpacity(target_bounds.opacity);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100238 shelf_->SetWidgetBounds(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000239 ScreenAsh::ConvertRectToScreen(
240 shelf_->GetNativeView()->parent(),
241 target_bounds.shelf_bounds_in_root));
242 if (shelf_->launcher())
243 shelf_->launcher()->SetLauncherViewBounds(
244 target_bounds.launcher_bounds_in_shelf);
245 GetLayer(shelf_->status_area_widget())->SetOpacity(target_bounds.opacity);
246 // TODO(harrym): Once status area widget is a child view of shelf
247 // this can be simplified.
248 gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
249 status_bounds.set_x(status_bounds.x() +
250 target_bounds.shelf_bounds_in_root.x());
251 status_bounds.set_y(status_bounds.y() +
252 target_bounds.shelf_bounds_in_root.y());
253 shelf_->status_area_widget()->SetBounds(
254 ScreenAsh::ConvertRectToScreen(
255 shelf_->status_area_widget()->GetNativeView()->parent(),
256 status_bounds));
257 Shell::GetInstance()->SetDisplayWorkAreaInsets(
258 root_window_, target_bounds.work_area_insets);
259 UpdateHitTestBounds();
260}
261
262ShelfVisibilityState ShelfLayoutManager::CalculateShelfVisibility() {
263 switch(auto_hide_behavior_) {
264 case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
265 return SHELF_AUTO_HIDE;
266 case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
267 return SHELF_VISIBLE;
268 case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
269 return SHELF_HIDDEN;
270 }
271 return SHELF_VISIBLE;
272}
273
274ShelfVisibilityState
275ShelfLayoutManager::CalculateShelfVisibilityWhileDragging() {
276 switch(auto_hide_behavior_) {
277 case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
278 case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
279 return SHELF_AUTO_HIDE;
280 case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
281 return SHELF_HIDDEN;
282 }
283 return SHELF_VISIBLE;
284}
285
286void ShelfLayoutManager::UpdateVisibilityState() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100287 if (Shell::GetInstance()->session_state_delegate()->IsScreenLocked()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000288 SetState(SHELF_VISIBLE);
289 } else if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS) {
290 // TODO(zelidrag): Verify shelf drag animation still shows on the device
291 // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN.
292 SetState(CalculateShelfVisibilityWhileDragging());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000293 } else {
294 WorkspaceWindowState window_state(workspace_controller_->GetWindowState());
295 switch (window_state) {
296 case WORKSPACE_WINDOW_STATE_FULL_SCREEN:
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100297 {
298 aura::Window* fullscreen_window =
299 GetRootWindowController(root_window_)->GetFullscreenWindow();
300 if (fullscreen_window->GetProperty(kFullscreenUsesMinimalChromeKey)) {
301 DCHECK_NE(auto_hide_behavior_, SHELF_AUTO_HIDE_ALWAYS_HIDDEN);
302 SetState(SHELF_AUTO_HIDE);
303 } else {
304 SetState(SHELF_HIDDEN);
305 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000306 break;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100307 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000308 case WORKSPACE_WINDOW_STATE_MAXIMIZED:
309 SetState(CalculateShelfVisibility());
310 break;
311
312 case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF:
313 case WORKSPACE_WINDOW_STATE_DEFAULT:
314 SetState(CalculateShelfVisibility());
315 SetWindowOverlapsShelf(window_state ==
316 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF);
317 break;
318 }
319 }
320}
321
322void ShelfLayoutManager::UpdateAutoHideState() {
323 ShelfAutoHideState auto_hide_state =
324 CalculateAutoHideState(state_.visibility_state);
325 if (auto_hide_state != state_.auto_hide_state) {
326 if (auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
327 // Hides happen immediately.
328 SetState(state_.visibility_state);
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100329 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000330 OnAutoHideStateChanged(auto_hide_state));
331 } else {
332 auto_hide_timer_.Stop();
333 auto_hide_timer_.Start(
334 FROM_HERE,
335 base::TimeDelta::FromMilliseconds(kAutoHideDelayMS),
336 this, &ShelfLayoutManager::UpdateAutoHideStateNow);
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100337 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
338 OnAutoHideStateChanged(
339 CalculateAutoHideState(state_.visibility_state)));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000340 }
341 } else {
342 auto_hide_timer_.Stop();
343 }
344}
345
346void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) {
347 window_overlaps_shelf_ = value;
348 UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
349}
350
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100351void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000352 observers_.AddObserver(observer);
353}
354
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100355void ShelfLayoutManager::RemoveObserver(ShelfLayoutManagerObserver* observer) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000356 observers_.RemoveObserver(observer);
357}
358
359////////////////////////////////////////////////////////////////////////////////
360// ShelfLayoutManager, Gesture dragging:
361
362void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) {
363 gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS;
364 gesture_drag_amount_ = 0.f;
365 gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ?
366 auto_hide_state() : SHELF_AUTO_HIDE_SHOWN;
367 UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
368}
369
370ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag(
371 const ui::GestureEvent& gesture) {
372 bool horizontal = IsHorizontalAlignment();
373 gesture_drag_amount_ += horizontal ? gesture.details().scroll_y() :
374 gesture.details().scroll_x();
375 LayoutShelf();
376
377 // Start reveling the status menu when:
378 // - dragging up on an already visible shelf
379 // - dragging up on a hidden shelf, but it is currently completely visible.
380 if (horizontal && gesture.details().scroll_y() < 0) {
381 int min_height = 0;
382 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN && shelf_)
383 min_height = shelf_->GetContentsView()->GetPreferredSize().height();
384
385 if (min_height < shelf_->GetWindowBoundsInScreen().height() &&
386 gesture.root_location().x() >=
387 shelf_->status_area_widget()->GetWindowBoundsInScreen().x() &&
388 IsDraggingTrayEnabled())
389 return DRAG_TRAY;
390 }
391
392 return DRAG_SHELF;
393}
394
395void ShelfLayoutManager::CompleteGestureDrag(const ui::GestureEvent& gesture) {
396 bool horizontal = IsHorizontalAlignment();
397 bool should_change = false;
398 if (gesture.type() == ui::ET_GESTURE_SCROLL_END) {
399 // The visibility of the shelf changes only if the shelf was dragged X%
400 // along the correct axis. If the shelf was already visible, then the
401 // direction of the drag does not matter.
402 const float kDragHideThreshold = 0.4f;
403 gfx::Rect bounds = GetIdealBounds();
404 float drag_ratio = fabs(gesture_drag_amount_) /
405 (horizontal ? bounds.height() : bounds.width());
406 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
407 should_change = drag_ratio > kDragHideThreshold;
408 } else {
409 bool correct_direction = false;
410 switch (alignment_) {
411 case SHELF_ALIGNMENT_BOTTOM:
412 case SHELF_ALIGNMENT_RIGHT:
413 correct_direction = gesture_drag_amount_ < 0;
414 break;
415 case SHELF_ALIGNMENT_LEFT:
416 case SHELF_ALIGNMENT_TOP:
417 correct_direction = gesture_drag_amount_ > 0;
418 break;
419 }
420 should_change = correct_direction && drag_ratio > kDragHideThreshold;
421 }
422 } else if (gesture.type() == ui::ET_SCROLL_FLING_START) {
423 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
424 should_change = horizontal ? fabs(gesture.details().velocity_y()) > 0 :
425 fabs(gesture.details().velocity_x()) > 0;
426 } else {
427 should_change = SelectValueForShelfAlignment(
428 gesture.details().velocity_y() < 0,
429 gesture.details().velocity_x() > 0,
430 gesture.details().velocity_x() < 0,
431 gesture.details().velocity_y() > 0);
432 }
433 } else {
434 NOTREACHED();
435 }
436
437 if (!should_change) {
438 CancelGestureDrag();
439 return;
440 }
441
442 gesture_drag_auto_hide_state_ =
443 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
444 SHELF_AUTO_HIDE_HIDDEN : SHELF_AUTO_HIDE_SHOWN;
445 if (shelf_)
446 shelf_->Deactivate();
447 shelf_->status_area_widget()->Deactivate();
448 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
449 auto_hide_behavior_ != SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS) {
450 gesture_drag_status_ = GESTURE_DRAG_NONE;
451 SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
452 } else if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN &&
453 auto_hide_behavior_ != SHELF_AUTO_HIDE_BEHAVIOR_NEVER) {
454 gesture_drag_status_ = GESTURE_DRAG_NONE;
455 SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
456 } else {
457 gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS;
458 UpdateVisibilityState();
459 gesture_drag_status_ = GESTURE_DRAG_NONE;
460 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100461 LayoutShelf();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000462}
463
464void ShelfLayoutManager::CancelGestureDrag() {
465 gesture_drag_status_ = GESTURE_DRAG_NONE;
466 ui::ScopedLayerAnimationSettings
467 launcher_settings(GetLayer(shelf_)->GetAnimator()),
468 status_settings(GetLayer(shelf_->status_area_widget())->GetAnimator());
469 LayoutShelf();
470 UpdateVisibilityState();
471 UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
472}
473
474////////////////////////////////////////////////////////////////////////////////
475// ShelfLayoutManager, aura::LayoutManager implementation:
476
477void ShelfLayoutManager::OnWindowResized() {
478 LayoutShelf();
479}
480
481void ShelfLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
482}
483
484void ShelfLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
485}
486
487void ShelfLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
488}
489
490void ShelfLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
491 bool visible) {
492}
493
494void ShelfLayoutManager::SetChildBounds(aura::Window* child,
495 const gfx::Rect& requested_bounds) {
496 SetChildBoundsDirect(child, requested_bounds);
497 // We may contain other widgets (such as frame maximize bubble) but they don't
498 // effect the layout in anyway.
499 if (!in_layout_ &&
500 ((shelf_->GetNativeView() == child) ||
501 (shelf_->status_area_widget()->GetNativeView() == child))) {
502 LayoutShelf();
503 }
504}
505
506void ShelfLayoutManager::OnLockStateChanged(bool locked) {
507 UpdateVisibilityState();
508}
509
510void ShelfLayoutManager::OnWindowActivated(aura::Window* gained_active,
511 aura::Window* lost_active) {
512 UpdateAutoHideStateNow();
513}
514
515bool ShelfLayoutManager::IsHorizontalAlignment() const {
516 return alignment_ == SHELF_ALIGNMENT_BOTTOM ||
517 alignment_ == SHELF_ALIGNMENT_TOP;
518}
519
520// static
521ShelfLayoutManager* ShelfLayoutManager::ForLauncher(aura::Window* window) {
522 ShelfWidget* shelf = RootWindowController::ForLauncher(window)->shelf();
523 return shelf ? shelf->shelf_layout_manager() : NULL;
524}
525
526////////////////////////////////////////////////////////////////////////////////
527// ShelfLayoutManager, private:
528
529ShelfLayoutManager::TargetBounds::TargetBounds() : opacity(0.0f) {}
530ShelfLayoutManager::TargetBounds::~TargetBounds() {}
531
532void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100533 if (!shelf_->GetNativeView())
534 return;
535
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000536 State state;
537 state.visibility_state = visibility_state;
538 state.auto_hide_state = CalculateAutoHideState(visibility_state);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100539 state.is_screen_locked =
540 Shell::GetInstance()->session_state_delegate()->IsScreenLocked();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000541
542 // It's possible for SetState() when a window becomes maximized but the state
543 // won't have changed value. Do the dimming check before the early exit.
544 if (workspace_controller_) {
545 shelf_->SetDimsShelf(
546 (state.visibility_state == SHELF_VISIBLE) &&
547 workspace_controller_->GetWindowState() ==
548 WORKSPACE_WINDOW_STATE_MAXIMIZED);
549 }
550
551 if (state_.Equals(state))
552 return; // Nothing changed.
553
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100554 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000555 WillChangeVisibilityState(visibility_state));
556
557 if (state.visibility_state == SHELF_AUTO_HIDE) {
558 // When state is SHELF_AUTO_HIDE we need to track when the mouse is over the
559 // launcher to unhide the shelf. AutoHideEventFilter does that for us.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100560 if (!event_filter_)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000561 event_filter_.reset(new AutoHideEventFilter(this));
562 } else {
563 event_filter_.reset(NULL);
564 }
565
566 auto_hide_timer_.Stop();
567
568 // Animating the background when transitioning from auto-hide & hidden to
569 // visible is janky. Update the background immediately in this case.
570 BackgroundAnimator::ChangeType change_type =
571 (state_.visibility_state == SHELF_AUTO_HIDE &&
572 state_.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
573 state.visibility_state == SHELF_VISIBLE) ?
574 BackgroundAnimator::CHANGE_IMMEDIATE : BackgroundAnimator::CHANGE_ANIMATE;
575 StopAnimating();
576
577 State old_state = state_;
578 state_ = state;
579 TargetBounds target_bounds;
580 CalculateTargetBounds(state_, &target_bounds);
581
582 ui::ScopedLayerAnimationSettings launcher_animation_setter(
583 GetLayer(shelf_)->GetAnimator());
584 launcher_animation_setter.SetTransitionDuration(
585 base::TimeDelta::FromMilliseconds(kWorkspaceSwitchTimeMS));
586 launcher_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
587 GetLayer(shelf_)->SetBounds(
588 target_bounds.shelf_bounds_in_root);
589 GetLayer(shelf_)->SetOpacity(target_bounds.opacity);
590 ui::ScopedLayerAnimationSettings status_animation_setter(
591 GetLayer(shelf_->status_area_widget())->GetAnimator());
592 status_animation_setter.SetTransitionDuration(
593 base::TimeDelta::FromMilliseconds(kWorkspaceSwitchTimeMS));
594 status_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
595
596 // Delay updating the background when going from SHELF_AUTO_HIDE_SHOWN to
597 // SHELF_AUTO_HIDE_HIDDEN until the shelf animates out. Otherwise during the
598 // animation you see the background change.
599 // Also delay the animation when the shelf was hidden, and has just been made
600 // visible (e.g. using a gesture-drag).
601 bool delay_shelf_update =
602 state.visibility_state == SHELF_AUTO_HIDE &&
603 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
604 old_state.visibility_state == SHELF_AUTO_HIDE;
605
606 if (state.visibility_state == SHELF_VISIBLE &&
607 old_state.visibility_state == SHELF_AUTO_HIDE &&
608 old_state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN)
609 delay_shelf_update = true;
610
611 if (delay_shelf_update) {
612 if (update_shelf_observer_)
613 update_shelf_observer_->Detach();
614 // UpdateShelfBackground deletes itself when the animation is done.
615 update_shelf_observer_ = new UpdateShelfObserver(this);
616 status_animation_setter.AddObserver(update_shelf_observer_);
617 }
618 ui::Layer* layer = GetLayer(shelf_->status_area_widget());
619 // TODO(harrym): Remove when status_area is view (crbug.com/180422).
620 gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
621 status_bounds.set_x(status_bounds.x() +
622 target_bounds.shelf_bounds_in_root.x());
623 status_bounds.set_y(status_bounds.y() +
624 target_bounds.shelf_bounds_in_root.y());
625 layer->SetBounds(status_bounds);
626 layer->SetOpacity(target_bounds.opacity);
627 Shell::GetInstance()->SetDisplayWorkAreaInsets(
628 root_window_, target_bounds.work_area_insets);
629 UpdateHitTestBounds();
630 if (!delay_shelf_update)
631 UpdateShelfBackground(change_type);
632}
633
634void ShelfLayoutManager::StopAnimating() {
635 GetLayer(shelf_)->GetAnimator()->StopAnimating();
636 GetLayer(shelf_->status_area_widget())->GetAnimator()->StopAnimating();
637}
638
639void ShelfLayoutManager::GetShelfSize(int* width, int* height) {
640 *width = *height = 0;
641 gfx::Size status_size(
642 shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
643 if (IsHorizontalAlignment())
644 *height = std::max(kLauncherPreferredSize, status_size.height());
645 else
646 *width = std::max(kLauncherPreferredSize, status_size.width());
647}
648
649void ShelfLayoutManager::AdjustBoundsBasedOnAlignment(int inset,
650 gfx::Rect* bounds) const {
651 bounds->Inset(SelectValueForShelfAlignment(
652 gfx::Insets(0, 0, inset, 0),
653 gfx::Insets(0, inset, 0, 0),
654 gfx::Insets(0, 0, 0, inset),
655 gfx::Insets(inset, 0, 0, 0)));
656}
657
658void ShelfLayoutManager::CalculateTargetBounds(
659 const State& state,
660 TargetBounds* target_bounds) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100661 const gfx::Rect available_bounds(GetAvailableBounds());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000662 gfx::Rect status_size(
663 shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
664 int shelf_width = 0, shelf_height = 0;
665 GetShelfSize(&shelf_width, &shelf_height);
666 if (IsHorizontalAlignment())
667 shelf_width = available_bounds.width();
668 else
669 shelf_height = available_bounds.height();
670
671 if (state.visibility_state == SHELF_AUTO_HIDE &&
672 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100673 // Auto-hidden shelf always starts with the default size. If a gesture-drag
674 // is in progress, then the call to UpdateTargetBoundsForGesture() below
675 // takes care of setting the height properly.
676 if (IsHorizontalAlignment())
677 shelf_height = kAutoHideSize;
678 else
679 shelf_width = kAutoHideSize;
680 } else if (state.visibility_state == SHELF_HIDDEN ||
681 !keyboard_bounds_.IsEmpty()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000682 if (IsHorizontalAlignment())
683 shelf_height = 0;
684 else
685 shelf_width = 0;
686 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100687
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000688 target_bounds->shelf_bounds_in_root = SelectValueForShelfAlignment(
689 gfx::Rect(available_bounds.x(), available_bounds.bottom() - shelf_height,
690 available_bounds.width(), shelf_height),
691 gfx::Rect(available_bounds.x(), available_bounds.y(),
692 shelf_width, available_bounds.height()),
693 gfx::Rect(available_bounds.right() - shelf_width, available_bounds.y(),
694 shelf_width, available_bounds.height()),
695 gfx::Rect(available_bounds.x(), available_bounds.y(),
696 available_bounds.width(), shelf_height));
697
698 int status_inset = (kLauncherPreferredSize -
699 PrimaryAxisValue(status_size.height(), status_size.width()));
700
701 target_bounds->status_bounds_in_shelf = SelectValueForShelfAlignment(
702 gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
703 status_inset, status_size.width(), status_size.height()),
704 gfx::Rect(shelf_width - (status_size.width() + status_inset),
705 shelf_height - status_size.height(), status_size.width(),
706 status_size.height()),
707 gfx::Rect(status_inset, shelf_height - status_size.height(),
708 status_size.width(), status_size.height()),
709 gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
710 shelf_height - (status_size.height() + status_inset),
711 status_size.width(), status_size.height()));
712
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000713 target_bounds->work_area_insets = SelectValueForShelfAlignment(
714 gfx::Insets(0, 0, GetWorkAreaSize(state, shelf_height), 0),
715 gfx::Insets(0, GetWorkAreaSize(state, shelf_width), 0, 0),
716 gfx::Insets(0, 0, 0, GetWorkAreaSize(state, shelf_width)),
717 gfx::Insets(GetWorkAreaSize(state, shelf_height), 0, 0, 0));
718
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100719 // Also push in the work area inset for the keyboard if it is visible.
720 if (!keyboard_bounds_.IsEmpty()) {
721 target_bounds->work_area_insets.Set(
722 target_bounds->work_area_insets.top(),
723 target_bounds->work_area_insets.left(),
724 target_bounds->work_area_insets.bottom() + keyboard_bounds_.height(),
725 target_bounds->work_area_insets.right());
726 }
727
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000728 target_bounds->opacity =
729 (gesture_drag_status_ != GESTURE_DRAG_NONE ||
730 state.visibility_state == SHELF_VISIBLE ||
731 state.visibility_state == SHELF_AUTO_HIDE) ? 1.0f : 0.0f;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100732
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000733 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS)
734 UpdateTargetBoundsForGesture(target_bounds);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100735
736 // This needs to happen after calling UpdateTargetBoundsForGesture(), because
737 // that can change the size of the shelf.
738 target_bounds->launcher_bounds_in_shelf = SelectValueForShelfAlignment(
739 gfx::Rect(base::i18n::IsRTL() ? status_size.width() : 0, 0,
740 shelf_width - status_size.width(),
741 target_bounds->shelf_bounds_in_root.height()),
742 gfx::Rect(0, 0, shelf_width, shelf_height - status_size.height()),
743 gfx::Rect(0, 0, shelf_width, shelf_height - status_size.height()),
744 gfx::Rect(base::i18n::IsRTL() ? status_size.width() : 0, 0,
745 shelf_width - status_size.width(),
746 target_bounds->shelf_bounds_in_root.height()));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000747}
748
749void ShelfLayoutManager::UpdateTargetBoundsForGesture(
750 TargetBounds* target_bounds) const {
751 CHECK_EQ(GESTURE_DRAG_IN_PROGRESS, gesture_drag_status_);
752 bool horizontal = IsHorizontalAlignment();
753 const gfx::Rect& available_bounds(root_window_->bounds());
754 int resistance_free_region = 0;
755
756 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
757 visibility_state() == SHELF_AUTO_HIDE &&
758 auto_hide_state() != SHELF_AUTO_HIDE_SHOWN) {
759 // If the shelf was hidden when the drag started (and the state hasn't
760 // changed since then, e.g. because the tray-menu was shown because of the
761 // drag), then allow the drag some resistance-free region at first to make
762 // sure the shelf sticks with the finger until the shelf is visible.
763 resistance_free_region = kLauncherPreferredSize - kAutoHideSize;
764 }
765
766 bool resist = SelectValueForShelfAlignment(
767 gesture_drag_amount_ < -resistance_free_region,
768 gesture_drag_amount_ > resistance_free_region,
769 gesture_drag_amount_ < -resistance_free_region,
770 gesture_drag_amount_ > resistance_free_region);
771
772 float translate = 0.f;
773 if (resist) {
774 float diff = fabsf(gesture_drag_amount_) - resistance_free_region;
775 diff = std::min(diff, sqrtf(diff));
776 if (gesture_drag_amount_ < 0)
777 translate = -resistance_free_region - diff;
778 else
779 translate = resistance_free_region + diff;
780 } else {
781 translate = gesture_drag_amount_;
782 }
783
784 if (horizontal) {
785 // Move and size the launcher with the gesture.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100786 int shelf_height = target_bounds->shelf_bounds_in_root.height() - translate;
787 shelf_height = std::max(shelf_height, kAutoHideSize);
788 target_bounds->shelf_bounds_in_root.set_height(shelf_height);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000789 if (alignment_ == SHELF_ALIGNMENT_BOTTOM) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100790 target_bounds->shelf_bounds_in_root.set_y(
791 available_bounds.bottom() - shelf_height);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000792 }
793
794 // The statusbar should be in the center of the shelf.
795 gfx::Rect status_y = target_bounds->shelf_bounds_in_root;
796 status_y.set_y(0);
797 status_y.ClampToCenteredSize(
798 target_bounds->status_bounds_in_shelf.size());
799 target_bounds->status_bounds_in_shelf.set_y(status_y.y());
800 } else {
801 // Move and size the launcher with the gesture.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100802 int shelf_width = target_bounds->shelf_bounds_in_root.width() - translate;
803 shelf_width = std::max(shelf_width, kAutoHideSize);
804 target_bounds->shelf_bounds_in_root.set_width(shelf_width);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000805 if (alignment_ == SHELF_ALIGNMENT_RIGHT) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100806 target_bounds->shelf_bounds_in_root.set_y(
807 available_bounds.right() - shelf_width);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000808 }
809
810 // The statusbar should be in the center of the shelf.
811 gfx::Rect status_x = target_bounds->shelf_bounds_in_root;
812 status_x.set_x(0);
813 status_x.ClampToCenteredSize(
814 target_bounds->status_bounds_in_shelf.size());
815 target_bounds->status_bounds_in_shelf.set_x(status_x.x());
816 }
817}
818
819void ShelfLayoutManager::UpdateShelfBackground(
820 BackgroundAnimator::ChangeType type) {
821 bool launcher_paints = GetLauncherPaintsBackground();
822 shelf_->SetPaintsBackground(launcher_paints, type);
823}
824
825bool ShelfLayoutManager::GetLauncherPaintsBackground() const {
826 return gesture_drag_status_ != GESTURE_DRAG_NONE ||
827 (!state_.is_screen_locked && window_overlaps_shelf_) ||
828 (state_.visibility_state == SHELF_AUTO_HIDE) ;
829}
830
831void ShelfLayoutManager::UpdateAutoHideStateNow() {
832 SetState(state_.visibility_state);
833}
834
835ShelfAutoHideState ShelfLayoutManager::CalculateAutoHideState(
836 ShelfVisibilityState visibility_state) const {
837 if (visibility_state != SHELF_AUTO_HIDE || !shelf_)
838 return SHELF_AUTO_HIDE_HIDDEN;
839
840 if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS)
841 return gesture_drag_auto_hide_state_;
842
843 Shell* shell = Shell::GetInstance();
844 if (shell->GetAppListTargetVisibility())
845 return SHELF_AUTO_HIDE_SHOWN;
846
847 if (shelf_->status_area_widget() &&
848 shelf_->status_area_widget()->ShouldShowLauncher())
849 return SHELF_AUTO_HIDE_SHOWN;
850
851 if (shelf_->launcher() && shelf_->launcher()->IsShowingMenu())
852 return SHELF_AUTO_HIDE_SHOWN;
853
854 if (shelf_->launcher() && shelf_->launcher()->IsShowingOverflowBubble())
855 return SHELF_AUTO_HIDE_SHOWN;
856
857 if (shelf_->IsActive() || shelf_->status_area_widget()->IsActive())
858 return SHELF_AUTO_HIDE_SHOWN;
859
860 // Don't show if the user is dragging the mouse.
861 if (event_filter_.get() && event_filter_->in_mouse_drag())
862 return SHELF_AUTO_HIDE_HIDDEN;
863
864 gfx::Rect shelf_region = shelf_->GetWindowBoundsInScreen();
865 if (shelf_->status_area_widget() &&
866 shelf_->status_area_widget()->IsMessageBubbleShown() &&
867 IsVisible()) {
868 // Increase the the hit test area to prevent the shelf from disappearing
869 // when the mouse is over the bubble gap.
870 shelf_region.Inset(alignment_ == SHELF_ALIGNMENT_RIGHT ?
871 -kNotificationBubbleGapHeight : 0,
872 alignment_ == SHELF_ALIGNMENT_BOTTOM ?
873 -kNotificationBubbleGapHeight : 0,
874 alignment_ == SHELF_ALIGNMENT_LEFT ?
875 -kNotificationBubbleGapHeight : 0,
876 alignment_ == SHELF_ALIGNMENT_TOP ?
877 -kNotificationBubbleGapHeight : 0);
878 }
879
880 if (shelf_region.Contains(Shell::GetScreen()->GetCursorScreenPoint()))
881 return SHELF_AUTO_HIDE_SHOWN;
882
883 const std::vector<aura::Window*> windows =
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100884 ash::WindowCycleController::BuildWindowList(NULL, false);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000885
886 // Process the window list and check if there are any visible windows.
887 for (size_t i = 0; i < windows.size(); ++i) {
888 if (windows[i] && windows[i]->IsVisible() &&
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100889 !ash::wm::IsWindowMinimized(windows[i]) &&
890 root_window_ == windows[i]->GetRootWindow())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000891 return SHELF_AUTO_HIDE_HIDDEN;
892 }
893
894 // If there are no visible windows do not hide the shelf.
895 return SHELF_AUTO_HIDE_SHOWN;
896}
897
898void ShelfLayoutManager::UpdateHitTestBounds() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100899 gfx::Insets mouse_insets;
900 gfx::Insets touch_insets;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000901 if (state_.visibility_state == SHELF_VISIBLE) {
902 // Let clicks at the very top of the launcher through so windows can be
903 // resized with the bottom-right corner and bottom edge.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100904 mouse_insets = GetInsetsForAlignment(kWorkspaceAreaVisibleInset);
905 } else if (state_.visibility_state == SHELF_AUTO_HIDE) {
906 // Extend the touch hit target out a bit to allow users to drag shelf out
907 // while hidden.
908 touch_insets = GetInsetsForAlignment(-kWorkspaceAreaAutoHideInset);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000909 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100910
911 if (shelf_ && shelf_->GetNativeWindow())
912 shelf_->GetNativeWindow()->SetHitTestBoundsOverrideOuter(mouse_insets,
913 touch_insets);
914 shelf_->status_area_widget()->GetNativeWindow()->
915 SetHitTestBoundsOverrideOuter(mouse_insets, touch_insets);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000916}
917
918bool ShelfLayoutManager::IsShelfWindow(aura::Window* window) {
919 if (!window)
920 return false;
921 return (shelf_ &&
922 shelf_->GetNativeWindow()->Contains(window)) ||
923 (shelf_->status_area_widget()->GetNativeWindow()->Contains(window));
924}
925
926int ShelfLayoutManager::GetWorkAreaSize(const State& state, int size) const {
927 if (state.visibility_state == SHELF_VISIBLE)
928 return size;
929 if (state.visibility_state == SHELF_AUTO_HIDE)
930 return kAutoHideSize;
931 return 0;
932}
933
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100934gfx::Rect ShelfLayoutManager::GetAvailableBounds() const {
935 gfx::Rect bounds(root_window_->bounds());
936 bounds.set_height(bounds.height() - keyboard_bounds_.height());
937 return bounds;
938}
939
940void ShelfLayoutManager::OnKeyboardBoundsChanging(
941 const gfx::Rect& keyboard_bounds) {
942 keyboard_bounds_ = keyboard_bounds;
943 OnWindowResized();
944}
945
946gfx::Insets ShelfLayoutManager::GetInsetsForAlignment(int distance) const {
947 switch (alignment_) {
948 case SHELF_ALIGNMENT_BOTTOM:
949 return gfx::Insets(distance, 0, 0, 0);
950 case SHELF_ALIGNMENT_LEFT:
951 return gfx::Insets(0, 0, 0, distance);
952 case SHELF_ALIGNMENT_RIGHT:
953 return gfx::Insets(0, distance, 0, 0);
954 case SHELF_ALIGNMENT_TOP:
955 return gfx::Insets(0, 0, distance, 0);
956 }
957 NOTREACHED();
958 return gfx::Insets();
959}
960
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000961} // namespace internal
962} // namespace ash