Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(170)

Side by Side Diff: ash/common/shelf/shelf_layout_manager.cc

Issue 2734653002: chromeos: Move files in //ash/common to //ash (Closed)
Patch Set: fix a11y tests, fix docs Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/common/shelf/shelf_layout_manager.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <vector>
10
11 #include "ash/animation/animation_change_type.h"
12 #include "ash/common/session/session_controller.h"
13 #include "ash/common/session/session_state_delegate.h"
14 #include "ash/common/shelf/shelf_constants.h"
15 #include "ash/common/shelf/shelf_layout_manager_observer.h"
16 #include "ash/common/shelf/shelf_widget.h"
17 #include "ash/common/shelf/wm_shelf.h"
18 #include "ash/common/system/status_area_widget.h"
19 #include "ash/common/wm/fullscreen_window_finder.h"
20 #include "ash/common/wm/mru_window_tracker.h"
21 #include "ash/common/wm/window_state.h"
22 #include "ash/common/wm/wm_screen_util.h"
23 #include "ash/common/wm_shell.h"
24 #include "ash/common/wm_window.h"
25 #include "ash/public/cpp/shell_window_ids.h"
26 #include "ash/root_window_controller.h"
27 #include "base/auto_reset.h"
28 #include "base/command_line.h"
29 #include "base/i18n/rtl.h"
30 #include "ui/base/ui_base_switches.h"
31 #include "ui/compositor/layer.h"
32 #include "ui/compositor/layer_animation_observer.h"
33 #include "ui/compositor/layer_animator.h"
34 #include "ui/compositor/scoped_layer_animation_settings.h"
35 #include "ui/display/display.h"
36 #include "ui/display/screen.h"
37 #include "ui/events/event.h"
38 #include "ui/events/event_handler.h"
39 #include "ui/keyboard/keyboard_controller.h"
40 #include "ui/keyboard/keyboard_util.h"
41 #include "ui/views/border.h"
42 #include "ui/views/widget/widget.h"
43
44 namespace ash {
45 namespace {
46
47 // Delay before showing the shelf. This is after the mouse stops moving.
48 const int kAutoHideDelayMS = 200;
49
50 // Duration of the animation to show or hide the shelf.
51 const int kAnimationDurationMS = 200;
52
53 // To avoid hiding the shelf when the mouse transitions from a message bubble
54 // into the shelf, the hit test area is enlarged by this amount of pixels to
55 // keep the shelf from hiding.
56 const int kNotificationBubbleGapHeight = 6;
57
58 // The maximum size of the region on the display opposing the shelf managed by
59 // this ShelfLayoutManager which can trigger showing the shelf.
60 // For instance:
61 // - Primary display is left of secondary display.
62 // - Shelf is left aligned
63 // - This ShelfLayoutManager manages the shelf for the secondary display.
64 // |kMaxAutoHideShowShelfRegionSize| refers to the maximum size of the region
65 // from the right edge of the primary display which can trigger showing the
66 // auto hidden shelf. The region is used to make it easier to trigger showing
67 // the auto hidden shelf when the shelf is on the boundary between displays.
68 const int kMaxAutoHideShowShelfRegionSize = 10;
69
70 ui::Layer* GetLayer(views::Widget* widget) {
71 return widget->GetNativeView()->layer();
72 }
73
74 // Returns true if the window is in the app list window container.
75 bool IsAppListWindow(WmWindow* window) {
76 return window->GetParent() &&
77 window->GetParent()->GetShellWindowId() ==
78 kShellWindowId_AppListContainer;
79 }
80
81 } // namespace
82
83 // ShelfLayoutManager::UpdateShelfObserver -------------------------------------
84
85 // UpdateShelfObserver is used to delay updating the background until the
86 // animation completes.
87 class ShelfLayoutManager::UpdateShelfObserver
88 : public ui::ImplicitAnimationObserver {
89 public:
90 explicit UpdateShelfObserver(ShelfLayoutManager* shelf) : shelf_(shelf) {
91 shelf_->update_shelf_observer_ = this;
92 }
93
94 void Detach() { shelf_ = NULL; }
95
96 void OnImplicitAnimationsCompleted() override {
97 if (shelf_)
98 shelf_->MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
99 delete this;
100 }
101
102 private:
103 ~UpdateShelfObserver() override {
104 if (shelf_)
105 shelf_->update_shelf_observer_ = NULL;
106 }
107
108 // Shelf we're in. NULL if deleted before we're deleted.
109 ShelfLayoutManager* shelf_;
110
111 DISALLOW_COPY_AND_ASSIGN(UpdateShelfObserver);
112 };
113
114 ShelfLayoutManager::State::State()
115 : visibility_state(SHELF_VISIBLE),
116 auto_hide_state(SHELF_AUTO_HIDE_HIDDEN),
117 window_state(wm::WORKSPACE_WINDOW_STATE_DEFAULT),
118 pre_lock_screen_animation_active(false),
119 session_state(session_manager::SessionState::UNKNOWN) {}
120
121 bool ShelfLayoutManager::State::IsAddingSecondaryUser() const {
122 return session_state == session_manager::SessionState::LOGIN_SECONDARY;
123 }
124
125 bool ShelfLayoutManager::State::IsScreenLocked() const {
126 return session_state == session_manager::SessionState::LOCKED;
127 }
128
129 bool ShelfLayoutManager::State::Equals(const State& other) const {
130 return other.visibility_state == visibility_state &&
131 (visibility_state != SHELF_AUTO_HIDE ||
132 other.auto_hide_state == auto_hide_state) &&
133 other.window_state == window_state &&
134 other.pre_lock_screen_animation_active ==
135 pre_lock_screen_animation_active &&
136 other.session_state == session_state;
137 }
138
139 // ShelfLayoutManager ----------------------------------------------------------
140
141 ShelfLayoutManager::ShelfLayoutManager(ShelfWidget* shelf_widget,
142 WmShelf* wm_shelf)
143 : updating_bounds_(false),
144 shelf_widget_(shelf_widget),
145 wm_shelf_(wm_shelf),
146 window_overlaps_shelf_(false),
147 mouse_over_shelf_when_auto_hide_timer_started_(false),
148 gesture_drag_status_(GESTURE_DRAG_NONE),
149 gesture_drag_amount_(0.f),
150 gesture_drag_auto_hide_state_(SHELF_AUTO_HIDE_SHOWN),
151 update_shelf_observer_(NULL),
152 chromevox_panel_height_(0),
153 duration_override_in_ms_(0),
154 shelf_background_type_(SHELF_BACKGROUND_OVERLAP) {
155 DCHECK(shelf_widget_);
156 DCHECK(wm_shelf_);
157 WmShell::Get()->AddShellObserver(this);
158 WmShell::Get()->AddLockStateObserver(this);
159 WmShell::Get()->AddActivationObserver(this);
160 WmShell::Get()->session_controller()->AddSessionStateObserver(this);
161 state_.session_state =
162 WmShell::Get()->session_controller()->GetSessionState();
163 }
164
165 ShelfLayoutManager::~ShelfLayoutManager() {
166 if (update_shelf_observer_)
167 update_shelf_observer_->Detach();
168
169 for (auto& observer : observers_)
170 observer.WillDeleteShelfLayoutManager();
171 WmShell::Get()->RemoveShellObserver(this);
172 WmShell::Get()->RemoveLockStateObserver(this);
173 WmShell::Get()->session_controller()->RemoveSessionStateObserver(this);
174 }
175
176 void ShelfLayoutManager::PrepareForShutdown() {
177 in_shutdown_ = true;
178 // Stop observing changes to avoid updating a partially destructed shelf.
179 WmShell::Get()->RemoveActivationObserver(this);
180 }
181
182 bool ShelfLayoutManager::IsVisible() const {
183 // status_area_widget() may be NULL during the shutdown.
184 return shelf_widget_->status_area_widget() &&
185 shelf_widget_->status_area_widget()->IsVisible() &&
186 (state_.visibility_state == SHELF_VISIBLE ||
187 (state_.visibility_state == SHELF_AUTO_HIDE &&
188 state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN));
189 }
190
191 gfx::Rect ShelfLayoutManager::GetIdealBounds() {
192 const int shelf_size = GetShelfConstant(SHELF_SIZE);
193 WmWindow* shelf_window = WmWindow::Get(shelf_widget_->GetNativeWindow());
194 gfx::Rect rect(wm::GetDisplayBoundsInParent(shelf_window));
195 return SelectValueForShelfAlignment(
196 gfx::Rect(rect.x(), rect.bottom() - shelf_size, rect.width(), shelf_size),
197 gfx::Rect(rect.x(), rect.y(), shelf_size, rect.height()),
198 gfx::Rect(rect.right() - shelf_size, rect.y(), shelf_size,
199 rect.height()));
200 }
201
202 gfx::Size ShelfLayoutManager::GetPreferredSize() {
203 TargetBounds target_bounds;
204 CalculateTargetBounds(state_, &target_bounds);
205 return target_bounds.shelf_bounds_in_root.size();
206 }
207
208 void ShelfLayoutManager::LayoutShelfAndUpdateBounds(bool change_work_area) {
209 TargetBounds target_bounds;
210 CalculateTargetBounds(state_, &target_bounds);
211 UpdateBoundsAndOpacity(target_bounds, false, change_work_area, NULL);
212
213 // Update insets in ShelfWindowTargeter when shelf bounds change.
214 for (auto& observer : observers_)
215 observer.WillChangeVisibilityState(visibility_state());
216 }
217
218 void ShelfLayoutManager::LayoutShelf() {
219 LayoutShelfAndUpdateBounds(true);
220 }
221
222 ShelfVisibilityState ShelfLayoutManager::CalculateShelfVisibility() {
223 switch (wm_shelf_->auto_hide_behavior()) {
224 case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
225 return SHELF_AUTO_HIDE;
226 case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
227 return SHELF_VISIBLE;
228 case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
229 return SHELF_HIDDEN;
230 }
231 return SHELF_VISIBLE;
232 }
233
234 void ShelfLayoutManager::UpdateVisibilityState() {
235 // Bail out early before the shelf is initialized or after it is destroyed.
236 WmWindow* shelf_window = WmWindow::Get(shelf_widget_->GetNativeWindow());
237 if (in_shutdown_ || !wm_shelf_->IsShelfInitialized() || !shelf_window)
238 return;
239 if (state_.IsScreenLocked() || state_.IsAddingSecondaryUser()) {
240 SetState(SHELF_VISIBLE);
241 } else if (WmShell::Get()->IsPinned()) {
242 SetState(SHELF_HIDDEN);
243 } else {
244 // TODO(zelidrag): Verify shelf drag animation still shows on the device
245 // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN.
246 wm::WorkspaceWindowState window_state(
247 shelf_window->GetRootWindowController()->GetWorkspaceWindowState());
248 switch (window_state) {
249 case wm::WORKSPACE_WINDOW_STATE_FULL_SCREEN: {
250 if (IsShelfHiddenForFullscreen()) {
251 SetState(SHELF_HIDDEN);
252 } else {
253 // The shelf is sometimes not hidden when in immersive fullscreen.
254 // Force the shelf to be auto hidden in this case.
255 SetState(SHELF_AUTO_HIDE);
256 }
257 break;
258 }
259 case wm::WORKSPACE_WINDOW_STATE_MAXIMIZED:
260 SetState(CalculateShelfVisibility());
261 break;
262
263 case wm::WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF:
264 case wm::WORKSPACE_WINDOW_STATE_DEFAULT:
265 SetState(CalculateShelfVisibility());
266 SetWindowOverlapsShelf(
267 window_state == wm::WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF);
268 break;
269 }
270 }
271 }
272
273 void ShelfLayoutManager::UpdateAutoHideState() {
274 ShelfAutoHideState auto_hide_state =
275 CalculateAutoHideState(state_.visibility_state);
276 if (auto_hide_state != state_.auto_hide_state) {
277 if (auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
278 // Hides happen immediately.
279 SetState(state_.visibility_state);
280 } else {
281 if (!auto_hide_timer_.IsRunning()) {
282 mouse_over_shelf_when_auto_hide_timer_started_ =
283 shelf_widget_->GetWindowBoundsInScreen().Contains(
284 display::Screen::GetScreen()->GetCursorScreenPoint());
285 }
286 auto_hide_timer_.Start(
287 FROM_HERE, base::TimeDelta::FromMilliseconds(kAutoHideDelayMS), this,
288 &ShelfLayoutManager::UpdateAutoHideStateNow);
289 }
290 } else {
291 StopAutoHideTimer();
292 }
293 }
294
295 void ShelfLayoutManager::UpdateAutoHideForMouseEvent(ui::MouseEvent* event,
296 WmWindow* target) {
297 // This also checks IsShelfWindow() to make sure we don't attempt to hide the
298 // shelf if the mouse down occurs on the shelf.
299 in_mouse_drag_ = (event->type() == ui::ET_MOUSE_DRAGGED ||
300 (in_mouse_drag_ && event->type() != ui::ET_MOUSE_RELEASED &&
301 event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)) &&
302 !IsShelfWindow(target);
303
304 // Don't update during shutdown because synthetic mouse events (e.g. mouse
305 // exit) may be generated during status area widget teardown.
306 if (visibility_state() != SHELF_AUTO_HIDE || in_shutdown_)
307 return;
308
309 if (event->type() == ui::ET_MOUSE_MOVED ||
310 event->type() == ui::ET_MOUSE_ENTERED ||
311 event->type() == ui::ET_MOUSE_EXITED) {
312 UpdateAutoHideState();
313 }
314 }
315
316 void ShelfLayoutManager::UpdateAutoHideForGestureEvent(ui::GestureEvent* event,
317 WmWindow* target) {
318 if (visibility_state() != SHELF_AUTO_HIDE || in_shutdown_)
319 return;
320
321 if (IsShelfWindow(target) && ProcessGestureEvent(*event))
322 event->StopPropagation();
323 }
324
325 void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) {
326 window_overlaps_shelf_ = value;
327 MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
328 }
329
330 void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) {
331 observers_.AddObserver(observer);
332 }
333
334 void ShelfLayoutManager::RemoveObserver(ShelfLayoutManagerObserver* observer) {
335 observers_.RemoveObserver(observer);
336 }
337
338 bool ShelfLayoutManager::ProcessGestureEvent(const ui::GestureEvent& event) {
339 // The gestures are disabled in the lock/login screen.
340 SessionStateDelegate* delegate = WmShell::Get()->GetSessionStateDelegate();
341 if (!delegate->NumberOfLoggedInUsers() || delegate->IsScreenLocked())
342 return false;
343
344 if (IsShelfHiddenForFullscreen())
345 return false;
346
347 if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN) {
348 StartGestureDrag(event);
349 return true;
350 }
351
352 if (gesture_drag_status_ != GESTURE_DRAG_IN_PROGRESS)
353 return false;
354
355 if (event.type() == ui::ET_GESTURE_SCROLL_UPDATE) {
356 UpdateGestureDrag(event);
357 return true;
358 }
359
360 if (event.type() == ui::ET_GESTURE_SCROLL_END ||
361 event.type() == ui::ET_SCROLL_FLING_START) {
362 CompleteGestureDrag(event);
363 return true;
364 }
365
366 // Unexpected event. Reset the state and let the event fall through.
367 CancelGestureDrag();
368 return false;
369 }
370
371 void ShelfLayoutManager::SetAnimationDurationOverride(
372 int duration_override_in_ms) {
373 duration_override_in_ms_ = duration_override_in_ms;
374 }
375
376 ////////////////////////////////////////////////////////////////////////////////
377 // ShelfLayoutManager, wm::WmSnapToPixelLayoutManager implementation:
378
379 void ShelfLayoutManager::OnWindowResized() {
380 LayoutShelf();
381 }
382
383 void ShelfLayoutManager::SetChildBounds(WmWindow* child,
384 const gfx::Rect& requested_bounds) {
385 wm::WmSnapToPixelLayoutManager::SetChildBounds(child, requested_bounds);
386 // We may contain other widgets (such as frame maximize bubble) but they don't
387 // effect the layout in anyway.
388 if (!updating_bounds_ &&
389 ((WmWindow::Get(shelf_widget_->GetNativeWindow()) == child) ||
390 (WmWindow::Get(shelf_widget_->status_area_widget()->GetNativeWindow()) ==
391 child))) {
392 LayoutShelf();
393 }
394 }
395
396 void ShelfLayoutManager::OnShelfAutoHideBehaviorChanged(WmWindow* root_window) {
397 UpdateVisibilityState();
398 }
399
400 void ShelfLayoutManager::OnPinnedStateChanged(WmWindow* pinned_window) {
401 // Shelf needs to be hidden on entering to pinned mode, or restored
402 // on exiting from pinned mode.
403 UpdateVisibilityState();
404 }
405
406 void ShelfLayoutManager::OnWindowActivated(WmWindow* gained_active,
407 WmWindow* lost_active) {
408 UpdateAutoHideStateNow();
409 }
410
411 void ShelfLayoutManager::OnKeyboardBoundsChanging(const gfx::Rect& new_bounds) {
412 bool keyboard_is_about_to_hide = false;
413 if (new_bounds.IsEmpty() && !keyboard_bounds_.IsEmpty())
414 keyboard_is_about_to_hide = true;
415 // If new window behavior flag enabled and in non-sticky mode, do not change
416 // the work area.
417 bool change_work_area =
418 (!base::CommandLine::ForCurrentProcess()->HasSwitch(
419 ::switches::kUseNewVirtualKeyboardBehavior) ||
420 keyboard::KeyboardController::GetInstance()->keyboard_locked());
421
422 keyboard_bounds_ = new_bounds;
423 LayoutShelfAndUpdateBounds(change_work_area);
424
425 // On login screen if keyboard has been just hidden, update bounds just once
426 // but ignore target_bounds.work_area_insets since shelf overlaps with login
427 // window.
428 if (WmShell::Get()->GetSessionStateDelegate()->IsUserSessionBlocked() &&
429 keyboard_is_about_to_hide) {
430 WmWindow* window = WmWindow::Get(shelf_widget_->GetNativeWindow());
431 WmShell::Get()->SetDisplayWorkAreaInsets(window, gfx::Insets());
432 }
433 }
434
435 void ShelfLayoutManager::OnKeyboardClosed() {}
436
437 ShelfBackgroundType ShelfLayoutManager::GetShelfBackgroundType() const {
438 if (state_.pre_lock_screen_animation_active)
439 return SHELF_BACKGROUND_DEFAULT;
440
441 // Handle all non active screen states, including OOBE and pre-login.
442 if (state_.session_state != session_manager::SessionState::ACTIVE)
443 return SHELF_BACKGROUND_OVERLAP;
444
445 if (state_.visibility_state != SHELF_AUTO_HIDE &&
446 state_.window_state == wm::WORKSPACE_WINDOW_STATE_MAXIMIZED) {
447 return SHELF_BACKGROUND_MAXIMIZED;
448 }
449
450 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
451 window_overlaps_shelf_ || state_.visibility_state == SHELF_AUTO_HIDE) {
452 return SHELF_BACKGROUND_OVERLAP;
453 }
454
455 return SHELF_BACKGROUND_DEFAULT;
456 }
457
458 void ShelfLayoutManager::SetChromeVoxPanelHeight(int height) {
459 chromevox_panel_height_ = height;
460 LayoutShelf();
461 }
462
463 ////////////////////////////////////////////////////////////////////////////////
464 // ShelfLayoutManager, private:
465
466 ShelfLayoutManager::TargetBounds::TargetBounds()
467 : opacity(0.0f), status_opacity(0.0f) {}
468
469 ShelfLayoutManager::TargetBounds::~TargetBounds() {}
470
471 void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
472 State state;
473 state.visibility_state = visibility_state;
474 state.auto_hide_state = CalculateAutoHideState(visibility_state);
475
476 WmWindow* shelf_window = WmWindow::Get(shelf_widget_->GetNativeWindow());
477 RootWindowController* controller = shelf_window->GetRootWindowController();
478 state.window_state = controller ? controller->GetWorkspaceWindowState()
479 : wm::WORKSPACE_WINDOW_STATE_DEFAULT;
480 // Preserve the log in screen states.
481 state.session_state = state_.session_state;
482 state.pre_lock_screen_animation_active =
483 state_.pre_lock_screen_animation_active;
484
485 // Force an update because gesture drags affect the shelf bounds and we
486 // should animate back to the normal bounds at the end of a gesture.
487 bool force_update =
488 (gesture_drag_status_ == GESTURE_DRAG_CANCEL_IN_PROGRESS ||
489 gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS);
490
491 if (!force_update && state_.Equals(state))
492 return; // Nothing changed.
493
494 for (auto& observer : observers_)
495 observer.WillChangeVisibilityState(visibility_state);
496
497 StopAutoHideTimer();
498
499 State old_state = state_;
500 state_ = state;
501
502 AnimationChangeType change_type = AnimationChangeType::ANIMATE;
503 bool delay_background_change = false;
504
505 // Do not animate the background when:
506 // - Going from a hidden / auto hidden shelf in fullscreen to a visible shelf
507 // in maximized mode.
508 // - Going from an auto hidden shelf in maximized mode to a visible shelf in
509 // maximized mode.
510 if (state.visibility_state == SHELF_VISIBLE &&
511 state.window_state == wm::WORKSPACE_WINDOW_STATE_MAXIMIZED &&
512 old_state.visibility_state != SHELF_VISIBLE) {
513 change_type = AnimationChangeType::IMMEDIATE;
514 } else {
515 // Delay the animation when the shelf was hidden, and has just been made
516 // visible (e.g. using a gesture-drag).
517 if (state.visibility_state == SHELF_VISIBLE &&
518 old_state.visibility_state == SHELF_AUTO_HIDE &&
519 old_state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
520 delay_background_change = true;
521 }
522 }
523
524 if (delay_background_change) {
525 if (update_shelf_observer_)
526 update_shelf_observer_->Detach();
527 // |update_shelf_observer_| deletes itself when the animation is done.
528 update_shelf_observer_ = new UpdateShelfObserver(this);
529 } else {
530 MaybeUpdateShelfBackground(change_type);
531 }
532
533 TargetBounds target_bounds;
534 CalculateTargetBounds(state_, &target_bounds);
535 UpdateBoundsAndOpacity(
536 target_bounds, true /* animate */, true /* change_work_area */,
537 delay_background_change ? update_shelf_observer_ : NULL);
538
539 // OnAutoHideStateChanged Should be emitted when:
540 // - firstly state changed to auto-hide from other state
541 // - or, auto_hide_state has changed
542 if ((old_state.visibility_state != state_.visibility_state &&
543 state_.visibility_state == SHELF_AUTO_HIDE) ||
544 old_state.auto_hide_state != state_.auto_hide_state) {
545 for (auto& observer : observers_)
546 observer.OnAutoHideStateChanged(state_.auto_hide_state);
547 }
548 }
549
550 void ShelfLayoutManager::UpdateBoundsAndOpacity(
551 const TargetBounds& target_bounds,
552 bool animate,
553 bool change_work_area,
554 ui::ImplicitAnimationObserver* observer) {
555 base::AutoReset<bool> auto_reset_updating_bounds(&updating_bounds_, true);
556 {
557 ui::ScopedLayerAnimationSettings shelf_animation_setter(
558 GetLayer(shelf_widget_)->GetAnimator());
559 ui::ScopedLayerAnimationSettings status_animation_setter(
560 GetLayer(shelf_widget_->status_area_widget())->GetAnimator());
561 if (animate) {
562 int duration = duration_override_in_ms_ ? duration_override_in_ms_
563 : kAnimationDurationMS;
564 shelf_animation_setter.SetTransitionDuration(
565 base::TimeDelta::FromMilliseconds(duration));
566 shelf_animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
567 shelf_animation_setter.SetPreemptionStrategy(
568 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
569 status_animation_setter.SetTransitionDuration(
570 base::TimeDelta::FromMilliseconds(duration));
571 status_animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
572 status_animation_setter.SetPreemptionStrategy(
573 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
574 } else {
575 StopAnimating();
576 shelf_animation_setter.SetTransitionDuration(base::TimeDelta());
577 status_animation_setter.SetTransitionDuration(base::TimeDelta());
578 }
579 if (observer)
580 status_animation_setter.AddObserver(observer);
581
582 GetLayer(shelf_widget_)->SetOpacity(target_bounds.opacity);
583 WmWindow* shelf_window = WmWindow::Get(shelf_widget_->GetNativeWindow());
584 shelf_widget_->SetBounds(shelf_window->GetParent()->ConvertRectToScreen(
585 target_bounds.shelf_bounds_in_root));
586
587 GetLayer(shelf_widget_->status_area_widget())
588 ->SetOpacity(target_bounds.status_opacity);
589
590 // Having a window which is visible but does not have an opacity is an
591 // illegal state. We therefore hide the shelf here if required.
592 if (!target_bounds.status_opacity)
593 shelf_widget_->status_area_widget()->Hide();
594 // Setting visibility during an animation causes the visibility property to
595 // animate. Override the animation settings to immediately set the
596 // visibility property. Opacity will still animate.
597
598 // TODO(harrym): Once status area widget is a child view of shelf
599 // this can be simplified.
600 gfx::Rect status_bounds = target_bounds.status_bounds_in_shelf;
601 status_bounds.Offset(target_bounds.shelf_bounds_in_root.OffsetFromOrigin());
602 WmWindow* status_window =
603 WmWindow::Get(shelf_widget_->status_area_widget()->GetNativeWindow());
604 shelf_widget_->status_area_widget()->SetBounds(
605 status_window->GetParent()->ConvertRectToScreen(status_bounds));
606
607 // For crbug.com/622431, when the shelf alignment is BOTTOM_LOCKED, we
608 // don't set display work area, as it is not real user-set alignment.
609 if (!state_.IsScreenLocked() &&
610 wm_shelf_->GetAlignment() != SHELF_ALIGNMENT_BOTTOM_LOCKED &&
611 change_work_area) {
612 gfx::Insets insets;
613 // If user session is blocked (login to new user session or add user to
614 // the existing session - multi-profile) then give 100% of work area only
615 // if keyboard is not shown.
616 if (!state_.IsAddingSecondaryUser() || !keyboard_bounds_.IsEmpty())
617 insets = target_bounds.work_area_insets;
618 WmShell::Get()->SetDisplayWorkAreaInsets(shelf_window, insets);
619 }
620 }
621
622 // Set an empty border to avoid the shelf view and status area overlapping.
623 // TODO(msw): Avoid setting bounds of views within the shelf widget here.
624 gfx::Rect shelf_bounds = gfx::Rect(target_bounds.shelf_bounds_in_root.size());
625 shelf_widget_->GetContentsView()->SetBorder(views::CreateEmptyBorder(
626 shelf_bounds.InsetsFrom(target_bounds.shelf_bounds_in_shelf)));
627 shelf_widget_->GetContentsView()->Layout();
628
629 // Setting visibility during an animation causes the visibility property to
630 // animate. Set the visibility property without an animation.
631 if (target_bounds.status_opacity)
632 shelf_widget_->status_area_widget()->Show();
633 }
634
635 void ShelfLayoutManager::StopAnimating() {
636 GetLayer(shelf_widget_)->GetAnimator()->StopAnimating();
637 GetLayer(shelf_widget_->status_area_widget())->GetAnimator()->StopAnimating();
638 }
639
640 void ShelfLayoutManager::CalculateTargetBounds(const State& state,
641 TargetBounds* target_bounds) {
642 int shelf_size = GetShelfConstant(SHELF_SIZE);
643 if (state.visibility_state == SHELF_AUTO_HIDE &&
644 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
645 // Auto-hidden shelf always starts with the default size. If a gesture-drag
646 // is in progress, then the call to UpdateTargetBoundsForGesture() below
647 // takes care of setting the height properly.
648 shelf_size = kShelfAutoHideSize;
649 } else if (state.visibility_state == SHELF_HIDDEN ||
650 (!keyboard_bounds_.IsEmpty() &&
651 !keyboard::IsKeyboardOverscrollEnabled())) {
652 shelf_size = 0;
653 }
654
655 WmWindow* shelf_window = WmWindow::Get(shelf_widget_->GetNativeWindow());
656 gfx::Rect available_bounds = wm::GetDisplayBoundsWithShelf(shelf_window);
657 available_bounds.Inset(0, chromevox_panel_height_, 0, 0);
658 int shelf_width = PrimaryAxisValue(available_bounds.width(), shelf_size);
659 int shelf_height = PrimaryAxisValue(shelf_size, available_bounds.height());
660 int bottom_shelf_vertical_offset = available_bounds.bottom();
661 if (keyboard_bounds_.IsEmpty())
662 bottom_shelf_vertical_offset -= shelf_height;
663 else
664 bottom_shelf_vertical_offset -= keyboard_bounds_.height();
665
666 gfx::Point shelf_origin = SelectValueForShelfAlignment(
667 gfx::Point(available_bounds.x(), bottom_shelf_vertical_offset),
668 gfx::Point(available_bounds.x(), available_bounds.y()),
669 gfx::Point(available_bounds.right() - shelf_width, available_bounds.y()));
670 target_bounds->shelf_bounds_in_root =
671 gfx::Rect(shelf_origin.x(), shelf_origin.y(), shelf_width, shelf_height);
672
673 gfx::Size status_size(
674 shelf_widget_->status_area_widget()->GetWindowBoundsInScreen().size());
675 if (wm_shelf_->IsHorizontalAlignment())
676 status_size.set_height(GetShelfConstant(SHELF_SIZE));
677 else
678 status_size.set_width(GetShelfConstant(SHELF_SIZE));
679
680 gfx::Point status_origin = SelectValueForShelfAlignment(
681 gfx::Point(0, 0), gfx::Point(shelf_width - status_size.width(),
682 shelf_height - status_size.height()),
683 gfx::Point(0, shelf_height - status_size.height()));
684 if (wm_shelf_->IsHorizontalAlignment() && !base::i18n::IsRTL())
685 status_origin.set_x(shelf_width - status_size.width());
686 target_bounds->status_bounds_in_shelf = gfx::Rect(status_origin, status_size);
687
688 target_bounds->work_area_insets = SelectValueForShelfAlignment(
689 gfx::Insets(0, 0, GetWorkAreaInsets(state, shelf_height), 0),
690 gfx::Insets(0, GetWorkAreaInsets(state, shelf_width), 0, 0),
691 gfx::Insets(0, 0, 0, GetWorkAreaInsets(state, shelf_width)));
692
693 // TODO(varkha): The functionality of managing insets for display areas
694 // should probably be pushed to a separate component. This would simplify or
695 // remove entirely the dependency on keyboard and dock.
696
697 if (!keyboard_bounds_.IsEmpty() && !keyboard::IsKeyboardOverscrollEnabled()) {
698 // Also push in the work area inset for the keyboard if it is visible.
699 gfx::Insets keyboard_insets(0, 0, keyboard_bounds_.height(), 0);
700 target_bounds->work_area_insets += keyboard_insets;
701 }
702
703 // Also push in the work area inset for the dock if it is visible.
704 if (!dock_bounds_.IsEmpty()) {
705 gfx::Insets dock_insets(
706 0, (dock_bounds_.x() > 0 ? 0 : dock_bounds_.width()), 0,
707 (dock_bounds_.x() > 0 ? dock_bounds_.width() : 0));
708 target_bounds->work_area_insets += dock_insets;
709 }
710
711 // Also push in the work area insets for the ChromeVox panel if it's visible.
712 if (chromevox_panel_height_) {
713 gfx::Insets chromevox_insets(chromevox_panel_height_, 0, 0, 0);
714 target_bounds->work_area_insets += chromevox_insets;
715 }
716
717 target_bounds->opacity = ComputeTargetOpacity(state);
718 target_bounds->status_opacity =
719 (state.visibility_state == SHELF_AUTO_HIDE &&
720 state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
721 gesture_drag_status_ != GESTURE_DRAG_IN_PROGRESS)
722 ? 0.0f
723 : target_bounds->opacity;
724
725 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS)
726 UpdateTargetBoundsForGesture(target_bounds);
727
728 // This needs to happen after calling UpdateTargetBoundsForGesture(), because
729 // that can change the size of the shelf.
730 target_bounds->shelf_bounds_in_shelf = SelectValueForShelfAlignment(
731 gfx::Rect(0, 0, shelf_width - status_size.width(),
732 target_bounds->shelf_bounds_in_root.height()),
733 gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
734 shelf_height - status_size.height()),
735 gfx::Rect(0, 0, target_bounds->shelf_bounds_in_root.width(),
736 shelf_height - status_size.height()));
737
738 available_bounds.Subtract(target_bounds->shelf_bounds_in_root);
739 available_bounds.Subtract(keyboard_bounds_);
740
741 WmWindow* root = shelf_window->GetRootWindow();
742 user_work_area_bounds_ = root->ConvertRectToScreen(available_bounds);
743 }
744
745 void ShelfLayoutManager::UpdateTargetBoundsForGesture(
746 TargetBounds* target_bounds) const {
747 CHECK_EQ(GESTURE_DRAG_IN_PROGRESS, gesture_drag_status_);
748 bool horizontal = wm_shelf_->IsHorizontalAlignment();
749 WmWindow* window = WmWindow::Get(shelf_widget_->GetNativeWindow());
750 gfx::Rect available_bounds = wm::GetDisplayBoundsWithShelf(window);
751 int resistance_free_region = 0;
752
753 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_HIDDEN &&
754 visibility_state() == SHELF_AUTO_HIDE &&
755 auto_hide_state() != SHELF_AUTO_HIDE_SHOWN) {
756 // If the shelf was hidden when the drag started (and the state hasn't
757 // changed since then, e.g. because the tray-menu was shown because of the
758 // drag), then allow the drag some resistance-free region at first to make
759 // sure the shelf sticks with the finger until the shelf is visible.
760 resistance_free_region = GetShelfConstant(SHELF_SIZE) - kShelfAutoHideSize;
761 }
762
763 bool resist = SelectValueForShelfAlignment(
764 gesture_drag_amount_<-resistance_free_region, gesture_drag_amount_>
765 resistance_free_region,
766 gesture_drag_amount_ < -resistance_free_region);
767
768 float translate = 0.f;
769 if (resist) {
770 float diff = fabsf(gesture_drag_amount_) - resistance_free_region;
771 diff = std::min(diff, sqrtf(diff));
772 if (gesture_drag_amount_ < 0)
773 translate = -resistance_free_region - diff;
774 else
775 translate = resistance_free_region + diff;
776 } else {
777 translate = gesture_drag_amount_;
778 }
779 int shelf_insets = GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE);
780 if (horizontal) {
781 // Move and size the shelf with the gesture.
782 int shelf_height = target_bounds->shelf_bounds_in_root.height() - translate;
783 shelf_height = std::max(shelf_height, shelf_insets);
784 target_bounds->shelf_bounds_in_root.set_height(shelf_height);
785 if (wm_shelf_->IsHorizontalAlignment()) {
786 target_bounds->shelf_bounds_in_root.set_y(available_bounds.bottom() -
787 shelf_height);
788 }
789
790 target_bounds->status_bounds_in_shelf.set_y(0);
791 } else {
792 // Move and size the shelf with the gesture.
793 int shelf_width = target_bounds->shelf_bounds_in_root.width();
794 bool right_aligned = wm_shelf_->GetAlignment() == SHELF_ALIGNMENT_RIGHT;
795 if (right_aligned)
796 shelf_width -= translate;
797 else
798 shelf_width += translate;
799 shelf_width = std::max(shelf_width, shelf_insets);
800 target_bounds->shelf_bounds_in_root.set_width(shelf_width);
801 if (right_aligned) {
802 target_bounds->shelf_bounds_in_root.set_x(available_bounds.right() -
803 shelf_width);
804 }
805
806 if (right_aligned) {
807 target_bounds->status_bounds_in_shelf.set_x(0);
808 } else {
809 target_bounds->status_bounds_in_shelf.set_x(
810 target_bounds->shelf_bounds_in_root.width() -
811 GetShelfConstant(SHELF_SIZE));
812 }
813 }
814 }
815
816 void ShelfLayoutManager::MaybeUpdateShelfBackground(AnimationChangeType type) {
817 const ShelfBackgroundType new_background_type(GetShelfBackgroundType());
818
819 if (new_background_type == shelf_background_type_)
820 return;
821
822 shelf_background_type_ = new_background_type;
823 for (auto& observer : observers_)
824 observer.OnBackgroundUpdated(shelf_background_type_, type);
825 }
826
827 void ShelfLayoutManager::UpdateAutoHideStateNow() {
828 SetState(state_.visibility_state);
829
830 // If the state did not change, the auto hide timer may still be running.
831 StopAutoHideTimer();
832 }
833
834 void ShelfLayoutManager::StopAutoHideTimer() {
835 auto_hide_timer_.Stop();
836 mouse_over_shelf_when_auto_hide_timer_started_ = false;
837 }
838
839 gfx::Rect ShelfLayoutManager::GetAutoHideShowShelfRegionInScreen() const {
840 gfx::Rect shelf_bounds_in_screen = shelf_widget_->GetWindowBoundsInScreen();
841 gfx::Vector2d offset = SelectValueForShelfAlignment(
842 gfx::Vector2d(0, shelf_bounds_in_screen.height()),
843 gfx::Vector2d(-kMaxAutoHideShowShelfRegionSize, 0),
844 gfx::Vector2d(shelf_bounds_in_screen.width(), 0));
845
846 gfx::Rect show_shelf_region_in_screen = shelf_bounds_in_screen;
847 show_shelf_region_in_screen += offset;
848 if (wm_shelf_->IsHorizontalAlignment())
849 show_shelf_region_in_screen.set_height(kMaxAutoHideShowShelfRegionSize);
850 else
851 show_shelf_region_in_screen.set_width(kMaxAutoHideShowShelfRegionSize);
852
853 // TODO: Figure out if we need any special handling when the keyboard is
854 // visible.
855 return show_shelf_region_in_screen;
856 }
857
858 ShelfAutoHideState ShelfLayoutManager::CalculateAutoHideState(
859 ShelfVisibilityState visibility_state) const {
860 if (visibility_state != SHELF_AUTO_HIDE || !wm_shelf_->IsShelfInitialized())
861 return SHELF_AUTO_HIDE_HIDDEN;
862
863 if (shelf_widget_->IsShowingAppList())
864 return SHELF_AUTO_HIDE_SHOWN;
865
866 if (shelf_widget_->status_area_widget() &&
867 shelf_widget_->status_area_widget()->ShouldShowShelf())
868 return SHELF_AUTO_HIDE_SHOWN;
869
870 if (shelf_widget_->IsShowingContextMenu())
871 return SHELF_AUTO_HIDE_SHOWN;
872
873 if (shelf_widget_->IsShowingOverflowBubble())
874 return SHELF_AUTO_HIDE_SHOWN;
875
876 if (shelf_widget_->IsActive() ||
877 (shelf_widget_->status_area_widget() &&
878 shelf_widget_->status_area_widget()->IsActive()))
879 return SHELF_AUTO_HIDE_SHOWN;
880
881 const int64_t shelf_display_id =
882 WmWindow::Get(shelf_widget_->GetNativeWindow())
883 ->GetDisplayNearestWindow()
884 .id();
885 const std::vector<WmWindow*> windows =
886 WmShell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal();
887 // Process the window list and check if there are any visible windows.
888 // Ignore app list windows that may be animating to hide after dismissal.
889 bool visible_window = false;
890 for (size_t i = 0; i < windows.size(); ++i) {
891 if (windows[i] && windows[i]->IsVisible() && !IsAppListWindow(windows[i]) &&
892 !windows[i]->GetWindowState()->IsMinimized() &&
893 windows[i]->GetDisplayNearestWindow().id() == shelf_display_id) {
894 visible_window = true;
895 break;
896 }
897 }
898 // If there are no visible windows do not hide the shelf.
899 if (!visible_window)
900 return SHELF_AUTO_HIDE_SHOWN;
901
902 if (gesture_drag_status_ == GESTURE_DRAG_COMPLETE_IN_PROGRESS)
903 return gesture_drag_auto_hide_state_;
904
905 // Don't show if the user is dragging the mouse.
906 if (in_mouse_drag_)
907 return SHELF_AUTO_HIDE_HIDDEN;
908
909 // Ignore the mouse position if mouse events are disabled.
910 if (!shelf_widget_->IsMouseEventsEnabled())
911 return SHELF_AUTO_HIDE_HIDDEN;
912
913 gfx::Rect shelf_region = shelf_widget_->GetWindowBoundsInScreen();
914 if (shelf_widget_->status_area_widget() &&
915 shelf_widget_->status_area_widget()->IsMessageBubbleShown() &&
916 IsVisible()) {
917 // Increase the the hit test area to prevent the shelf from disappearing
918 // when the mouse is over the bubble gap.
919 ShelfAlignment alignment = wm_shelf_->GetAlignment();
920 shelf_region.Inset(
921 alignment == SHELF_ALIGNMENT_RIGHT ? -kNotificationBubbleGapHeight : 0,
922 wm_shelf_->IsHorizontalAlignment() ? -kNotificationBubbleGapHeight : 0,
923 alignment == SHELF_ALIGNMENT_LEFT ? -kNotificationBubbleGapHeight : 0,
924 0);
925 }
926
927 gfx::Point cursor_position_in_screen =
928 display::Screen::GetScreen()->GetCursorScreenPoint();
929 if (shelf_region.Contains(cursor_position_in_screen))
930 return SHELF_AUTO_HIDE_SHOWN;
931
932 // When the shelf is auto hidden and the shelf is on the boundary between two
933 // displays, it is hard to trigger showing the shelf. For instance, if a
934 // user's primary display is left of their secondary display, it is hard to
935 // unautohide a left aligned shelf on the secondary display.
936 // It is hard because:
937 // - It is hard to stop the cursor in the shelf "light bar" and not overshoot.
938 // - The cursor is warped to the other display if the cursor gets to the edge
939 // of the display.
940 // Show the shelf if the cursor started on the shelf and the user overshot the
941 // shelf slightly to make it easier to show the shelf in this situation. We
942 // do not check |auto_hide_timer_|.IsRunning() because it returns false when
943 // the timer's task is running.
944 if ((state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN ||
945 mouse_over_shelf_when_auto_hide_timer_started_) &&
946 GetAutoHideShowShelfRegionInScreen().Contains(
947 cursor_position_in_screen)) {
948 return SHELF_AUTO_HIDE_SHOWN;
949 }
950
951 return SHELF_AUTO_HIDE_HIDDEN;
952 }
953
954 bool ShelfLayoutManager::IsShelfWindow(WmWindow* window) {
955 if (!window)
956 return false;
957 WmWindow* shelf_window = WmWindow::Get(shelf_widget_->GetNativeWindow());
958 WmWindow* status_window =
959 WmWindow::Get(shelf_widget_->status_area_widget()->GetNativeWindow());
960 return (shelf_window && shelf_window->Contains(window)) ||
961 (status_window && status_window->Contains(window));
962 }
963
964 int ShelfLayoutManager::GetWorkAreaInsets(const State& state, int size) const {
965 if (state.visibility_state == SHELF_VISIBLE)
966 return size;
967 if (state.visibility_state == SHELF_AUTO_HIDE)
968 return GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE);
969 return 0;
970 }
971
972 void ShelfLayoutManager::OnDockBoundsChanging(
973 const gfx::Rect& dock_bounds,
974 DockedWindowLayoutManagerObserver::Reason reason) {
975 // Skip shelf layout in case docked notification originates from this class.
976 if (reason == DISPLAY_INSETS_CHANGED)
977 return;
978 if (dock_bounds_ != dock_bounds) {
979 dock_bounds_ = dock_bounds;
980 OnWindowResized();
981 UpdateVisibilityState();
982 MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
983 }
984 }
985
986 void ShelfLayoutManager::OnLockStateEvent(LockStateObserver::EventType event) {
987 if (event == EVENT_LOCK_ANIMATION_STARTED) {
988 // Enter the screen locked state and update the visibility to avoid an odd
989 // animation when transitioning the orientation from L/R to bottom.
990 state_.pre_lock_screen_animation_active = true;
991 UpdateShelfVisibilityAfterLoginUIChange();
992 } else {
993 state_.pre_lock_screen_animation_active = false;
994 }
995 MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
996 }
997
998 void ShelfLayoutManager::SessionStateChanged(
999 session_manager::SessionState state) {
1000 // Check transition changes to/from the add user to session and change the
1001 // shelf alignment accordingly
1002 const bool was_adding_user = state_.IsAddingSecondaryUser();
1003 const bool was_locked = state_.IsScreenLocked();
1004 state_.session_state = state;
1005 MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
1006 if (was_adding_user != state_.IsAddingSecondaryUser()) {
1007 UpdateShelfVisibilityAfterLoginUIChange();
1008 return;
1009 }
1010
1011 // Force the shelf to layout for alignment (bottom if locked, restore the
1012 // previous alignment otherwise).
1013 if (was_locked != state_.IsScreenLocked())
1014 UpdateShelfVisibilityAfterLoginUIChange();
1015
1016 TargetBounds target_bounds;
1017 CalculateTargetBounds(state_, &target_bounds);
1018 UpdateBoundsAndOpacity(target_bounds, true /* animate */,
1019 true /* change_work_area */, NULL);
1020 UpdateVisibilityState();
1021 }
1022
1023 void ShelfLayoutManager::UpdateShelfVisibilityAfterLoginUIChange() {
1024 UpdateVisibilityState();
1025 LayoutShelf();
1026 }
1027
1028 float ShelfLayoutManager::ComputeTargetOpacity(const State& state) {
1029 if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
1030 state.visibility_state == SHELF_VISIBLE) {
1031 return 1.0f;
1032 }
1033 // In Chrome OS Material Design, when shelf is hidden during auto hide state,
1034 // target bounds are also hidden. So the window can extend to the edge of
1035 // screen.
1036 return (state.visibility_state == SHELF_AUTO_HIDE &&
1037 state.auto_hide_state == SHELF_AUTO_HIDE_SHOWN)
1038 ? 1.0f
1039 : 0.0f;
1040 }
1041
1042 bool ShelfLayoutManager::IsShelfHiddenForFullscreen() const {
1043 const WmWindow* fullscreen_window = wm::GetWindowForFullscreenMode(
1044 WmWindow::Get(shelf_widget_->GetNativeWindow()));
1045 return fullscreen_window &&
1046 fullscreen_window->GetWindowState()->hide_shelf_when_fullscreen();
1047 }
1048
1049 ////////////////////////////////////////////////////////////////////////////////
1050 // ShelfLayoutManager, Gesture functions:
1051
1052 void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) {
1053 gesture_drag_status_ = GESTURE_DRAG_IN_PROGRESS;
1054 gesture_drag_amount_ = 0.f;
1055 gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE
1056 ? auto_hide_state()
1057 : SHELF_AUTO_HIDE_SHOWN;
1058 MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
1059 }
1060
1061 void ShelfLayoutManager::UpdateGestureDrag(const ui::GestureEvent& gesture) {
1062 gesture_drag_amount_ += PrimaryAxisValue(gesture.details().scroll_y(),
1063 gesture.details().scroll_x());
1064 LayoutShelf();
1065 }
1066
1067 void ShelfLayoutManager::CompleteGestureDrag(const ui::GestureEvent& gesture) {
1068 bool horizontal = wm_shelf_->IsHorizontalAlignment();
1069 bool should_change = false;
1070 if (gesture.type() == ui::ET_GESTURE_SCROLL_END) {
1071 // The visibility of the shelf changes only if the shelf was dragged X%
1072 // along the correct axis. If the shelf was already visible, then the
1073 // direction of the drag does not matter.
1074 const float kDragHideThreshold = 0.4f;
1075 gfx::Rect bounds = GetIdealBounds();
1076 float drag_ratio = fabs(gesture_drag_amount_) /
1077 (horizontal ? bounds.height() : bounds.width());
1078 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
1079 should_change = drag_ratio > kDragHideThreshold;
1080 } else {
1081 bool correct_direction = false;
1082 switch (wm_shelf_->GetAlignment()) {
1083 case SHELF_ALIGNMENT_BOTTOM:
1084 case SHELF_ALIGNMENT_BOTTOM_LOCKED:
1085 case SHELF_ALIGNMENT_RIGHT:
1086 correct_direction = gesture_drag_amount_ < 0;
1087 break;
1088 case SHELF_ALIGNMENT_LEFT:
1089 correct_direction = gesture_drag_amount_ > 0;
1090 break;
1091 }
1092 should_change = correct_direction && drag_ratio > kDragHideThreshold;
1093 }
1094 } else if (gesture.type() == ui::ET_SCROLL_FLING_START) {
1095 if (gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN) {
1096 should_change = horizontal ? fabs(gesture.details().velocity_y()) > 0
1097 : fabs(gesture.details().velocity_x()) > 0;
1098 } else {
1099 should_change =
1100 SelectValueForShelfAlignment(gesture.details().velocity_y() < 0,
1101 gesture.details().velocity_x() > 0,
1102 gesture.details().velocity_x() < 0);
1103 }
1104 } else {
1105 NOTREACHED();
1106 }
1107
1108 if (!should_change) {
1109 CancelGestureDrag();
1110 return;
1111 }
1112
1113 shelf_widget_->Deactivate();
1114 shelf_widget_->status_area_widget()->Deactivate();
1115
1116 gesture_drag_auto_hide_state_ =
1117 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN
1118 ? SHELF_AUTO_HIDE_HIDDEN
1119 : SHELF_AUTO_HIDE_SHOWN;
1120 ShelfAutoHideBehavior new_auto_hide_behavior =
1121 gesture_drag_auto_hide_state_ == SHELF_AUTO_HIDE_SHOWN
1122 ? SHELF_AUTO_HIDE_BEHAVIOR_NEVER
1123 : SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
1124
1125 // When in fullscreen and the shelf is forced to be auto hidden, the auto hide
1126 // behavior affects neither the visibility state nor the auto hide state. Set
1127 // |gesture_drag_status_| to GESTURE_DRAG_COMPLETE_IN_PROGRESS to set the auto
1128 // hide state to |gesture_drag_auto_hide_state_|.
1129 gesture_drag_status_ = GESTURE_DRAG_COMPLETE_IN_PROGRESS;
1130 if (wm_shelf_->auto_hide_behavior() != new_auto_hide_behavior)
1131 wm_shelf_->SetAutoHideBehavior(new_auto_hide_behavior);
1132 else
1133 UpdateVisibilityState();
1134 gesture_drag_status_ = GESTURE_DRAG_NONE;
1135 }
1136
1137 void ShelfLayoutManager::CancelGestureDrag() {
1138 gesture_drag_status_ = GESTURE_DRAG_CANCEL_IN_PROGRESS;
1139 UpdateVisibilityState();
1140 gesture_drag_status_ = GESTURE_DRAG_NONE;
1141 }
1142
1143 } // namespace ash
OLDNEW
« no previous file with comments | « ash/common/shelf/shelf_layout_manager.h ('k') | ash/common/shelf/shelf_layout_manager_observer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698