blob: c0e5f778638a6d6047c87fd809992d6c368af5c6 [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>
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +01009#include <cstring>
10#include <string>
11#include <vector>
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000012
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"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010018#include "ash/session_state_delegate.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010019#include "ash/shelf/shelf_bezel_event_filter.h"
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +010020#include "ash/shelf/shelf_layout_manager_observer.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000021#include "ash/shelf/shelf_widget.h"
22#include "ash/shell.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000023#include "ash/shell_window_ids.h"
24#include "ash/system/status_area_widget.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010025#include "ash/wm/gestures/shelf_gesture_handler.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010026#include "ash/wm/mru_window_tracker.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000027#include "ash/wm/property_util.h"
Ben Murdoch2385ea32013-08-06 11:01:04 +010028#include "ash/wm/window_animations.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010029#include "ash/wm/window_properties.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000030#include "ash/wm/window_util.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010031#include "ash/wm/workspace_controller.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000032#include "base/auto_reset.h"
33#include "base/command_line.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010034#include "base/command_line.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000035#include "base/i18n/rtl.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010036#include "base/strings/string_number_conversions.h"
37#include "base/strings/string_util.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000038#include "ui/aura/client/activation_client.h"
39#include "ui/aura/root_window.h"
40#include "ui/base/events/event.h"
41#include "ui/base/events/event_handler.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010042#include "ui/base/ui_base_switches.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000043#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/gfx/screen.h"
48#include "ui/views/widget/widget.h"
49
50namespace ash {
51namespace internal {
52
53namespace {
54
55// Delay before showing the launcher. This is after the mouse stops moving.
56const int kAutoHideDelayMS = 200;
57
58// To avoid hiding the shelf when the mouse transitions from a message bubble
59// into the shelf, the hit test area is enlarged by this amount of pixels to
60// keep the shelf from hiding.
61const int kNotificationBubbleGapHeight = 6;
62
Ben Murdochbb1529c2013-08-08 10:24:53 +010063// The maximum size of the region on the display opposing the shelf managed by
64// this ShelfLayoutManager which can trigger showing the shelf.
65// For instance:
66// - Primary display is left of secondary display.
67// - Shelf is left aligned
68// - This ShelfLayoutManager manages the shelf for the secondary display.
69// |kMaxAutoHideShowShelfRegionSize| refers to the maximum size of the region
70// from the right edge of the primary display which can trigger showing the
71// auto hidden shelf. The region is used to make it easier to trigger showing
72// the auto hidden shelf when the shelf is on the boundary between displays.
73const int kMaxAutoHideShowShelfRegionSize = 10;
74
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000075ui::Layer* GetLayer(views::Widget* widget) {
76 return widget->GetNativeView()->layer();
77}
78
79bool IsDraggingTrayEnabled() {
80 static bool dragging_tray_allowed = CommandLine::ForCurrentProcess()->
81 HasSwitch(ash::switches::kAshEnableTrayDragging);
82 return dragging_tray_allowed;
83}
84
85} // namespace
86
87// static
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010088const int ShelfLayoutManager::kWorkspaceAreaVisibleInset = 2;
89
90// static
91const int ShelfLayoutManager::kWorkspaceAreaAutoHideInset = 5;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000092
93// static
94const int ShelfLayoutManager::kAutoHideSize = 3;
95
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010096// static
97const int ShelfLayoutManager::kShelfSize = 47;
98
Ben Murdochca12bfa2013-07-23 11:17:05 +010099int ShelfLayoutManager::GetPreferredShelfSize() {
100 return ash::switches::UseAlternateShelfLayout() ?
101 ShelfLayoutManager::kShelfSize : kLauncherPreferredSize;
102}
103
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000104// ShelfLayoutManager::AutoHideEventFilter -------------------------------------
105
106// Notifies ShelfLayoutManager any time the mouse moves.
107class ShelfLayoutManager::AutoHideEventFilter : public ui::EventHandler {
108 public:
109 explicit AutoHideEventFilter(ShelfLayoutManager* shelf);
110 virtual ~AutoHideEventFilter();
111
112 // Returns true if the last mouse event was a mouse drag.
113 bool in_mouse_drag() const { return in_mouse_drag_; }
114
115 // Overridden from ui::EventHandler:
116 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100117 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000118
119 private:
120 ShelfLayoutManager* shelf_;
121 bool in_mouse_drag_;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100122 ShelfGestureHandler gesture_handler_;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000123 DISALLOW_COPY_AND_ASSIGN(AutoHideEventFilter);
124};
125
126ShelfLayoutManager::AutoHideEventFilter::AutoHideEventFilter(
127 ShelfLayoutManager* shelf)
128 : shelf_(shelf),
129 in_mouse_drag_(false) {
130 Shell::GetInstance()->AddPreTargetHandler(this);
131}
132
133ShelfLayoutManager::AutoHideEventFilter::~AutoHideEventFilter() {
134 Shell::GetInstance()->RemovePreTargetHandler(this);
135}
136
137void ShelfLayoutManager::AutoHideEventFilter::OnMouseEvent(
138 ui::MouseEvent* event) {
139 // This also checks IsShelfWindow() to make sure we don't attempt to hide the
140 // shelf if the mouse down occurs on the shelf.
141 in_mouse_drag_ = (event->type() == ui::ET_MOUSE_DRAGGED ||
142 (in_mouse_drag_ && event->type() != ui::ET_MOUSE_RELEASED &&
143 event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)) &&
144 !shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()));
145 if (event->type() == ui::ET_MOUSE_MOVED)
146 shelf_->UpdateAutoHideState();
147 return;
148}
149
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100150void ShelfLayoutManager::AutoHideEventFilter::OnGestureEvent(
151 ui::GestureEvent* event) {
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100152 if (shelf_->IsShelfWindow(static_cast<aura::Window*>(event->target()))) {
153 if (gesture_handler_.ProcessGestureEvent(*event))
154 event->StopPropagation();
155 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100156}
157
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000158// ShelfLayoutManager:UpdateShelfObserver --------------------------------------
159
160// UpdateShelfObserver is used to delay updating the background until the
161// animation completes.
162class ShelfLayoutManager::UpdateShelfObserver
163 : public ui::ImplicitAnimationObserver {
164 public:
165 explicit UpdateShelfObserver(ShelfLayoutManager* shelf) : shelf_(shelf) {
166 shelf_->update_shelf_observer_ = this;
167 }
168
169 void Detach() {
170 shelf_ = NULL;
171 }
172
173 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
174 if (shelf_) {
175 shelf_->UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
176 }
177 delete this;
178 }
179
180 private:
181 virtual ~UpdateShelfObserver() {
182 if (shelf_)
183 shelf_->update_shelf_observer_ = NULL;
184 }
185
186 // Shelf we're in. NULL if deleted before we're deleted.
187 ShelfLayoutManager* shelf_;
188
189 DISALLOW_COPY_AND_ASSIGN(UpdateShelfObserver);
190};
191
192// ShelfLayoutManager ----------------------------------------------------------
193
194ShelfLayoutManager::ShelfLayoutManager(ShelfWidget* shelf)
195 : root_window_(shelf->GetNativeView()->GetRootWindow()),
196 in_layout_(false),
197 auto_hide_behavior_(SHELF_AUTO_HIDE_BEHAVIOR_NEVER),
198 alignment_(SHELF_ALIGNMENT_BOTTOM),
199 shelf_(shelf),
200 workspace_controller_(NULL),
201 window_overlaps_shelf_(false),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100202 mouse_over_shelf_when_auto_hide_timer_started_(false),
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100203 bezel_event_filter_(new ShelfBezelEventFilter(this)),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000204 gesture_drag_status_(GESTURE_DRAG_NONE),
205 gesture_drag_amount_(0.f),
206 gesture_drag_auto_hide_state_(SHELF_AUTO_HIDE_SHOWN),
207 update_shelf_observer_(NULL) {
208 Shell::GetInstance()->AddShellObserver(this);
209 aura::client::GetActivationClient(root_window_)->AddObserver(this);
210}
211
212ShelfLayoutManager::~ShelfLayoutManager() {
213 if (update_shelf_observer_)
214 update_shelf_observer_->Detach();
215
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100216 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, WillDeleteShelf());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000217 Shell::GetInstance()->RemoveShellObserver(this);
218 aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
219}
220
221void ShelfLayoutManager::SetAutoHideBehavior(ShelfAutoHideBehavior behavior) {
222 if (auto_hide_behavior_ == behavior)
223 return;
224 auto_hide_behavior_ = behavior;
225 UpdateVisibilityState();
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100226 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100227 OnAutoHideBehaviorChanged(root_window_,
228 auto_hide_behavior_));
229}
230
231void ShelfLayoutManager::PrepareForShutdown() {
232 // Clear all event filters, otherwise sometimes those filters may catch
233 // synthesized mouse event and cause crashes during the shutdown.
234 set_workspace_controller(NULL);
235 auto_hide_event_filter_.reset();
236 bezel_event_filter_.reset();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000237}
238
239bool ShelfLayoutManager::IsVisible() const {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100240 // status_area_widget() may be NULL during the shutdown.
241 return shelf_->status_area_widget() &&
242 shelf_->status_area_widget()->IsVisible() &&
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000243 (state_.visibility_state == SHELF_VISIBLE ||
244 (state_.visibility_state == SHELF_AUTO_HIDE &&
245 state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN));
246}
247
248bool ShelfLayoutManager::SetAlignment(ShelfAlignment alignment) {
249 if (alignment_ == alignment)
250 return false;
251
252 alignment_ = alignment;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100253 shelf_->SetAlignment(alignment);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000254 LayoutShelf();
255 return true;
256}
257
258gfx::Rect ShelfLayoutManager::GetIdealBounds() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000259 gfx::Rect bounds(
260 ScreenAsh::GetDisplayBoundsInParent(shelf_->GetNativeView()));
261 int width = 0, height = 0;
262 GetShelfSize(&width, &height);
263 return SelectValueForShelfAlignment(
264 gfx::Rect(bounds.x(), bounds.bottom() - height, bounds.width(), height),
265 gfx::Rect(bounds.x(), bounds.y(), width, bounds.height()),
266 gfx::Rect(bounds.right() - width, bounds.y(), width, bounds.height()),
267 gfx::Rect(bounds.x(), bounds.y(), bounds.width(), height));
268}
269
270void ShelfLayoutManager::LayoutShelf() {
271 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
272 StopAnimating();
273 TargetBounds target_bounds;
274 CalculateTargetBounds(state_, &target_bounds);
275 GetLayer(shelf_)->SetOpacity(target_bounds.opacity);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100276 shelf_->SetWidgetBounds(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000277 ScreenAsh::ConvertRectToScreen(
278 shelf_->GetNativeView()->parent(),
279 target_bounds.shelf_bounds_in_root));
280 if (shelf_->launcher())
281 shelf_->launcher()->SetLauncherViewBounds(
282 target_bounds.launcher_bounds_in_shelf);
283 GetLayer(shelf_->status_area_widget())->SetOpacity(target_bounds.opacity);
284 // TODO(harrym): Once status area widget is a child view of shelf
285 // this can be simplified.
286 gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
287 status_bounds.set_x(status_bounds.x() +
288 target_bounds.shelf_bounds_in_root.x());
289 status_bounds.set_y(status_bounds.y() +
290 target_bounds.shelf_bounds_in_root.y());
291 shelf_->status_area_widget()->SetBounds(
292 ScreenAsh::ConvertRectToScreen(
293 shelf_->status_area_widget()->GetNativeView()->parent(),
294 status_bounds));
295 Shell::GetInstance()->SetDisplayWorkAreaInsets(
296 root_window_, target_bounds.work_area_insets);
297 UpdateHitTestBounds();
298}
299
300ShelfVisibilityState ShelfLayoutManager::CalculateShelfVisibility() {
301 switch(auto_hide_behavior_) {
302 case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
303 return SHELF_AUTO_HIDE;
304 case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
305 return SHELF_VISIBLE;
306 case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
307 return SHELF_HIDDEN;
308 }
309 return SHELF_VISIBLE;
310}
311
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000312void ShelfLayoutManager::UpdateVisibilityState() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100313 if (Shell::GetInstance()->session_state_delegate()->IsScreenLocked()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000314 SetState(SHELF_VISIBLE);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100315 } else {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000316 // TODO(zelidrag): Verify shelf drag animation still shows on the device
317 // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000318 WorkspaceWindowState window_state(workspace_controller_->GetWindowState());
319 switch (window_state) {
320 case WORKSPACE_WINDOW_STATE_FULL_SCREEN:
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100321 if (FullscreenWithMinimalChrome()) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100322 SetState(SHELF_AUTO_HIDE);
323 } else {
324 SetState(SHELF_HIDDEN);
325 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000326 break;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000327 case WORKSPACE_WINDOW_STATE_MAXIMIZED:
328 SetState(CalculateShelfVisibility());
329 break;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000330 case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF:
331 case WORKSPACE_WINDOW_STATE_DEFAULT:
332 SetState(CalculateShelfVisibility());
333 SetWindowOverlapsShelf(window_state ==
334 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF);
335 break;
336 }
337 }
338}
339
340void ShelfLayoutManager::UpdateAutoHideState() {
341 ShelfAutoHideState auto_hide_state =
342 CalculateAutoHideState(state_.visibility_state);
343 if (auto_hide_state != state_.auto_hide_state) {
344 if (auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
345 // Hides happen immediately.
346 SetState(state_.visibility_state);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000347 } else {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100348 if (!auto_hide_timer_.IsRunning()) {
349 mouse_over_shelf_when_auto_hide_timer_started_ =
350 shelf_->GetWindowBoundsInScreen().Contains(
351 Shell::GetScreen()->GetCursorScreenPoint());
352 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000353 auto_hide_timer_.Start(
354 FROM_HERE,
355 base::TimeDelta::FromMilliseconds(kAutoHideDelayMS),
356 this, &ShelfLayoutManager::UpdateAutoHideStateNow);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000357 }
358 } else {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100359 StopAutoHideTimer();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000360 }
361}
362
363void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) {
364 window_overlaps_shelf_ = value;
365 UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
366}
367
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100368void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000369 observers_.AddObserver(observer);
370}
371
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100372void ShelfLayoutManager::RemoveObserver(ShelfLayoutManagerObserver* observer) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000373 observers_.RemoveObserver(observer);
374}
375
376////////////////////////////////////////////////////////////////////////////////
377// ShelfLayoutManager, Gesture dragging:
378
379void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) {
380 gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS;
381 gesture_drag_amount_ = 0.f;
382 gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ?
383 auto_hide_state() : SHELF_AUTO_HIDE_SHOWN;
384 UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
385}
386
387ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag(
388 const ui::GestureEvent& gesture) {
389 bool horizontal = IsHorizontalAlignment();
390 gesture_drag_amount_ += horizontal ? gesture.details().scroll_y() :
391 gesture.details().scroll_x();
392 LayoutShelf();
393
394 // Start reveling the status menu when:
395 // - dragging up on an already visible shelf
396 // - dragging up on a hidden shelf, but it is currently completely visible.
397 if (horizontal && gesture.details().scroll_y() < 0) {
398 int min_height = 0;
399 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN && shelf_)
400 min_height = shelf_->GetContentsView()->GetPreferredSize().height();
401
402 if (min_height < shelf_->GetWindowBoundsInScreen().height() &&
403 gesture.root_location().x() >=
404 shelf_->status_area_widget()->GetWindowBoundsInScreen().x() &&
405 IsDraggingTrayEnabled())
406 return DRAG_TRAY;
407 }
408
409 return DRAG_SHELF;
410}
411
412void ShelfLayoutManager::CompleteGestureDrag(const ui::GestureEvent& gesture) {
413 bool horizontal = IsHorizontalAlignment();
414 bool should_change = false;
415 if (gesture.type() == ui::ET_GESTURE_SCROLL_END) {
416 // The visibility of the shelf changes only if the shelf was dragged X%
417 // along the correct axis. If the shelf was already visible, then the
418 // direction of the drag does not matter.
419 const float kDragHideThreshold = 0.4f;
420 gfx::Rect bounds = GetIdealBounds();
421 float drag_ratio = fabs(gesture_drag_amount_) /
422 (horizontal ? bounds.height() : bounds.width());
423 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
424 should_change = drag_ratio > kDragHideThreshold;
425 } else {
426 bool correct_direction = false;
427 switch (alignment_) {
428 case SHELF_ALIGNMENT_BOTTOM:
429 case SHELF_ALIGNMENT_RIGHT:
430 correct_direction = gesture_drag_amount_ < 0;
431 break;
432 case SHELF_ALIGNMENT_LEFT:
433 case SHELF_ALIGNMENT_TOP:
434 correct_direction = gesture_drag_amount_ > 0;
435 break;
436 }
437 should_change = correct_direction && drag_ratio > kDragHideThreshold;
438 }
439 } else if (gesture.type() == ui::ET_SCROLL_FLING_START) {
440 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
441 should_change = horizontal ? fabs(gesture.details().velocity_y()) > 0 :
442 fabs(gesture.details().velocity_x()) > 0;
443 } else {
444 should_change = SelectValueForShelfAlignment(
445 gesture.details().velocity_y() < 0,
446 gesture.details().velocity_x() > 0,
447 gesture.details().velocity_x() < 0,
448 gesture.details().velocity_y() > 0);
449 }
450 } else {
451 NOTREACHED();
452 }
453
454 if (!should_change) {
455 CancelGestureDrag();
456 return;
457 }
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100458 if (shelf_) {
459 shelf_->Deactivate();
460 shelf_->status_area_widget()->Deactivate();
461 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000462 gesture_drag_auto_hide_state_ =
463 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
464 SHELF_AUTO_HIDE_HIDDEN : SHELF_AUTO_HIDE_SHOWN;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100465 ShelfAutoHideBehavior new_auto_hide_behavior =
466 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN ?
467 SHELF_AUTO_HIDE_BEHAVIOR_NEVER : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
468
469 // In fullscreen with minimal chrome, the auto hide behavior affects neither
470 // the visibility state nor the auto hide state. Set |gesture_drag_status_|
471 // to GESTURE_DRAG_COMPLETE_IN_PROGRESS to set the auto hide state to
472 // |gesture_drag_auto_hide_state_|.
473 gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS;
474 if (auto_hide_behavior_ != new_auto_hide_behavior)
475 SetAutoHideBehavior(new_auto_hide_behavior);
476 else
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000477 UpdateVisibilityState();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100478 gesture_drag_status_ = GESTURE_DRAG_NONE;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100479 LayoutShelf();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000480}
481
482void ShelfLayoutManager::CancelGestureDrag() {
483 gesture_drag_status_ = GESTURE_DRAG_NONE;
484 ui::ScopedLayerAnimationSettings
485 launcher_settings(GetLayer(shelf_)->GetAnimator()),
486 status_settings(GetLayer(shelf_->status_area_widget())->GetAnimator());
487 LayoutShelf();
488 UpdateVisibilityState();
489 UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
490}
491
492////////////////////////////////////////////////////////////////////////////////
493// ShelfLayoutManager, aura::LayoutManager implementation:
494
495void ShelfLayoutManager::OnWindowResized() {
496 LayoutShelf();
497}
498
499void ShelfLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
500}
501
502void ShelfLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
503}
504
505void ShelfLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
506}
507
508void ShelfLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
509 bool visible) {
510}
511
512void ShelfLayoutManager::SetChildBounds(aura::Window* child,
513 const gfx::Rect& requested_bounds) {
514 SetChildBoundsDirect(child, requested_bounds);
515 // We may contain other widgets (such as frame maximize bubble) but they don't
516 // effect the layout in anyway.
517 if (!in_layout_ &&
518 ((shelf_->GetNativeView() == child) ||
519 (shelf_->status_area_widget()->GetNativeView() == child))) {
520 LayoutShelf();
521 }
522}
523
524void ShelfLayoutManager::OnLockStateChanged(bool locked) {
525 UpdateVisibilityState();
526}
527
528void ShelfLayoutManager::OnWindowActivated(aura::Window* gained_active,
529 aura::Window* lost_active) {
530 UpdateAutoHideStateNow();
531}
532
533bool ShelfLayoutManager::IsHorizontalAlignment() const {
534 return alignment_ == SHELF_ALIGNMENT_BOTTOM ||
535 alignment_ == SHELF_ALIGNMENT_TOP;
536}
537
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100538bool ShelfLayoutManager::FullscreenWithMinimalChrome() const {
539 RootWindowController* controller = GetRootWindowController(root_window_);
540 if (!controller)
541 return false;
Ben Murdoch2385ea32013-08-06 11:01:04 +0100542 const aura::Window* window = controller->GetFullscreenWindow();
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100543 if (!window)
544 return false;
545 if (!window->GetProperty(kFullscreenUsesMinimalChromeKey))
546 return false;
547 return true;
548}
549
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000550// static
551ShelfLayoutManager* ShelfLayoutManager::ForLauncher(aura::Window* window) {
552 ShelfWidget* shelf = RootWindowController::ForLauncher(window)->shelf();
553 return shelf ? shelf->shelf_layout_manager() : NULL;
554}
555
556////////////////////////////////////////////////////////////////////////////////
557// ShelfLayoutManager, private:
558
559ShelfLayoutManager::TargetBounds::TargetBounds() : opacity(0.0f) {}
560ShelfLayoutManager::TargetBounds::~TargetBounds() {}
561
562void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100563 if (!shelf_->GetNativeView())
564 return;
565
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000566 State state;
567 state.visibility_state = visibility_state;
568 state.auto_hide_state = CalculateAutoHideState(visibility_state);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100569 state.is_screen_locked =
570 Shell::GetInstance()->session_state_delegate()->IsScreenLocked();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100571 state.window_state = workspace_controller_ ?
572 workspace_controller_->GetWindowState() : WORKSPACE_WINDOW_STATE_DEFAULT;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000573
574 // It's possible for SetState() when a window becomes maximized but the state
575 // won't have changed value. Do the dimming check before the early exit.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100576 shelf_->SetDimsShelf(
577 (state.visibility_state == SHELF_VISIBLE) &&
578 state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000579
580 if (state_.Equals(state))
581 return; // Nothing changed.
582
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100583 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000584 WillChangeVisibilityState(visibility_state));
585
586 if (state.visibility_state == SHELF_AUTO_HIDE) {
587 // When state is SHELF_AUTO_HIDE we need to track when the mouse is over the
588 // launcher to unhide the shelf. AutoHideEventFilter does that for us.
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100589 if (!auto_hide_event_filter_)
590 auto_hide_event_filter_.reset(new AutoHideEventFilter(this));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000591 } else {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100592 auto_hide_event_filter_.reset(NULL);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000593 }
594
Ben Murdochbb1529c2013-08-08 10:24:53 +0100595 StopAutoHideTimer();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000596
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100597 // The transition of background from auto-hide to visible is janky if the
598 // transition also cause the shelf's slide animation from the bottom edge.
599 // This happens if:
600 // - shelf is hidden
601 // - or, shelf is visible but workspace state is maximized
602 bool keep_maximized = state_.window_state == state.window_state &&
603 state_.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000604 BackgroundAnimator::ChangeType change_type =
605 (state_.visibility_state == SHELF_AUTO_HIDE &&
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100606 state.visibility_state == SHELF_VISIBLE &&
607 (state_.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN ||
608 keep_maximized)) ?
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000609 BackgroundAnimator::CHANGE_IMMEDIATE : BackgroundAnimator::CHANGE_ANIMATE;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000610
611 State old_state = state_;
612 state_ = state;
613 TargetBounds target_bounds;
614 CalculateTargetBounds(state_, &target_bounds);
615
616 ui::ScopedLayerAnimationSettings launcher_animation_setter(
617 GetLayer(shelf_)->GetAnimator());
618 launcher_animation_setter.SetTransitionDuration(
Ben Murdoch2385ea32013-08-06 11:01:04 +0100619 base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000620 launcher_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
Ben Murdocheb525c52013-07-10 11:40:50 +0100621 launcher_animation_setter.SetPreemptionStrategy(
622 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000623 GetLayer(shelf_)->SetBounds(
624 target_bounds.shelf_bounds_in_root);
625 GetLayer(shelf_)->SetOpacity(target_bounds.opacity);
626 ui::ScopedLayerAnimationSettings status_animation_setter(
627 GetLayer(shelf_->status_area_widget())->GetAnimator());
628 status_animation_setter.SetTransitionDuration(
Ben Murdoch2385ea32013-08-06 11:01:04 +0100629 base::TimeDelta::FromMilliseconds(kCrossFadeDurationMS));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000630 status_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
Ben Murdocheb525c52013-07-10 11:40:50 +0100631 status_animation_setter.SetPreemptionStrategy(
632 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000633
634 // Delay updating the background when going from SHELF_AUTO_HIDE_SHOWN to
635 // SHELF_AUTO_HIDE_HIDDEN until the shelf animates out. Otherwise during the
636 // animation you see the background change.
637 // Also delay the animation when the shelf was hidden, and has just been made
638 // visible (e.g. using a gesture-drag).
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100639 // But do not delay if the transition happens when a window is maximized.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000640 bool delay_shelf_update =
641 state.visibility_state == SHELF_AUTO_HIDE &&
642 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
643 old_state.visibility_state == SHELF_AUTO_HIDE;
644
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100645 if (!keep_maximized && state.visibility_state == SHELF_VISIBLE &&
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000646 old_state.visibility_state == SHELF_AUTO_HIDE &&
647 old_state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN)
648 delay_shelf_update = true;
649
650 if (delay_shelf_update) {
651 if (update_shelf_observer_)
652 update_shelf_observer_->Detach();
653 // UpdateShelfBackground deletes itself when the animation is done.
654 update_shelf_observer_ = new UpdateShelfObserver(this);
655 status_animation_setter.AddObserver(update_shelf_observer_);
656 }
657 ui::Layer* layer = GetLayer(shelf_->status_area_widget());
658 // TODO(harrym): Remove when status_area is view (crbug.com/180422).
659 gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
660 status_bounds.set_x(status_bounds.x() +
661 target_bounds.shelf_bounds_in_root.x());
662 status_bounds.set_y(status_bounds.y() +
663 target_bounds.shelf_bounds_in_root.y());
664 layer->SetBounds(status_bounds);
Ben Murdocheb525c52013-07-10 11:40:50 +0100665 layer->SetOpacity(target_bounds.status_opacity);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000666 Shell::GetInstance()->SetDisplayWorkAreaInsets(
667 root_window_, target_bounds.work_area_insets);
668 UpdateHitTestBounds();
669 if (!delay_shelf_update)
670 UpdateShelfBackground(change_type);
Ben Murdoch558790d2013-07-30 15:19:42 +0100671
672 // OnAutoHideStateChanged Should be emitted when:
673 // - firstly state changed to auto-hide from other state
674 // - or, auto_hide_state has changed
675 if ((old_state.visibility_state != state_.visibility_state &&
676 state_.visibility_state == SHELF_AUTO_HIDE) ||
677 old_state.auto_hide_state != state_.auto_hide_state) {
678 FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
679 OnAutoHideStateChanged(state_.auto_hide_state));
680 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000681}
682
683void ShelfLayoutManager::StopAnimating() {
684 GetLayer(shelf_)->GetAnimator()->StopAnimating();
685 GetLayer(shelf_->status_area_widget())->GetAnimator()->StopAnimating();
686}
687
688void ShelfLayoutManager::GetShelfSize(int* width, int* height) {
689 *width = *height = 0;
690 gfx::Size status_size(
691 shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
692 if (IsHorizontalAlignment())
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100693 *height = GetPreferredShelfSize();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000694 else
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100695 *width = GetPreferredShelfSize();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000696}
697
698void ShelfLayoutManager::AdjustBoundsBasedOnAlignment(int inset,
699 gfx::Rect* bounds) const {
700 bounds->Inset(SelectValueForShelfAlignment(
701 gfx::Insets(0, 0, inset, 0),
702 gfx::Insets(0, inset, 0, 0),
703 gfx::Insets(0, 0, 0, inset),
704 gfx::Insets(inset, 0, 0, 0)));
705}
706
707void ShelfLayoutManager::CalculateTargetBounds(
708 const State& state,
709 TargetBounds* target_bounds) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100710 const gfx::Rect available_bounds(GetAvailableBounds());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000711 gfx::Rect status_size(
712 shelf_->status_area_widget()->GetWindowBoundsInScreen().size());
713 int shelf_width = 0, shelf_height = 0;
714 GetShelfSize(&shelf_width, &shelf_height);
715 if (IsHorizontalAlignment())
716 shelf_width = available_bounds.width();
717 else
718 shelf_height = available_bounds.height();
719
720 if (state.visibility_state == SHELF_AUTO_HIDE &&
721 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100722 // Auto-hidden shelf always starts with the default size. If a gesture-drag
723 // is in progress, then the call to UpdateTargetBoundsForGesture() below
724 // takes care of setting the height properly.
725 if (IsHorizontalAlignment())
726 shelf_height = kAutoHideSize;
727 else
728 shelf_width = kAutoHideSize;
729 } else if (state.visibility_state == SHELF_HIDDEN ||
730 !keyboard_bounds_.IsEmpty()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000731 if (IsHorizontalAlignment())
732 shelf_height = 0;
733 else
734 shelf_width = 0;
735 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100736
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000737 target_bounds->shelf_bounds_in_root = SelectValueForShelfAlignment(
738 gfx::Rect(available_bounds.x(), available_bounds.bottom() - shelf_height,
739 available_bounds.width(), shelf_height),
740 gfx::Rect(available_bounds.x(), available_bounds.y(),
741 shelf_width, available_bounds.height()),
742 gfx::Rect(available_bounds.right() - shelf_width, available_bounds.y(),
743 shelf_width, available_bounds.height()),
744 gfx::Rect(available_bounds.x(), available_bounds.y(),
745 available_bounds.width(), shelf_height));
746
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100747 int status_inset = std::max(0, GetPreferredShelfSize() -
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000748 PrimaryAxisValue(status_size.height(), status_size.width()));
749
750 target_bounds->status_bounds_in_shelf = SelectValueForShelfAlignment(
751 gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
752 status_inset, status_size.width(), status_size.height()),
753 gfx::Rect(shelf_width - (status_size.width() + status_inset),
754 shelf_height - status_size.height(), status_size.width(),
755 status_size.height()),
756 gfx::Rect(status_inset, shelf_height - status_size.height(),
757 status_size.width(), status_size.height()),
758 gfx::Rect(base::i18n::IsRTL() ? 0 : shelf_width - status_size.width(),
759 shelf_height - (status_size.height() + status_inset),
760 status_size.width(), status_size.height()));
761
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000762 target_bounds->work_area_insets = SelectValueForShelfAlignment(
763 gfx::Insets(0, 0, GetWorkAreaSize(state, shelf_height), 0),
764 gfx::Insets(0, GetWorkAreaSize(state, shelf_width), 0, 0),
765 gfx::Insets(0, 0, 0, GetWorkAreaSize(state, shelf_width)),
766 gfx::Insets(GetWorkAreaSize(state, shelf_height), 0, 0, 0));
767
Ben Murdochca12bfa2013-07-23 11:17:05 +0100768 // TODO(varkha): The functionality of managing insets for display areas
769 // should probably be pushed to a separate component. This would simplify or
770 // remove entirely the dependency on keyboard and dock.
771
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100772 // Also push in the work area inset for the keyboard if it is visible.
773 if (!keyboard_bounds_.IsEmpty()) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100774 gfx::Insets keyboard_insets(0, 0, keyboard_bounds_.height(), 0);
775 target_bounds->work_area_insets += keyboard_insets;
776 }
777
778 // Also push in the work area inset for the dock if it is visible.
779 if (!dock_bounds_.IsEmpty()) {
780 gfx::Insets dock_insets(
781 0, (dock_bounds_.x() > 0 ? 0 : dock_bounds_.width()),
782 0, (dock_bounds_.x() > 0 ? dock_bounds_.width() : 0));
783 target_bounds->work_area_insets += dock_insets;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100784 }
785
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000786 target_bounds->opacity =
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100787 (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000788 state.visibility_state == SHELF_VISIBLE ||
789 state.visibility_state == SHELF_AUTO_HIDE) ? 1.0f : 0.0f;
Ben Murdocheb525c52013-07-10 11:40:50 +0100790 target_bounds->status_opacity =
791 (state.visibility_state == SHELF_AUTO_HIDE &&
792 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) ?
793 0.0f : target_bounds->opacity;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100794
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000795 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS)
796 UpdateTargetBoundsForGesture(target_bounds);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100797
798 // This needs to happen after calling UpdateTargetBoundsForGesture(), because
799 // that can change the size of the shelf.
800 target_bounds->launcher_bounds_in_shelf = SelectValueForShelfAlignment(
801 gfx::Rect(base::i18n::IsRTL() ? status_size.width() : 0, 0,
802 shelf_width - status_size.width(),
803 target_bounds->shelf_bounds_in_root.height()),
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100804 gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
805 shelf_height - status_size.height()),
806 gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
807 shelf_height - status_size.height()),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100808 gfx::Rect(base::i18n::IsRTL() ? status_size.width() : 0, 0,
809 shelf_width - status_size.width(),
810 target_bounds->shelf_bounds_in_root.height()));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000811}
812
813void ShelfLayoutManager::UpdateTargetBoundsForGesture(
814 TargetBounds* target_bounds) const {
815 CHECK_EQ(GESTURE_DRAG_IN_PROGRESS, gesture_drag_status_);
816 bool horizontal = IsHorizontalAlignment();
817 const gfx::Rect& available_bounds(root_window_->bounds());
818 int resistance_free_region = 0;
819
820 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
821 visibility_state() == SHELF_AUTO_HIDE &&
822 auto_hide_state() != SHELF_AUTO_HIDE_SHOWN) {
823 // If the shelf was hidden when the drag started (and the state hasn't
824 // changed since then, e.g. because the tray-menu was shown because of the
825 // drag), then allow the drag some resistance-free region at first to make
826 // sure the shelf sticks with the finger until the shelf is visible.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100827 resistance_free_region = GetPreferredShelfSize() - kAutoHideSize;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000828 }
829
830 bool resist = SelectValueForShelfAlignment(
831 gesture_drag_amount_ < -resistance_free_region,
832 gesture_drag_amount_ > resistance_free_region,
833 gesture_drag_amount_ < -resistance_free_region,
834 gesture_drag_amount_ > resistance_free_region);
835
836 float translate = 0.f;
837 if (resist) {
838 float diff = fabsf(gesture_drag_amount_) - resistance_free_region;
839 diff = std::min(diff, sqrtf(diff));
840 if (gesture_drag_amount_ < 0)
841 translate = -resistance_free_region - diff;
842 else
843 translate = resistance_free_region + diff;
844 } else {
845 translate = gesture_drag_amount_;
846 }
847
848 if (horizontal) {
849 // Move and size the launcher with the gesture.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100850 int shelf_height = target_bounds->shelf_bounds_in_root.height() - translate;
851 shelf_height = std::max(shelf_height, kAutoHideSize);
852 target_bounds->shelf_bounds_in_root.set_height(shelf_height);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000853 if (alignment_ == SHELF_ALIGNMENT_BOTTOM) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100854 target_bounds->shelf_bounds_in_root.set_y(
855 available_bounds.bottom() - shelf_height);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000856 }
857
858 // The statusbar should be in the center of the shelf.
859 gfx::Rect status_y = target_bounds->shelf_bounds_in_root;
860 status_y.set_y(0);
861 status_y.ClampToCenteredSize(
862 target_bounds->status_bounds_in_shelf.size());
863 target_bounds->status_bounds_in_shelf.set_y(status_y.y());
864 } else {
865 // Move and size the launcher with the gesture.
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100866 int shelf_width = target_bounds->shelf_bounds_in_root.width();
867 if (alignment_ == SHELF_ALIGNMENT_RIGHT)
868 shelf_width -= translate;
869 else
870 shelf_width += translate;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100871 shelf_width = std::max(shelf_width, kAutoHideSize);
872 target_bounds->shelf_bounds_in_root.set_width(shelf_width);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000873 if (alignment_ == SHELF_ALIGNMENT_RIGHT) {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100874 target_bounds->shelf_bounds_in_root.set_x(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100875 available_bounds.right() - shelf_width);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000876 }
877
878 // The statusbar should be in the center of the shelf.
879 gfx::Rect status_x = target_bounds->shelf_bounds_in_root;
880 status_x.set_x(0);
881 status_x.ClampToCenteredSize(
882 target_bounds->status_bounds_in_shelf.size());
883 target_bounds->status_bounds_in_shelf.set_x(status_x.x());
884 }
885}
886
887void ShelfLayoutManager::UpdateShelfBackground(
888 BackgroundAnimator::ChangeType type) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100889 shelf_->SetPaintsBackground(GetShelfBackgroundType(), type);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000890}
891
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100892ShelfBackgroundType ShelfLayoutManager::GetShelfBackgroundType() const {
893 if (state_.visibility_state != SHELF_AUTO_HIDE &&
894 state_.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED) {
895 return SHELF_BACKGROUND_MAXIMIZED;
896 }
897
898 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000899 (!state_.is_screen_locked && window_overlaps_shelf_) ||
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100900 (state_.visibility_state == SHELF_AUTO_HIDE)) {
901 return SHELF_BACKGROUND_OVERLAP;
902 }
903
904 return SHELF_BACKGROUND_DEFAULT;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000905}
906
907void ShelfLayoutManager::UpdateAutoHideStateNow() {
908 SetState(state_.visibility_state);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100909
910 // If the state did not change, the auto hide timer may still be running.
911 StopAutoHideTimer();
912}
913
914void ShelfLayoutManager::StopAutoHideTimer() {
915 auto_hide_timer_.Stop();
916 mouse_over_shelf_when_auto_hide_timer_started_ = false;
917}
918
919gfx::Rect ShelfLayoutManager::GetAutoHideShowShelfRegionInScreen() const {
920 gfx::Rect shelf_bounds_in_screen = shelf_->GetWindowBoundsInScreen();
921 gfx::Vector2d offset = SelectValueForShelfAlignment(
922 gfx::Vector2d(0, shelf_bounds_in_screen.height()),
923 gfx::Vector2d(-kMaxAutoHideShowShelfRegionSize, 0),
924 gfx::Vector2d(shelf_bounds_in_screen.width(), 0),
925 gfx::Vector2d(0, -kMaxAutoHideShowShelfRegionSize));
926
927 gfx::Rect show_shelf_region_in_screen = shelf_bounds_in_screen;
928 show_shelf_region_in_screen += offset;
929 if (IsHorizontalAlignment())
930 show_shelf_region_in_screen.set_height(kMaxAutoHideShowShelfRegionSize);
931 else
932 show_shelf_region_in_screen.set_width(kMaxAutoHideShowShelfRegionSize);
933
934 // TODO: Figure out if we need any special handling when the keyboard is
935 // visible.
936 return show_shelf_region_in_screen;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000937}
938
939ShelfAutoHideState ShelfLayoutManager::CalculateAutoHideState(
940 ShelfVisibilityState visibility_state) const {
941 if (visibility_state != SHELF_AUTO_HIDE || !shelf_)
942 return SHELF_AUTO_HIDE_HIDDEN;
943
944 if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS)
945 return gesture_drag_auto_hide_state_;
946
947 Shell* shell = Shell::GetInstance();
948 if (shell->GetAppListTargetVisibility())
949 return SHELF_AUTO_HIDE_SHOWN;
950
951 if (shelf_->status_area_widget() &&
952 shelf_->status_area_widget()->ShouldShowLauncher())
953 return SHELF_AUTO_HIDE_SHOWN;
954
955 if (shelf_->launcher() && shelf_->launcher()->IsShowingMenu())
956 return SHELF_AUTO_HIDE_SHOWN;
957
958 if (shelf_->launcher() && shelf_->launcher()->IsShowingOverflowBubble())
959 return SHELF_AUTO_HIDE_SHOWN;
960
961 if (shelf_->IsActive() || shelf_->status_area_widget()->IsActive())
962 return SHELF_AUTO_HIDE_SHOWN;
963
964 // Don't show if the user is dragging the mouse.
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100965 if (auto_hide_event_filter_.get() && auto_hide_event_filter_->in_mouse_drag())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000966 return SHELF_AUTO_HIDE_HIDDEN;
967
968 gfx::Rect shelf_region = shelf_->GetWindowBoundsInScreen();
969 if (shelf_->status_area_widget() &&
970 shelf_->status_area_widget()->IsMessageBubbleShown() &&
971 IsVisible()) {
972 // Increase the the hit test area to prevent the shelf from disappearing
973 // when the mouse is over the bubble gap.
974 shelf_region.Inset(alignment_ == SHELF_ALIGNMENT_RIGHT ?
975 -kNotificationBubbleGapHeight : 0,
976 alignment_ == SHELF_ALIGNMENT_BOTTOM ?
977 -kNotificationBubbleGapHeight : 0,
978 alignment_ == SHELF_ALIGNMENT_LEFT ?
979 -kNotificationBubbleGapHeight : 0,
980 alignment_ == SHELF_ALIGNMENT_TOP ?
981 -kNotificationBubbleGapHeight : 0);
982 }
983
Ben Murdochbb1529c2013-08-08 10:24:53 +0100984 gfx::Point cursor_position_in_screen =
985 Shell::GetScreen()->GetCursorScreenPoint();
986 if (shelf_region.Contains(cursor_position_in_screen))
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000987 return SHELF_AUTO_HIDE_SHOWN;
988
Ben Murdochbb1529c2013-08-08 10:24:53 +0100989 // When the shelf is auto hidden and the shelf is on the boundary between two
990 // displays, it is hard to trigger showing the shelf. For instance, if a
991 // user's primary display is left of their secondary display, it is hard to
992 // unautohide a left aligned shelf on the secondary display.
993 // It is hard because:
994 // - It is hard to stop the cursor in the shelf "light bar" and not overshoot.
995 // - The cursor is warped to the other display if the cursor gets to the edge
996 // of the display.
997 // Show the shelf if the cursor started on the shelf and the user overshot the
998 // shelf slightly to make it easier to show the shelf in this situation. We
999 // do not check |auto_hide_timer_|.IsRunning() because it returns false when
1000 // the timer's task is running.
1001 if ((state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN ||
1002 mouse_over_shelf_when_auto_hide_timer_started_) &&
1003 GetAutoHideShowShelfRegionInScreen().Contains(
1004 cursor_position_in_screen)) {
1005 return SHELF_AUTO_HIDE_SHOWN;
1006 }
1007
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001008 const std::vector<aura::Window*> windows =
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +01001009 ash::MruWindowTracker::BuildWindowList(false);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001010
1011 // Process the window list and check if there are any visible windows.
1012 for (size_t i = 0; i < windows.size(); ++i) {
1013 if (windows[i] && windows[i]->IsVisible() &&
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001014 !ash::wm::IsWindowMinimized(windows[i]) &&
1015 root_window_ == windows[i]->GetRootWindow())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001016 return SHELF_AUTO_HIDE_HIDDEN;
1017 }
1018
1019 // If there are no visible windows do not hide the shelf.
1020 return SHELF_AUTO_HIDE_SHOWN;
1021}
1022
1023void ShelfLayoutManager::UpdateHitTestBounds() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001024 gfx::Insets mouse_insets;
1025 gfx::Insets touch_insets;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001026 if (state_.visibility_state == SHELF_VISIBLE) {
1027 // Let clicks at the very top of the launcher through so windows can be
1028 // resized with the bottom-right corner and bottom edge.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001029 mouse_insets = GetInsetsForAlignment(kWorkspaceAreaVisibleInset);
1030 } else if (state_.visibility_state == SHELF_AUTO_HIDE) {
1031 // Extend the touch hit target out a bit to allow users to drag shelf out
1032 // while hidden.
1033 touch_insets = GetInsetsForAlignment(-kWorkspaceAreaAutoHideInset);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001034 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001035
1036 if (shelf_ && shelf_->GetNativeWindow())
1037 shelf_->GetNativeWindow()->SetHitTestBoundsOverrideOuter(mouse_insets,
1038 touch_insets);
1039 shelf_->status_area_widget()->GetNativeWindow()->
1040 SetHitTestBoundsOverrideOuter(mouse_insets, touch_insets);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001041}
1042
1043bool ShelfLayoutManager::IsShelfWindow(aura::Window* window) {
1044 if (!window)
1045 return false;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001046 return (shelf_ && shelf_->GetNativeWindow()->Contains(window)) ||
1047 (shelf_->status_area_widget() &&
1048 shelf_->status_area_widget()->GetNativeWindow()->Contains(window));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001049}
1050
1051int ShelfLayoutManager::GetWorkAreaSize(const State& state, int size) const {
1052 if (state.visibility_state == SHELF_VISIBLE)
1053 return size;
1054 if (state.visibility_state == SHELF_AUTO_HIDE)
1055 return kAutoHideSize;
1056 return 0;
1057}
1058
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001059gfx::Rect ShelfLayoutManager::GetAvailableBounds() const {
1060 gfx::Rect bounds(root_window_->bounds());
1061 bounds.set_height(bounds.height() - keyboard_bounds_.height());
1062 return bounds;
1063}
1064
1065void ShelfLayoutManager::OnKeyboardBoundsChanging(
1066 const gfx::Rect& keyboard_bounds) {
1067 keyboard_bounds_ = keyboard_bounds;
1068 OnWindowResized();
1069}
1070
Ben Murdochca12bfa2013-07-23 11:17:05 +01001071void ShelfLayoutManager::OnDockBoundsChanging(
1072 const gfx::Rect& dock_bounds) {
1073 if (dock_bounds_ != dock_bounds) {
1074 dock_bounds_ = dock_bounds;
1075 OnWindowResized();
1076 }
1077}
1078
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001079gfx::Insets ShelfLayoutManager::GetInsetsForAlignment(int distance) const {
1080 switch (alignment_) {
1081 case SHELF_ALIGNMENT_BOTTOM:
1082 return gfx::Insets(distance, 0, 0, 0);
1083 case SHELF_ALIGNMENT_LEFT:
1084 return gfx::Insets(0, 0, 0, distance);
1085 case SHELF_ALIGNMENT_RIGHT:
1086 return gfx::Insets(0, distance, 0, 0);
1087 case SHELF_ALIGNMENT_TOP:
1088 return gfx::Insets(0, 0, distance, 0);
1089 }
1090 NOTREACHED();
1091 return gfx::Insets();
1092}
1093
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001094} // namespace internal
1095} // namespace ash