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

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

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

Powered by Google App Engine
This is Rietveld 408576698