| 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 |