OLD | NEW |
| (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/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_shell.h" | |
23 #include "ash/common/wm_window.h" | |
24 #include "ash/public/cpp/shell_window_ids.h" | |
25 #include "ash/root_window_controller.h" | |
26 #include "ash/system/status_area_widget.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 | |
OLD | NEW |