Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef mozilla_layers_GestureEventListener_h
8
#define mozilla_layers_GestureEventListener_h
9
10
#include "InputData.h" // for MultiTouchInput, etc
11
#include "Units.h"
12
#include "mozilla/EventForwards.h" // for nsEventStatus
13
#include "mozilla/RefPtr.h" // for RefPtr
14
#include "nsISupportsImpl.h"
15
#include "nsTArray.h" // for nsTArray
16
17
namespace mozilla {
18
19
class CancelableRunnable;
20
21
namespace layers {
22
23
class AsyncPanZoomController;
24
25
/**
26
* Platform-non-specific, generalized gesture event listener. This class
27
* intercepts all touches events on their way to AsyncPanZoomController and
28
* determines whether or not they are part of a gesture.
29
*
30
* For example, seeing that two fingers are on the screen means that the user
31
* wants to do a pinch gesture, so we don't forward the touches along to
32
* AsyncPanZoomController since it will think that they are just trying to pan
33
* the screen. Instead, we generate a PinchGestureInput and send that. If the
34
* touch event is not part of a gesture, we just return nsEventStatus_eIgnore
35
* and AsyncPanZoomController is expected to handle it.
36
*/
37
class GestureEventListener final {
38
public:
39
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GestureEventListener)
40
41
explicit GestureEventListener(
42
AsyncPanZoomController* aAsyncPanZoomController);
43
44
// --------------------------------------------------------------------------
45
// These methods must only be called on the controller/UI thread.
46
//
47
48
/**
49
* General input handler for a touch event. If the touch event is not a part
50
* of a gesture, then we pass it along to AsyncPanZoomController. Otherwise,
51
* it gets consumed here and never forwarded along.
52
*/
53
nsEventStatus HandleInputEvent(const MultiTouchInput& aEvent);
54
55
/**
56
* Returns the identifier of the touch in the last touch event processed by
57
* this GestureEventListener. This should only be called when the last touch
58
* event contained only one touch.
59
*/
60
int32_t GetLastTouchIdentifier() const;
61
62
/**
63
* Function used to disable long tap gestures.
64
*
65
* On slow running tests, drags and touch events can be misinterpreted
66
* as a long tap. This allows tests to disable long tap gesture detection.
67
*/
68
static void SetLongTapEnabled(bool aLongTapEnabled);
69
70
private:
71
// Private destructor, to discourage deletion outside of Release():
72
~GestureEventListener();
73
74
/**
75
* States of GEL finite-state machine.
76
*/
77
enum GestureState {
78
// This is the initial and final state of any gesture.
79
// In this state there's no gesture going on, and we don't think we're
80
// about to enter one.
81
// Allowed next states: GESTURE_FIRST_SINGLE_TOUCH_DOWN,
82
// GESTURE_MULTI_TOUCH_DOWN.
83
GESTURE_NONE,
84
85
// A touch start with a single touch point has just happened.
86
// After having gotten into this state we start timers for MAX_TAP_TIME and
87
// StaticPrefs::ui_click_hold_context_menus_delay().
88
// Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
89
// GESTURE_FIRST_SINGLE_TOUCH_UP,
90
// GESTURE_LONG_TOUCH_DOWN,
91
// GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN.
92
GESTURE_FIRST_SINGLE_TOUCH_DOWN,
93
94
// While in GESTURE_FIRST_SINGLE_TOUCH_DOWN state a MAX_TAP_TIME timer got
95
// triggered. Now we'll trigger either a single tap if a user lifts her
96
// finger or a long tap if StaticPrefs::ui_click_hold_context_menus_delay()
97
// happens first.
98
// Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
99
// GESTURE_LONG_TOUCH_DOWN.
100
GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN,
101
102
// A user put her finger down and lifted it up quickly enough.
103
// After having gotten into this state we clear the timer for MAX_TAP_TIME.
104
// Allowed next states: GESTURE_SECOND_SINGLE_TOUCH_DOWN, GESTURE_NONE,
105
// GESTURE_MULTI_TOUCH_DOWN.
106
GESTURE_FIRST_SINGLE_TOUCH_UP,
107
108
// A user put down her finger again right after a single tap thus the
109
// gesture can't be a single tap, but rather a double tap. But we're
110
// still not sure about that until the user lifts her finger again.
111
// Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_ONE_TOUCH_PINCH,
112
// GESTURE_NONE.
113
GESTURE_SECOND_SINGLE_TOUCH_DOWN,
114
115
// A long touch has happened, but the user still keeps her finger down.
116
// We'll trigger a "long tap up" event when the finger is up.
117
// Allowed next states: GESTURE_NONE, GESTURE_MULTI_TOUCH_DOWN.
118
GESTURE_LONG_TOUCH_DOWN,
119
120
// We have detected that two or more fingers are on the screen, but there
121
// hasn't been enough movement yet to make us start actually zooming the
122
// screen.
123
// Allowed next states: GESTURE_PINCH, GESTURE_NONE
124
GESTURE_MULTI_TOUCH_DOWN,
125
126
// There are two or more fingers on the screen, and the user has already
127
// pinched enough for us to start zooming the screen.
128
// Allowed next states: GESTURE_NONE
129
GESTURE_PINCH,
130
131
// The user has double tapped, but not lifted her finger, and moved her
132
// finger more than PINCH_START_THRESHOLD.
133
// Allowed next states: GESTURE_NONE.
134
GESTURE_ONE_TOUCH_PINCH
135
};
136
137
/**
138
* These HandleInput* functions comprise input alphabet of the GEL
139
* finite-state machine triggering state transitions.
140
*/
141
nsEventStatus HandleInputTouchSingleStart();
142
nsEventStatus HandleInputTouchMultiStart();
143
nsEventStatus HandleInputTouchEnd();
144
nsEventStatus HandleInputTouchMove();
145
nsEventStatus HandleInputTouchCancel();
146
void HandleInputTimeoutLongTap();
147
void HandleInputTimeoutMaxTap(bool aDuringFastFling);
148
149
void EnterFirstSingleTouchDown();
150
151
void TriggerSingleTapConfirmedEvent();
152
153
bool MoveDistanceExceeds(ScreenCoord aThreshold) const;
154
bool MoveDistanceIsLarge() const;
155
bool SecondTapIsFar() const;
156
157
/**
158
* Returns current vertical span, counting from the where the gesture first
159
* began (after a brief delay detecting the gesture from first touch).
160
*/
161
ScreenCoord GetYSpanFromGestureStartPoint();
162
163
/**
164
* Do actual state transition and reset substates.
165
*/
166
void SetState(GestureState aState);
167
168
RefPtr<AsyncPanZoomController> mAsyncPanZoomController;
169
170
/**
171
* Array containing all active touches. When a touch happens it, gets added to
172
* this array, even if we choose not to handle it. When it ends, we remove it.
173
* We need to maintain this array in order to detect the end of the
174
* "multitouch" states because touch start events contain all current touches,
175
* but touch end events contain only those touches that have gone.
176
*/
177
nsTArray<SingleTouchData> mTouches;
178
179
/**
180
* Current state we're dealing with.
181
*/
182
GestureState mState;
183
184
/**
185
* Total change in span since we detected a pinch gesture. Only used when we
186
* are in the |GESTURE_WAITING_PINCH| state and need to know how far zoomed
187
* out we are compared to our original pinch span. Note that this does _not_
188
* continue to be updated once we jump into the |GESTURE_PINCH| state.
189
*/
190
ScreenCoord mSpanChange;
191
192
/**
193
* Previous span calculated for the purposes of setting inside a
194
* PinchGestureInput.
195
*/
196
ScreenCoord mPreviousSpan;
197
198
/* Properties similar to mSpanChange and mPreviousSpan, but for the focus */
199
ScreenCoord mFocusChange;
200
ScreenPoint mPreviousFocus;
201
202
/**
203
* Cached copy of the last touch input.
204
*/
205
MultiTouchInput mLastTouchInput;
206
207
/**
208
* Cached copy of the last tap gesture input.
209
* In the situation when we have a tap followed by a pinch we lose info
210
* about tap since we keep only last input and to dispatch it correctly
211
* we save last tap copy into this variable.
212
* For more info see bug 947892.
213
*/
214
MultiTouchInput mLastTapInput;
215
216
/**
217
* Position of the last touch that exceeds the GetTouchStartTolerance when
218
* performing a one-touch-pinch gesture; using the mTouchStartPosition is
219
* slightly inaccurate because by the time the touch position has passed
220
* the threshold for the gesture, there is already a span that the zoom
221
* is calculated from, instead of starting at 1.0 when the threshold gets
222
* passed.
223
*/
224
ScreenPoint mOneTouchPinchStartPosition;
225
226
/**
227
* Position of the last touch starting. This is only valid during an attempt
228
* to determine if a touch is a tap. If a touch point moves away from
229
* mTouchStartPosition to the distance greater than
230
* AsyncPanZoomController::GetTouchStartTolerance() while in
231
* GESTURE_FIRST_SINGLE_TOUCH_DOWN, GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN
232
* or GESTURE_SECOND_SINGLE_TOUCH_DOWN then we're certain the gesture is
233
* not tap.
234
*/
235
ScreenPoint mTouchStartPosition;
236
237
/**
238
* We store the window/GeckoView's display offset as well, so we can
239
* track the user's physical touch movements in absolute display coordinates.
240
*/
241
ExternalPoint mTouchStartOffset;
242
243
/**
244
* Task used to timeout a long tap. This gets posted to the UI thread such
245
* that it runs a time when a single tap happens. We cache it so that
246
* we can cancel it if any other touch event happens.
247
*
248
* The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN
249
* and GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN states.
250
*
251
* CancelLongTapTimeoutTask: Cancel the mLongTapTimeoutTask and also set
252
* it to null.
253
*/
254
RefPtr<CancelableRunnable> mLongTapTimeoutTask;
255
void CancelLongTapTimeoutTask();
256
void CreateLongTapTimeoutTask();
257
258
/**
259
* Task used to timeout a single tap or a double tap.
260
*
261
* The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN,
262
* GESTURE_FIRST_SINGLE_TOUCH_UP and GESTURE_SECOND_SINGLE_TOUCH_DOWN states.
263
*
264
* CancelMaxTapTimeoutTask: Cancel the mMaxTapTimeoutTask and also set
265
* it to null.
266
*/
267
RefPtr<CancelableRunnable> mMaxTapTimeoutTask;
268
void CancelMaxTapTimeoutTask();
269
void CreateMaxTapTimeoutTask();
270
271
/**
272
* Tracks whether the single-tap event was already sent to content. This is
273
* needed because it affects how the double-tap gesture, if detected, is
274
* handled. The value is only valid in states GESTURE_FIRST_SINGLE_TOUCH_UP
275
* and GESTURE_SECOND_SINGLE_TOUCH_DOWN; to more easily catch violations it is
276
* stored in a Maybe which is set to Nothing() at all other times.
277
*/
278
Maybe<bool> mSingleTapSent;
279
};
280
281
} // namespace layers
282
} // namespace mozilla
283
284
#endif