| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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/wm/immersive_fullscreen_controller.h" | |
| 6 | |
| 7 #include <set> | |
| 8 | |
| 9 #include "ash/common/ash_constants.h" | |
| 10 #include "ash/common/wm/immersive/wm_immersive_fullscreen_controller_delegate.h" | |
| 11 #include "ash/shared/immersive_context.h" | |
| 12 #include "ash/shared/immersive_focus_watcher.h" | |
| 13 #include "ash/shared/immersive_gesture_handler.h" | |
| 14 #include "ash/wm/immersive_handler_factory.h" | |
| 15 #include "base/metrics/histogram.h" | |
| 16 #include "ui/display/display.h" | |
| 17 #include "ui/display/screen.h" | |
| 18 #include "ui/events/base_event_utils.h" | |
| 19 #include "ui/gfx/animation/slide_animation.h" | |
| 20 #include "ui/gfx/geometry/point.h" | |
| 21 #include "ui/gfx/geometry/rect.h" | |
| 22 #include "ui/views/bubble/bubble_dialog_delegate.h" | |
| 23 #include "ui/views/view.h" | |
| 24 #include "ui/views/widget/widget.h" | |
| 25 | |
| 26 namespace ash { | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 // Duration for the reveal show/hide slide animation. The slower duration is | |
| 31 // used for the initial slide out to give the user more change to see what | |
| 32 // happened. | |
| 33 const int kRevealSlowAnimationDurationMs = 400; | |
| 34 const int kRevealFastAnimationDurationMs = 200; | |
| 35 | |
| 36 // The delay in milliseconds between the mouse stopping at the top edge of the | |
| 37 // screen and the top-of-window views revealing. | |
| 38 const int kMouseRevealDelayMs = 200; | |
| 39 | |
| 40 // The maximum amount of pixels that the cursor can move for the cursor to be | |
| 41 // considered "stopped". This allows the user to reveal the top-of-window views | |
| 42 // without holding the cursor completely still. | |
| 43 const int kMouseRevealXThresholdPixels = 3; | |
| 44 | |
| 45 // Used to multiply x value of an update in check to determine if gesture is | |
| 46 // vertical. This is used to make sure that gesture is close to vertical instead | |
| 47 // of just more vertical then horizontal. | |
| 48 const int kSwipeVerticalThresholdMultiplier = 3; | |
| 49 | |
| 50 // The height in pixels of the region above the top edge of the display which | |
| 51 // hosts the immersive fullscreen window in which mouse events are ignored | |
| 52 // (cannot reveal or unreveal the top-of-window views). | |
| 53 // See ShouldIgnoreMouseEventAtLocation() for more details. | |
| 54 const int kHeightOfDeadRegionAboveTopContainer = 10; | |
| 55 | |
| 56 } // namespace | |
| 57 | |
| 58 // The height in pixels of the region below the top edge of the display in which | |
| 59 // the mouse can trigger revealing the top-of-window views. | |
| 60 // The height must be greater than 1px because the top pixel is used to trigger | |
| 61 // moving the cursor between displays if the user has a vertical display layout | |
| 62 // (primary display above/below secondary display). | |
| 63 const int ImmersiveFullscreenController::kMouseRevealBoundsHeight = 3; | |
| 64 | |
| 65 //////////////////////////////////////////////////////////////////////////////// | |
| 66 | |
| 67 ImmersiveFullscreenController::ImmersiveFullscreenController() | |
| 68 : delegate_(NULL), | |
| 69 top_container_(NULL), | |
| 70 widget_(NULL), | |
| 71 observers_enabled_(false), | |
| 72 enabled_(false), | |
| 73 reveal_state_(CLOSED), | |
| 74 revealed_lock_count_(0), | |
| 75 mouse_x_when_hit_top_in_screen_(-1), | |
| 76 gesture_begun_(false), | |
| 77 animation_(new gfx::SlideAnimation(this)), | |
| 78 animations_disabled_for_test_(false), | |
| 79 weak_ptr_factory_(this) {} | |
| 80 | |
| 81 ImmersiveFullscreenController::~ImmersiveFullscreenController() { | |
| 82 EnableWindowObservers(false); | |
| 83 } | |
| 84 | |
| 85 void ImmersiveFullscreenController::Init( | |
| 86 WmImmersiveFullscreenControllerDelegate* delegate, | |
| 87 views::Widget* widget, | |
| 88 views::View* top_container) { | |
| 89 delegate_ = delegate; | |
| 90 top_container_ = top_container; | |
| 91 widget_ = widget; | |
| 92 ImmersiveContext::Get()->InstallResizeHandleWindowTargeter(this); | |
| 93 } | |
| 94 | |
| 95 void ImmersiveFullscreenController::SetEnabled(WindowType window_type, | |
| 96 bool enabled) { | |
| 97 if (enabled_ == enabled) | |
| 98 return; | |
| 99 enabled_ = enabled; | |
| 100 | |
| 101 EnableWindowObservers(enabled_); | |
| 102 | |
| 103 ImmersiveContext::Get()->OnEnteringOrExitingImmersive(this, enabled); | |
| 104 | |
| 105 if (enabled_) { | |
| 106 // Animate enabling immersive mode by sliding out the top-of-window views. | |
| 107 // No animation occurs if a lock is holding the top-of-window views open. | |
| 108 | |
| 109 // Do a reveal to set the initial state for the animation. (And any | |
| 110 // required state in case the animation cannot run because of a lock holding | |
| 111 // the top-of-window views open.) | |
| 112 MaybeStartReveal(ANIMATE_NO); | |
| 113 | |
| 114 // Reset the located event so that it does not affect whether the | |
| 115 // top-of-window views are hidden. | |
| 116 located_event_revealed_lock_.reset(); | |
| 117 | |
| 118 // Try doing the animation. | |
| 119 MaybeEndReveal(ANIMATE_SLOW); | |
| 120 | |
| 121 if (reveal_state_ == REVEALED) { | |
| 122 // Reveal was unsuccessful. Reacquire the revealed locks if appropriate. | |
| 123 UpdateLocatedEventRevealedLock(); | |
| 124 if (immersive_focus_watcher_) | |
| 125 immersive_focus_watcher_->UpdateFocusRevealedLock(); | |
| 126 } | |
| 127 } else { | |
| 128 // Stop cursor-at-top tracking. | |
| 129 top_edge_hover_timer_.Stop(); | |
| 130 reveal_state_ = CLOSED; | |
| 131 | |
| 132 delegate_->OnImmersiveFullscreenExited(); | |
| 133 } | |
| 134 | |
| 135 if (enabled_) { | |
| 136 UMA_HISTOGRAM_ENUMERATION("Ash.ImmersiveFullscreen.WindowType", window_type, | |
| 137 WINDOW_TYPE_COUNT); | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 bool ImmersiveFullscreenController::IsEnabled() const { | |
| 142 return enabled_; | |
| 143 } | |
| 144 | |
| 145 bool ImmersiveFullscreenController::IsRevealed() const { | |
| 146 return enabled_ && reveal_state_ != CLOSED; | |
| 147 } | |
| 148 | |
| 149 ImmersiveRevealedLock* ImmersiveFullscreenController::GetRevealedLock( | |
| 150 AnimateReveal animate_reveal) { | |
| 151 return new ImmersiveRevealedLock(weak_ptr_factory_.GetWeakPtr(), | |
| 152 animate_reveal); | |
| 153 } | |
| 154 | |
| 155 //////////////////////////////////////////////////////////////////////////////// | |
| 156 | |
| 157 void ImmersiveFullscreenController::OnMouseEvent( | |
| 158 const ui::MouseEvent& event, | |
| 159 const gfx::Point& location_in_screen, | |
| 160 views::Widget* target) { | |
| 161 if (!enabled_) | |
| 162 return; | |
| 163 | |
| 164 if (event.type() != ui::ET_MOUSE_MOVED && | |
| 165 event.type() != ui::ET_MOUSE_PRESSED && | |
| 166 event.type() != ui::ET_MOUSE_RELEASED && | |
| 167 event.type() != ui::ET_MOUSE_CAPTURE_CHANGED) { | |
| 168 return; | |
| 169 } | |
| 170 | |
| 171 // Mouse hover can initiate revealing the top-of-window views while |widget_| | |
| 172 // is inactive. | |
| 173 | |
| 174 if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) { | |
| 175 top_edge_hover_timer_.Stop(); | |
| 176 UpdateLocatedEventRevealedLock(&event, location_in_screen); | |
| 177 } else if (event.type() != ui::ET_MOUSE_CAPTURE_CHANGED) { | |
| 178 // Trigger a reveal if the cursor pauses at the top of the screen for a | |
| 179 // while. | |
| 180 UpdateTopEdgeHoverTimer(event, location_in_screen, target); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 void ImmersiveFullscreenController::OnTouchEvent( | |
| 185 const ui::TouchEvent& event, | |
| 186 const gfx::Point& location_in_screen) { | |
| 187 if (!enabled_ || event.type() != ui::ET_TOUCH_PRESSED) | |
| 188 return; | |
| 189 | |
| 190 // Touch should not initiate revealing the top-of-window views while |widget_| | |
| 191 // is inactive. | |
| 192 if (!widget_->IsActive()) | |
| 193 return; | |
| 194 | |
| 195 UpdateLocatedEventRevealedLock(&event, location_in_screen); | |
| 196 } | |
| 197 | |
| 198 void ImmersiveFullscreenController::OnGestureEvent( | |
| 199 ui::GestureEvent* event, | |
| 200 const gfx::Point& location_in_screen) { | |
| 201 if (!enabled_) | |
| 202 return; | |
| 203 | |
| 204 // Touch gestures should not initiate revealing the top-of-window views while | |
| 205 // |widget_| is inactive. | |
| 206 if (!widget_->IsActive()) | |
| 207 return; | |
| 208 | |
| 209 switch (event->type()) { | |
| 210 case ui::ET_GESTURE_SCROLL_BEGIN: | |
| 211 if (ShouldHandleGestureEvent(location_in_screen)) { | |
| 212 gesture_begun_ = true; | |
| 213 // Do not consume the event. Otherwise, we end up consuming all | |
| 214 // ui::ET_GESTURE_SCROLL_BEGIN events in the top-of-window views | |
| 215 // when the top-of-window views are revealed. | |
| 216 } | |
| 217 break; | |
| 218 case ui::ET_GESTURE_SCROLL_UPDATE: | |
| 219 if (gesture_begun_) { | |
| 220 if (UpdateRevealedLocksForSwipe(GetSwipeType(*event))) | |
| 221 event->SetHandled(); | |
| 222 gesture_begun_ = false; | |
| 223 } | |
| 224 break; | |
| 225 case ui::ET_GESTURE_SCROLL_END: | |
| 226 case ui::ET_SCROLL_FLING_START: | |
| 227 gesture_begun_ = false; | |
| 228 break; | |
| 229 default: | |
| 230 break; | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 void ImmersiveFullscreenController::OnPointerEventObserved( | |
| 235 const ui::PointerEvent& event, | |
| 236 const gfx::Point& location_in_screen, | |
| 237 views::Widget* target) { | |
| 238 if (event.IsMousePointerEvent()) { | |
| 239 const ui::MouseEvent mouse_event(event); | |
| 240 OnMouseEvent(mouse_event, location_in_screen, target); | |
| 241 } else { | |
| 242 DCHECK(event.IsTouchPointerEvent()); | |
| 243 const ui::TouchEvent touch_event(event); | |
| 244 OnTouchEvent(touch_event, location_in_screen); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 void ImmersiveFullscreenController::OnMouseCaptureChanged() { | |
| 249 const ui::MouseEvent event(ui::ET_MOUSE_CAPTURE_CHANGED, gfx::Point(), | |
| 250 gfx::Point(), ui::EventTimeForNow(), 0, 0); | |
| 251 OnMouseEvent(event, display::Screen::GetScreen()->GetCursorScreenPoint(), | |
| 252 nullptr); | |
| 253 } | |
| 254 | |
| 255 //////////////////////////////////////////////////////////////////////////////// | |
| 256 // views::WidgetObserver overrides: | |
| 257 | |
| 258 void ImmersiveFullscreenController::OnWidgetDestroying(views::Widget* widget) { | |
| 259 EnableWindowObservers(false); | |
| 260 widget_window_ = nullptr; | |
| 261 | |
| 262 // Set |enabled_| to false such that any calls to MaybeStartReveal() and | |
| 263 // MaybeEndReveal() have no effect. | |
| 264 enabled_ = false; | |
| 265 } | |
| 266 | |
| 267 //////////////////////////////////////////////////////////////////////////////// | |
| 268 // gfx::AnimationDelegate overrides: | |
| 269 | |
| 270 void ImmersiveFullscreenController::AnimationEnded( | |
| 271 const gfx::Animation* animation) { | |
| 272 if (reveal_state_ == SLIDING_OPEN) { | |
| 273 OnSlideOpenAnimationCompleted(); | |
| 274 } else if (reveal_state_ == SLIDING_CLOSED) { | |
| 275 OnSlideClosedAnimationCompleted(); | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 void ImmersiveFullscreenController::AnimationProgressed( | |
| 280 const gfx::Animation* animation) { | |
| 281 delegate_->SetVisibleFraction(animation->GetCurrentValue()); | |
| 282 } | |
| 283 | |
| 284 //////////////////////////////////////////////////////////////////////////////// | |
| 285 // ImmersiveRevealedLock::Delegate overrides: | |
| 286 | |
| 287 void ImmersiveFullscreenController::LockRevealedState( | |
| 288 AnimateReveal animate_reveal) { | |
| 289 ++revealed_lock_count_; | |
| 290 Animate animate = | |
| 291 (animate_reveal == ANIMATE_REVEAL_YES) ? ANIMATE_FAST : ANIMATE_NO; | |
| 292 MaybeStartReveal(animate); | |
| 293 } | |
| 294 | |
| 295 void ImmersiveFullscreenController::UnlockRevealedState() { | |
| 296 --revealed_lock_count_; | |
| 297 DCHECK_GE(revealed_lock_count_, 0); | |
| 298 if (revealed_lock_count_ == 0) { | |
| 299 // Always animate ending the reveal fast. | |
| 300 MaybeEndReveal(ANIMATE_FAST); | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 //////////////////////////////////////////////////////////////////////////////// | |
| 305 // private: | |
| 306 | |
| 307 void ImmersiveFullscreenController::EnableWindowObservers(bool enable) { | |
| 308 if (observers_enabled_ == enable) | |
| 309 return; | |
| 310 observers_enabled_ = enable; | |
| 311 | |
| 312 if (enable) { | |
| 313 immersive_focus_watcher_ = | |
| 314 ImmersiveHandlerFactory::Get()->CreateFocusWatcher(this); | |
| 315 immersive_gesture_handler_ = | |
| 316 ImmersiveHandlerFactory::Get()->CreateGestureHandler(this); | |
| 317 widget_->AddObserver(this); | |
| 318 const bool wants_moves = true; | |
| 319 ImmersiveContext::Get()->AddPointerWatcher(this, wants_moves); | |
| 320 } else { | |
| 321 ImmersiveContext::Get()->RemovePointerWatcher(this); | |
| 322 widget_->RemoveObserver(this); | |
| 323 immersive_gesture_handler_.reset(); | |
| 324 immersive_focus_watcher_.reset(); | |
| 325 | |
| 326 animation_->Stop(); | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 void ImmersiveFullscreenController::UpdateTopEdgeHoverTimer( | |
| 331 const ui::MouseEvent& event, | |
| 332 const gfx::Point& location_in_screen, | |
| 333 views::Widget* target) { | |
| 334 DCHECK(enabled_); | |
| 335 DCHECK(reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED); | |
| 336 | |
| 337 // Check whether |widget_| is the event target instead of checking for | |
| 338 // activation. This allows the timer to be started when |widget_| is inactive | |
| 339 // but prevents starting the timer if the mouse is over a portion of the top | |
| 340 // edge obscured by an unrelated widget. | |
| 341 if (!top_edge_hover_timer_.IsRunning() && target != widget_) { | |
| 342 return; | |
| 343 } | |
| 344 | |
| 345 // Mouse hover should not initiate revealing the top-of-window views while a | |
| 346 // window has mouse capture. | |
| 347 if (ImmersiveContext::Get()->DoesAnyWindowHaveCapture()) | |
| 348 return; | |
| 349 | |
| 350 if (ShouldIgnoreMouseEventAtLocation(location_in_screen)) | |
| 351 return; | |
| 352 | |
| 353 // Stop the timer if the cursor left the top edge or is on a different | |
| 354 // display. | |
| 355 gfx::Rect hit_bounds_in_screen = GetDisplayBoundsInScreen(); | |
| 356 hit_bounds_in_screen.set_height(kMouseRevealBoundsHeight); | |
| 357 if (!hit_bounds_in_screen.Contains(location_in_screen)) { | |
| 358 top_edge_hover_timer_.Stop(); | |
| 359 return; | |
| 360 } | |
| 361 | |
| 362 // The cursor is now at the top of the screen. Consider the cursor "not | |
| 363 // moving" even if it moves a little bit because users don't have perfect | |
| 364 // pointing precision. (The y position is not tested because | |
| 365 // |hit_bounds_in_screen| is short.) | |
| 366 if (top_edge_hover_timer_.IsRunning() && | |
| 367 abs(location_in_screen.x() - mouse_x_when_hit_top_in_screen_) <= | |
| 368 kMouseRevealXThresholdPixels) | |
| 369 return; | |
| 370 | |
| 371 // Start the reveal if the cursor doesn't move for some amount of time. | |
| 372 mouse_x_when_hit_top_in_screen_ = location_in_screen.x(); | |
| 373 top_edge_hover_timer_.Stop(); | |
| 374 // Timer is stopped when |this| is destroyed, hence Unretained() is safe. | |
| 375 top_edge_hover_timer_.Start( | |
| 376 FROM_HERE, base::TimeDelta::FromMilliseconds(kMouseRevealDelayMs), | |
| 377 base::Bind( | |
| 378 &ImmersiveFullscreenController::AcquireLocatedEventRevealedLock, | |
| 379 base::Unretained(this))); | |
| 380 } | |
| 381 | |
| 382 void ImmersiveFullscreenController::UpdateLocatedEventRevealedLock( | |
| 383 const ui::LocatedEvent* event, | |
| 384 const gfx::Point& location_in_screen) { | |
| 385 if (!enabled_) | |
| 386 return; | |
| 387 DCHECK(!event || event->IsMouseEvent() || event->IsTouchEvent()); | |
| 388 | |
| 389 // Neither the mouse nor touch can initiate a reveal when the top-of-window | |
| 390 // views are sliding closed or are closed with the following exceptions: | |
| 391 // - Hovering at y = 0 which is handled in OnMouseEvent(). | |
| 392 // - Doing a SWIPE_OPEN edge gesture which is handled in OnGestureEvent(). | |
| 393 if (reveal_state_ == CLOSED || reveal_state_ == SLIDING_CLOSED) | |
| 394 return; | |
| 395 | |
| 396 // For the sake of simplicity, ignore |widget_|'s activation in computing | |
| 397 // whether the top-of-window views should stay revealed. Ideally, the | |
| 398 // top-of-window views would stay revealed only when the mouse cursor is | |
| 399 // hovered above a non-obscured portion of the top-of-window views. The | |
| 400 // top-of-window views may be partially obscured when |widget_| is inactive. | |
| 401 | |
| 402 // Ignore all events while a window has capture. This keeps the top-of-window | |
| 403 // views revealed during a drag. | |
| 404 if (ImmersiveContext::Get()->DoesAnyWindowHaveCapture()) | |
| 405 return; | |
| 406 | |
| 407 if ((!event || event->IsMouseEvent()) && | |
| 408 ShouldIgnoreMouseEventAtLocation(location_in_screen)) { | |
| 409 return; | |
| 410 } | |
| 411 | |
| 412 // The visible bounds of |top_container_| should be contained in | |
| 413 // |hit_bounds_in_screen|. | |
| 414 std::vector<gfx::Rect> hit_bounds_in_screen = | |
| 415 delegate_->GetVisibleBoundsInScreen(); | |
| 416 bool keep_revealed = false; | |
| 417 for (size_t i = 0; i < hit_bounds_in_screen.size(); ++i) { | |
| 418 // Allow the cursor to move slightly off the top-of-window views before | |
| 419 // sliding closed. In the case of ImmersiveModeControllerAsh, this helps | |
| 420 // when the user is attempting to click on the bookmark bar and overshoots | |
| 421 // slightly. | |
| 422 if (event && event->type() == ui::ET_MOUSE_MOVED) { | |
| 423 const int kBoundsOffsetY = 8; | |
| 424 hit_bounds_in_screen[i].Inset(0, 0, 0, -kBoundsOffsetY); | |
| 425 } | |
| 426 | |
| 427 if (hit_bounds_in_screen[i].Contains(location_in_screen)) { | |
| 428 keep_revealed = true; | |
| 429 break; | |
| 430 } | |
| 431 } | |
| 432 | |
| 433 if (keep_revealed) | |
| 434 AcquireLocatedEventRevealedLock(); | |
| 435 else | |
| 436 located_event_revealed_lock_.reset(); | |
| 437 } | |
| 438 | |
| 439 void ImmersiveFullscreenController::UpdateLocatedEventRevealedLock() { | |
| 440 if (!ImmersiveContext::Get()->IsMouseEventsEnabled()) { | |
| 441 // If mouse events are disabled, the user's last interaction was probably | |
| 442 // via touch. Do no do further processing in this case as there is no easy | |
| 443 // way of retrieving the position of the user's last touch. | |
| 444 return; | |
| 445 } | |
| 446 UpdateLocatedEventRevealedLock( | |
| 447 nullptr, display::Screen::GetScreen()->GetCursorScreenPoint()); | |
| 448 } | |
| 449 | |
| 450 void ImmersiveFullscreenController::AcquireLocatedEventRevealedLock() { | |
| 451 // CAUTION: Acquiring the lock results in a reentrant call to | |
| 452 // AcquireLocatedEventRevealedLock() when | |
| 453 // |ImmersiveFullscreenController::animations_disabled_for_test_| is true. | |
| 454 if (!located_event_revealed_lock_.get()) | |
| 455 located_event_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES)); | |
| 456 } | |
| 457 | |
| 458 bool ImmersiveFullscreenController::UpdateRevealedLocksForSwipe( | |
| 459 SwipeType swipe_type) { | |
| 460 if (!enabled_ || swipe_type == SWIPE_NONE) | |
| 461 return false; | |
| 462 | |
| 463 // Swipes while |widget_| is inactive should have been filtered out in | |
| 464 // OnGestureEvent(). | |
| 465 DCHECK(widget_->IsActive()); | |
| 466 | |
| 467 if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) { | |
| 468 if (swipe_type == SWIPE_OPEN && !located_event_revealed_lock_.get()) { | |
| 469 located_event_revealed_lock_.reset(GetRevealedLock(ANIMATE_REVEAL_YES)); | |
| 470 return true; | |
| 471 } | |
| 472 } else { | |
| 473 if (swipe_type == SWIPE_CLOSE) { | |
| 474 // Attempt to end the reveal. If other code is holding onto a lock, the | |
| 475 // attempt will be unsuccessful. | |
| 476 located_event_revealed_lock_.reset(); | |
| 477 if (immersive_focus_watcher_) | |
| 478 immersive_focus_watcher_->ReleaseLock(); | |
| 479 | |
| 480 if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) { | |
| 481 widget_->GetFocusManager()->ClearFocus(); | |
| 482 return true; | |
| 483 } | |
| 484 | |
| 485 // Ending the reveal was unsuccessful. Reaquire the locks if appropriate. | |
| 486 UpdateLocatedEventRevealedLock(); | |
| 487 if (immersive_focus_watcher_) | |
| 488 immersive_focus_watcher_->UpdateFocusRevealedLock(); | |
| 489 } | |
| 490 } | |
| 491 return false; | |
| 492 } | |
| 493 | |
| 494 int ImmersiveFullscreenController::GetAnimationDuration(Animate animate) const { | |
| 495 switch (animate) { | |
| 496 case ANIMATE_NO: | |
| 497 return 0; | |
| 498 case ANIMATE_SLOW: | |
| 499 return kRevealSlowAnimationDurationMs; | |
| 500 case ANIMATE_FAST: | |
| 501 return kRevealFastAnimationDurationMs; | |
| 502 } | |
| 503 NOTREACHED(); | |
| 504 return 0; | |
| 505 } | |
| 506 | |
| 507 void ImmersiveFullscreenController::MaybeStartReveal(Animate animate) { | |
| 508 if (!enabled_) | |
| 509 return; | |
| 510 | |
| 511 if (animations_disabled_for_test_) | |
| 512 animate = ANIMATE_NO; | |
| 513 | |
| 514 // Callers with ANIMATE_NO expect this function to synchronously reveal the | |
| 515 // top-of-window views. | |
| 516 if (reveal_state_ == REVEALED || | |
| 517 (reveal_state_ == SLIDING_OPEN && animate != ANIMATE_NO)) { | |
| 518 return; | |
| 519 } | |
| 520 | |
| 521 RevealState previous_reveal_state = reveal_state_; | |
| 522 reveal_state_ = SLIDING_OPEN; | |
| 523 if (previous_reveal_state == CLOSED) { | |
| 524 delegate_->OnImmersiveRevealStarted(); | |
| 525 | |
| 526 // Do not do any more processing if OnImmersiveRevealStarted() changed | |
| 527 // |reveal_state_|. | |
| 528 if (reveal_state_ != SLIDING_OPEN) | |
| 529 return; | |
| 530 } | |
| 531 // Slide in the reveal view. | |
| 532 if (animate == ANIMATE_NO) { | |
| 533 animation_->Reset(1); | |
| 534 OnSlideOpenAnimationCompleted(); | |
| 535 } else { | |
| 536 animation_->SetSlideDuration(GetAnimationDuration(animate)); | |
| 537 animation_->Show(); | |
| 538 } | |
| 539 } | |
| 540 | |
| 541 void ImmersiveFullscreenController::OnSlideOpenAnimationCompleted() { | |
| 542 DCHECK_EQ(SLIDING_OPEN, reveal_state_); | |
| 543 reveal_state_ = REVEALED; | |
| 544 delegate_->SetVisibleFraction(1); | |
| 545 | |
| 546 // The user may not have moved the mouse since the reveal was initiated. | |
| 547 // Update the revealed lock to reflect the mouse's current state. | |
| 548 UpdateLocatedEventRevealedLock(); | |
| 549 } | |
| 550 | |
| 551 void ImmersiveFullscreenController::MaybeEndReveal(Animate animate) { | |
| 552 if (!enabled_ || revealed_lock_count_ != 0) | |
| 553 return; | |
| 554 | |
| 555 if (animations_disabled_for_test_) | |
| 556 animate = ANIMATE_NO; | |
| 557 | |
| 558 // Callers with ANIMATE_NO expect this function to synchronously close the | |
| 559 // top-of-window views. | |
| 560 if (reveal_state_ == CLOSED || | |
| 561 (reveal_state_ == SLIDING_CLOSED && animate != ANIMATE_NO)) { | |
| 562 return; | |
| 563 } | |
| 564 | |
| 565 reveal_state_ = SLIDING_CLOSED; | |
| 566 int duration_ms = GetAnimationDuration(animate); | |
| 567 if (duration_ms > 0) { | |
| 568 animation_->SetSlideDuration(duration_ms); | |
| 569 animation_->Hide(); | |
| 570 } else { | |
| 571 animation_->Reset(0); | |
| 572 OnSlideClosedAnimationCompleted(); | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 void ImmersiveFullscreenController::OnSlideClosedAnimationCompleted() { | |
| 577 DCHECK_EQ(SLIDING_CLOSED, reveal_state_); | |
| 578 reveal_state_ = CLOSED; | |
| 579 delegate_->OnImmersiveRevealEnded(); | |
| 580 } | |
| 581 | |
| 582 ImmersiveFullscreenController::SwipeType | |
| 583 ImmersiveFullscreenController::GetSwipeType( | |
| 584 const ui::GestureEvent& event) const { | |
| 585 if (event.type() != ui::ET_GESTURE_SCROLL_UPDATE) | |
| 586 return SWIPE_NONE; | |
| 587 // Make sure that it is a clear vertical gesture. | |
| 588 if (std::abs(event.details().scroll_y()) <= | |
| 589 kSwipeVerticalThresholdMultiplier * std::abs(event.details().scroll_x())) | |
| 590 return SWIPE_NONE; | |
| 591 if (event.details().scroll_y() < 0) | |
| 592 return SWIPE_CLOSE; | |
| 593 if (event.details().scroll_y() > 0) | |
| 594 return SWIPE_OPEN; | |
| 595 return SWIPE_NONE; | |
| 596 } | |
| 597 | |
| 598 bool ImmersiveFullscreenController::ShouldIgnoreMouseEventAtLocation( | |
| 599 const gfx::Point& location) const { | |
| 600 // Ignore mouse events in the region immediately above the top edge of the | |
| 601 // display. This is to handle the case of a user with a vertical display | |
| 602 // layout (primary display above/below secondary display) and the immersive | |
| 603 // fullscreen window on the bottom display. It is really hard to trigger a | |
| 604 // reveal in this case because: | |
| 605 // - It is hard to stop the cursor in the top |kMouseRevealBoundsHeight| | |
| 606 // pixels of the bottom display. | |
| 607 // - The cursor is warped to the top display if the cursor gets to the top | |
| 608 // edge of the bottom display. | |
| 609 // Mouse events are ignored in the bottom few pixels of the top display | |
| 610 // (Mouse events in this region cannot start or end a reveal). This allows a | |
| 611 // user to overshoot the top of the bottom display and still reveal the | |
| 612 // top-of-window views. | |
| 613 gfx::Rect dead_region = GetDisplayBoundsInScreen(); | |
| 614 dead_region.set_y(dead_region.y() - kHeightOfDeadRegionAboveTopContainer); | |
| 615 dead_region.set_height(kHeightOfDeadRegionAboveTopContainer); | |
| 616 return dead_region.Contains(location); | |
| 617 } | |
| 618 | |
| 619 bool ImmersiveFullscreenController::ShouldHandleGestureEvent( | |
| 620 const gfx::Point& location) const { | |
| 621 DCHECK(widget_->IsActive()); | |
| 622 if (reveal_state_ == REVEALED) { | |
| 623 std::vector<gfx::Rect> hit_bounds_in_screen( | |
| 624 delegate_->GetVisibleBoundsInScreen()); | |
| 625 for (size_t i = 0; i < hit_bounds_in_screen.size(); ++i) { | |
| 626 if (hit_bounds_in_screen[i].Contains(location)) | |
| 627 return true; | |
| 628 } | |
| 629 return false; | |
| 630 } | |
| 631 | |
| 632 // When the top-of-window views are not fully revealed, handle gestures which | |
| 633 // start in the top few pixels of the screen. | |
| 634 gfx::Rect hit_bounds_in_screen(GetDisplayBoundsInScreen()); | |
| 635 hit_bounds_in_screen.set_height(kImmersiveFullscreenTopEdgeInset); | |
| 636 if (hit_bounds_in_screen.Contains(location)) | |
| 637 return true; | |
| 638 | |
| 639 // There may be a bezel sensor off screen logically above | |
| 640 // |hit_bounds_in_screen|. The check for the event not contained by the | |
| 641 // closest screen ensures that the event is from a valid bezel (as opposed to | |
| 642 // another screen in an extended desktop). | |
| 643 gfx::Rect screen_bounds = | |
| 644 display::Screen::GetScreen()->GetDisplayNearestPoint(location).bounds(); | |
| 645 return (!screen_bounds.Contains(location) && | |
| 646 location.y() < hit_bounds_in_screen.y() && | |
| 647 location.x() >= hit_bounds_in_screen.x() && | |
| 648 location.x() < hit_bounds_in_screen.right()); | |
| 649 } | |
| 650 | |
| 651 gfx::Rect ImmersiveFullscreenController::GetDisplayBoundsInScreen() const { | |
| 652 return ImmersiveContext::Get()->GetDisplayBoundsInScreen(widget_); | |
| 653 } | |
| 654 | |
| 655 } // namespace ash | |
| OLD | NEW |