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 #ifndef CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_H_
6 #define CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_H_
7
8 #include <memory>
9 #include <vector>
10
11 #include "base/component_export.h"
12 #include "base/macros.h"
13 #include "base/timer/timer.h"
14 #include "chromeos/ui/frame/immersive/immersive_revealed_lock.h"
15 #include "ui/aura/window_observer.h"
16 #include "ui/events/event_handler.h"
17 #include "ui/events/event_observer.h"
18 #include "ui/gfx/animation/animation_delegate.h"
19 #include "ui/gfx/animation/slide_animation.h"
20 #include "ui/views/view_observer.h"
21 #include "ui/views/widget/widget_observer.h"
22
23 namespace aura {
24 class WindowTargeter;
25 } // namespace aura
26
27 namespace ash {
28 class ImmersiveFullscreenControllerTest;
29 } // namespace ash
30
31 namespace gfx {
32 class Point;
33 } // namespace gfx
34
35 namespace ui {
36 class GestureEvent;
37 class LocatedEvent;
38 class MouseEvent;
39 class TouchEvent;
40 } // namespace ui
41
42 namespace views {
43 class View;
44 class Widget;
45 } // namespace views
46
47 namespace chromeos {
48
49 class ImmersiveFocusWatcher;
50 class ImmersiveFullscreenControllerDelegate;
51 class ImmersiveFullscreenControllerTestApi;
52
53 // This class is tested as part of //ash, eg ImmersiveFullscreenControllerTest,
54 // which inherits from AshTestBase.
COMPONENT_EXPORT(CHROMEOS_UI_FRAME)55 class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) ImmersiveFullscreenController
56 : public aura::WindowObserver,
57 public gfx::AnimationDelegate,
58 public ui::EventObserver,
59 public ui::EventHandler,
60 public views::ViewObserver,
61 public ImmersiveRevealedLock::Delegate {
62 public:
63 // How many pixels are reserved for touch-events towards the top of an
64 // immersive-fullscreen window.
65 static const int kImmersiveFullscreenTopEdgeInset;
66
67 // The height in pixels of the region below the top edge of the display in
68 // which the mouse can trigger revealing the top-of-window views. The height
69 // must be greater than 1px because the top pixel is used to trigger moving
70 // the cursor between displays if the user has a vertical display layout
71 // (primary display above/below secondary display).
72 static const int kMouseRevealBoundsHeight;
73
74 ImmersiveFullscreenController();
75 ~ImmersiveFullscreenController() override;
76
77 // Initializes the controller. Must be called prior to enabling immersive
78 // fullscreen via EnableForWidget(). |top_container| is used to keep the
79 // top-of-window views revealed when a child of |top_container| has focus.
80 // |top_container| does not affect which mouse and touch events keep the
81 // top-of-window views revealed. |widget| is the widget to make fullscreen.
82 void Init(ImmersiveFullscreenControllerDelegate* delegate,
83 views::Widget* widget,
84 views::View* top_container);
85
86 // Returns true if in immersive fullscreen.
87 bool IsEnabled() const;
88
89 // Returns true if in immersive fullscreen and the top-of-window views are
90 // fully or partially visible.
91 bool IsRevealed() const;
92
93 // Returns a lock which will keep the top-of-window views revealed for its
94 // lifetime. Several locks can be obtained. When all of the locks are
95 // destroyed, if immersive fullscreen is enabled and there is nothing else
96 // keeping the top-of-window views revealed, the top-of-window views will be
97 // closed. This method always returns a valid lock regardless of whether
98 // immersive fullscreen is enabled. The lock's lifetime can span immersive
99 // fullscreen being enabled / disabled. If acquiring the lock causes a reveal,
100 // the top-of-window views will animate according to |animate_reveal|. The
101 // caller takes ownership of the returned lock.
102 ImmersiveRevealedLock* GetRevealedLock(AnimateReveal animate_reveal)
103 WARN_UNUSED_RESULT;
104
105 views::Widget* widget() { return widget_; }
106 views::View* top_container() { return top_container_; }
107
108 // ui::EventObserver:
109 void OnEvent(const ui::Event& event) override;
110
111 // ui::EventHandler:
112 void OnEvent(ui::Event* event) override;
113 void OnGestureEvent(ui::GestureEvent* event) override;
114
115 // aura::WindowObserver:
116 void OnWindowPropertyChanged(aura::Window* window,
117 const void* key,
118 intptr_t old) override;
119 void OnWindowDestroying(aura::Window* window) override;
120
121 // views::ViewObserver:
122 void OnViewBoundsChanged(views::View* observed_view) override;
123 void OnViewIsDeleting(views::View* observed_view) override;
124
125 // gfx::AnimationDelegate overrides:
126 void AnimationEnded(const gfx::Animation* animation) override;
127 void AnimationProgressed(const gfx::Animation* animation) override;
128
129 // chromeos::ImmersiveRevealedLock::Delegate overrides:
130 void LockRevealedState(AnimateReveal animate_reveal) override;
131 void UnlockRevealedState() override;
132
133 static void EnableForWidget(views::Widget* widget, bool enabled);
134
135 static ImmersiveFullscreenController* Get(views::Widget* widget);
136
137 private:
138 friend class ash::ImmersiveFullscreenControllerTest;
139 friend class ImmersiveFullscreenControllerTestApi;
140
141 enum Animate {
142 ANIMATE_NO,
143 ANIMATE_SLOW,
144 ANIMATE_FAST,
145 };
146 enum RevealState {
147 CLOSED,
148 SLIDING_OPEN,
149 REVEALED,
150 SLIDING_CLOSED,
151 };
152 enum SwipeType { SWIPE_OPEN, SWIPE_CLOSE, SWIPE_NONE };
153
154 // Enables or disables observers for the widget's aura::Window and
155 // |top_container_|.
156 void EnableWindowObservers(bool enable);
157
158 // Enables or disables observers for mouse, touch, focus, and activation.
159 void EnableEventObservers(bool enable);
160
161 // Called to handle EventObserver::OnEvent.
162 void HandleMouseEvent(const ui::MouseEvent& event,
163 const gfx::Point& location_in_screen,
164 views::Widget* target);
165 void HandleTouchEvent(const ui::TouchEvent& event,
166 const gfx::Point& location_in_screen);
167
168 // Updates |top_edge_hover_timer_| based on a mouse |event|. If the mouse is
169 // hovered at the top of the screen the timer is started. If the mouse moves
170 // away from the top edge, or moves too much in the x direction, the timer is
171 // stopped.
172 void UpdateTopEdgeHoverTimer(const ui::MouseEvent& event,
173 const gfx::Point& location_in_screen,
174 views::Widget* target);
175
176 // Updates |located_event_revealed_lock_| based on the current mouse state and
177 // the current touch state.
178 // |event| is null if the source event is not known.
179 void UpdateLocatedEventRevealedLock(const ui::LocatedEvent* event,
180 const gfx::Point& location_in_screen);
181
182 // Convenience for calling two argument version with a null event and looking
183 // up the location from the last mouse location.
184 void UpdateLocatedEventRevealedLock();
185
186 // Acquires |located_event_revealed_lock_| if it is not already held.
187 void AcquireLocatedEventRevealedLock();
188
189 // Updates |focus_revealed_lock_| based on the currently active view and the
190 // currently active widget.
191 void UpdateFocusRevealedLock();
192
193 // Update |located_event_revealed_lock_| and |focus_revealed_lock_| as a
194 // result of a gesture of |swipe_type|. Returns true if any locks were
195 // acquired or released.
196 bool UpdateRevealedLocksForSwipe(SwipeType swipe_type);
197
198 // Returns the animation duration given |animate|.
199 base::TimeDelta GetAnimationDuration(Animate animate) const;
200
201 // Temporarily reveals the top-of-window views while in immersive mode,
202 // hiding them when the cursor exits the area of the top views. If |animate|
203 // is not ANIMATE_NO, slides in the view, otherwise shows it immediately.
204 void MaybeStartReveal(Animate animate);
205
206 // Called when the animation to slide open the top-of-window views has
207 // completed.
208 void OnSlideOpenAnimationCompleted();
209
210 // Hides the top-of-window views if immersive mode is enabled and nothing is
211 // keeping them revealed. Optionally animates.
212 void MaybeEndReveal(Animate animate);
213
214 // Called when the animation to slide out the top-of-window views has
215 // completed.
216 void OnSlideClosedAnimationCompleted();
217
218 // Returns the type of swipe given |event|.
219 SwipeType GetSwipeType(const ui::GestureEvent& event) const;
220
221 // Returns true if a mouse event at |location_in_screen| should be ignored.
222 // Ignored mouse events should not contribute to revealing or unrevealing the
223 // top-of-window views.
224 bool ShouldIgnoreMouseEventAtLocation(
225 const gfx::Point& location_in_screen) const;
226
227 // True when |location| is "near" to the top container. When the top container
228 // is not closed "near" means within the displayed bounds or above it. When
229 // the top container is closed "near" means either within the displayed
230 // bounds, above it, or within a few pixels below it. This allow the container
231 // to steal enough pixels to detect a swipe in and handles the case that there
232 // is a bezel sensor above the top container.
233 bool ShouldHandleGestureEvent(const gfx::Point& location) const;
234
235 // Returns the display bounds of the screen |widget_| is on.
236 gfx::Rect GetDisplayBoundsInScreen() const;
237
238 // Test if the |widget| is the event target to control reveal state.
239 bool IsTargetForWidget(views::Widget* widget) const;
240
241 // Enables or disables immersive fullscreen in accordance with the
242 // kImmersiveIsActive property.
243 void UpdateEnabled();
244
245 // Adds insets that redirect touch events at the top of the Widget to the
246 // Widget's window instead of a child window. These insets allow triggering
247 // immersive reveal and are not used when the immersive reveal is already
248 // active.
249 void EnableTouchInsets(bool enable);
250
251 // Not owned.
252 ImmersiveFullscreenControllerDelegate* delegate_ = nullptr;
253 views::View* top_container_ = nullptr;
254 views::Widget* widget_ = nullptr;
255
256 // True if the observers have been enabled.
257 bool event_observers_enabled_ = false;
258
259 // True when in immersive fullscreen.
260 bool enabled_ = false;
261
262 // State machine for the revealed/closed animations.
263 RevealState reveal_state_ = CLOSED;
264
265 int revealed_lock_count_ = 0;
266
267 // Timer to track cursor being held at the top edge of the screen.
268 base::OneShotTimer top_edge_hover_timer_;
269
270 // The cursor x position in screen coordinates when the cursor first hit the
271 // top edge of the screen.
272 int mouse_x_when_hit_top_in_screen_ = -1;
273
274 // Tracks if the controller has seen a ET_GESTURE_SCROLL_BEGIN, without the
275 // following events.
276 bool gesture_begun_ = false;
277
278 // Lock which keeps the top-of-window views revealed based on the current
279 // mouse state and the current touch state. Acquiring the lock is used to
280 // trigger a reveal when the user moves the mouse to the top of the screen
281 // and when the user does a SWIPE_OPEN edge gesture.
282 std::unique_ptr<ImmersiveRevealedLock> located_event_revealed_lock_;
283
284 std::unique_ptr<gfx::AnimationDelegate> animation_notifier_;
285
286 // The animation which controls sliding the top-of-window views in and out.
287 std::unique_ptr<gfx::SlideAnimation> animation_;
288
289 // Whether the animations are disabled for testing.
290 bool animations_disabled_for_test_;
291
292 std::unique_ptr<ImmersiveFocusWatcher> immersive_focus_watcher_;
293
294 // The window targeter that was in use before immersive fullscreen mode was
295 // entered, if any. Will be re-installed on the window after leaving immersive
296 // fullscreen.
297 std::unique_ptr<aura::WindowTargeter> normal_targeter_;
298
299 // |animations_disabled_for_test_| is initialized to this. See
300 // ImmersiveFullscreenControllerTestApi::GlobalAnimationDisabler for details.
301 static bool value_for_animations_disabled_for_test_;
302
303 base::WeakPtrFactory<ImmersiveFullscreenController> weak_ptr_factory_{this};
304
305 DISALLOW_COPY_AND_ASSIGN(ImmersiveFullscreenController);
306 };
307
308 } // namespace chromeos
309
310 #endif // CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_H_
311