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 |