@react-native-oh-tpl/react-native-gesture-handler 2.14.8-rc.1 → 2.14.14
Sign up to get free protection for your applications and to get access to all the features.
- package/DrawerLayout/index.ts +2 -2
- package/Swipeable/index.ts +2 -2
- package/harmony/gesture_handler/BuildProfile.ets +17 -0
- package/harmony/gesture_handler/build-profile.json5 +19 -0
- package/harmony/gesture_handler/hvigorfile.ts +2 -0
- package/harmony/gesture_handler/index.ets +2 -0
- package/harmony/gesture_handler/oh-package-lock.json5 +18 -0
- package/harmony/gesture_handler/oh-package.json5 +12 -0
- package/harmony/gesture_handler/src/main/cpp/CMakeLists.txt +8 -0
- package/harmony/gesture_handler/src/main/cpp/GestureHandlerPackage.h +12 -0
- package/harmony/gesture_handler/src/main/cpp/RnohReactNativeHarmonyGestureHandlerPackage.cpp +123 -0
- package/harmony/gesture_handler/src/main/cpp/RnohReactNativeHarmonyGestureHandlerPackage.h +15 -0
- package/harmony/gesture_handler/src/main/cpp/componentInstances/RNGestureHandlerButtonComponentInstance.h +27 -0
- package/harmony/gesture_handler/src/main/cpp/componentInstances/RNGestureHandlerRootViewComponentInstance.h +245 -0
- package/harmony/gesture_handler/src/main/ets/RNOHPackage.ets +17 -0
- package/harmony/gesture_handler/src/main/ets/core/CircularBuffer.ts +42 -0
- package/harmony/gesture_handler/src/main/ets/core/GestureHandler.ts +739 -0
- package/harmony/gesture_handler/src/main/ets/core/GestureHandlerOrchestrator.ts +344 -0
- package/harmony/gesture_handler/src/main/ets/core/GestureHandlerRegistry.ts +63 -0
- package/harmony/gesture_handler/src/main/ets/core/IncomingEvent.ts +78 -0
- package/harmony/gesture_handler/src/main/ets/core/InteractionManager.ts +144 -0
- package/harmony/gesture_handler/src/main/ets/core/LeastSquareSolver.ts +182 -0
- package/harmony/gesture_handler/src/main/ets/core/Multiset.ts +26 -0
- package/harmony/gesture_handler/src/main/ets/core/OutgoingEvent.ts +34 -0
- package/harmony/gesture_handler/src/main/ets/core/OutgoingEventDispatcher.ts +12 -0
- package/harmony/gesture_handler/src/main/ets/core/PointerTracker.ts +239 -0
- package/harmony/gesture_handler/src/main/ets/core/RNGHError.ts +5 -0
- package/harmony/gesture_handler/src/main/ets/core/RNGHLogger.ts +16 -0
- package/harmony/gesture_handler/src/main/ets/core/State.ts +47 -0
- package/harmony/gesture_handler/src/main/ets/core/Vector2D.ts +80 -0
- package/harmony/gesture_handler/src/main/ets/core/VelocityTracker.ts +106 -0
- package/harmony/gesture_handler/src/main/ets/core/View.ts +21 -0
- package/harmony/gesture_handler/src/main/ets/core/ViewRegistry.ts +7 -0
- package/harmony/gesture_handler/src/main/ets/core/index.ts +15 -0
- package/harmony/gesture_handler/src/main/ets/detectors/ScaleGestureDetector.ts +169 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/FlingGestureHandler.ts +219 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/GestureHandlerFactory.ts +67 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/LongPressGestureHandler.ts +139 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/ManualGestureHandler.ts +50 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/NativeViewGestureHandler.ts +124 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/PanGestureHandler.ts +361 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/PinchGestureHandler.ts +174 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/RotationGestureHandler.ts +172 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/TapGestureHandler.ts +216 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/detectors/RotationGestureDetector.ts +167 -0
- package/harmony/gesture_handler/src/main/ets/gesture-handlers/index.ts +1 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/GestureHandlerPackage.ts +25 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/Logger.ts +107 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/OutgoingEventDispatchers.ts +94 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/RNGHRootViewController.ts +182 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/RNGHView.ts +62 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/RNGHViewController.ts +262 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/RNGHViewRegistry.ts +19 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/RNGestureHandlerModule.ts +267 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/RNOHGestureResponder.ts +15 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/RNOHScrollLocker.ts +25 -0
- package/harmony/gesture_handler/src/main/ets/rnoh/types.ts +25 -0
- package/harmony/gesture_handler/src/main/module.json5 +9 -0
- package/harmony/gesture_handler/src/main/resources/base/element/color.json +8 -0
- package/harmony/gesture_handler/src/main/resources/base/element/string.json +16 -0
- package/harmony/gesture_handler/src/main/resources/base/media/icon.png +0 -0
- package/harmony/gesture_handler/src/main/resources/base/profile/main_pages.json +5 -0
- package/harmony/gesture_handler/src/main/resources/en_US/element/string.json +16 -0
- package/harmony/gesture_handler/src/main/resources/zh_CN/element/string.json +16 -0
- package/harmony/gesture_handler/ts.ts +2 -0
- package/harmony/gesture_handler.har +0 -0
- package/lib/commonjs/RNGestureHandlerModule.js +3 -2
- package/lib/commonjs/RNGestureHandlerModule.js.map +1 -1
- package/lib/commonjs/components/GestureHandlerRootView.js +3 -3
- package/lib/commonjs/components/GestureHandlerRootView.js.map +1 -1
- package/lib/commonjs/handlers/createHandler.js +18 -15
- package/lib/commonjs/handlers/createHandler.js.map +1 -1
- package/lib/commonjs/index.js +36 -8
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/specs/NativeRNGestureHandlerModule.js +2 -1
- package/lib/commonjs/specs/NativeRNGestureHandlerModule.js.map +1 -1
- package/lib/commonjs/specs/RNGestureHandlerButtonNativeComponent.js +3 -2
- package/lib/commonjs/specs/RNGestureHandlerButtonNativeComponent.js.map +1 -1
- package/lib/commonjs/specs/RNGestureHandlerRootViewNativeComponent.js +3 -2
- package/lib/commonjs/specs/RNGestureHandlerRootViewNativeComponent.js.map +1 -1
- package/lib/module/RNGestureHandlerModule.js.map +1 -1
- package/lib/module/components/GestureHandlerRootView.js.map +1 -1
- package/lib/module/handlers/createHandler.js +15 -12
- package/lib/module/handlers/createHandler.js.map +1 -1
- package/lib/module/index.js +5 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/NativeRNGestureHandlerModule.js.map +1 -1
- package/lib/module/specs/RNGestureHandlerButtonNativeComponent.js.map +1 -1
- package/lib/module/specs/RNGestureHandlerRootViewNativeComponent.js.map +1 -1
- package/lib/typescript/RNGestureHandlerModule.d.ts +2 -2
- package/lib/typescript/components/GestureHandlerRootView.d.ts +6 -6
- package/lib/typescript/handlers/createHandler.d.ts +11 -11
- package/lib/typescript/index.d.ts +47 -42
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/specs/NativeRNGestureHandlerModule.d.ts +14 -14
- package/lib/typescript/specs/RNGestureHandlerButtonNativeComponent.d.ts +14 -14
- package/lib/typescript/specs/RNGestureHandlerRootViewNativeComponent.d.ts +6 -6
- package/package.json +78 -70
- package/src/RNGestureHandlerModule.ts +4 -4
- package/src/components/GestureHandlerRootView.tsx +23 -23
- package/src/handlers/createHandler.tsx +534 -534
- package/src/index.ts +172 -172
- package/src/specs/NativeRNGestureHandlerModule.ts +26 -26
- package/src/specs/RNGestureHandlerButtonNativeComponent.ts +18 -18
- package/src/specs/RNGestureHandlerRootViewNativeComponent.ts +6 -6
@@ -0,0 +1,344 @@
|
|
1
|
+
import { GestureHandler } from "./GestureHandler"
|
2
|
+
import { State, getStateName } from "./State"
|
3
|
+
import { PointerType } from "./IncomingEvent"
|
4
|
+
import { RNGHLogger } from "./RNGHLogger"
|
5
|
+
|
6
|
+
export class GestureHandlerOrchestrator {
|
7
|
+
private awaitingHandlers: Set<GestureHandler> = new Set()
|
8
|
+
private gestureHandlers: GestureHandler[] = []
|
9
|
+
private handlersToCancel: GestureHandler[] = []
|
10
|
+
private activationIndex: number = 0
|
11
|
+
|
12
|
+
private logger: RNGHLogger
|
13
|
+
|
14
|
+
constructor(logger: RNGHLogger) {
|
15
|
+
this.logger = logger.cloneAndJoinPrefix("GestureHandlerOrchestrator")
|
16
|
+
}
|
17
|
+
|
18
|
+
public onHandlerStateChange(handler: GestureHandler, newState: State, oldState: State, sendIfDisabled?: boolean) {
|
19
|
+
const logger =
|
20
|
+
this.logger.cloneAndJoinPrefix(`onHandlerStateChange(handler=${handler.getTag()}, newState=${getStateName(newState)}, oldState=${getStateName(oldState)})`)
|
21
|
+
logger.debug("start")
|
22
|
+
|
23
|
+
if (!handler.isEnabled() && !sendIfDisabled) {
|
24
|
+
return;
|
25
|
+
}
|
26
|
+
|
27
|
+
// this.handlingChangeSemaphore += 1;
|
28
|
+
|
29
|
+
if (this.isFinishedState(newState)) {
|
30
|
+
this.awaitingHandlers.forEach((otherHandler) => {
|
31
|
+
if (otherHandler.shouldWaitFor(handler)) {
|
32
|
+
if (newState === State.END) {
|
33
|
+
otherHandler?.cancel();
|
34
|
+
if (otherHandler.getState() === State.END) {
|
35
|
+
// Handle edge case, where discrete gestures end immediately after activation thus
|
36
|
+
// their state is set to END and when the gesture they are waiting for activates they
|
37
|
+
// should be cancelled, however `cancel` was never sent as gestures were already in the END state.
|
38
|
+
// Send synthetic BEGAN -> CANCELLED to properly handle JS logic
|
39
|
+
otherHandler.sendEvent({ newState: State.CANCELLED, oldState: State.BEGAN });
|
40
|
+
}
|
41
|
+
otherHandler?.setAwaiting(false);
|
42
|
+
} else {
|
43
|
+
this.tryActivate(otherHandler);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
});
|
47
|
+
}
|
48
|
+
|
49
|
+
if (newState === State.ACTIVE) {
|
50
|
+
this.tryActivate(handler);
|
51
|
+
} else if (oldState === State.ACTIVE || oldState === State.END) {
|
52
|
+
if (handler.isActive()) {
|
53
|
+
handler.sendEvent({ newState, oldState });
|
54
|
+
} else if (
|
55
|
+
oldState === State.ACTIVE &&
|
56
|
+
(newState === State.CANCELLED || newState === State.FAILED)
|
57
|
+
) {
|
58
|
+
handler.sendEvent({ newState, oldState: State.BEGAN });
|
59
|
+
}
|
60
|
+
} else if (
|
61
|
+
oldState !== State.UNDETERMINED ||
|
62
|
+
newState !== State.CANCELLED
|
63
|
+
) {
|
64
|
+
handler.sendEvent({ newState, oldState });
|
65
|
+
}
|
66
|
+
|
67
|
+
// this.handlingChangeSemaphore -= 1;
|
68
|
+
|
69
|
+
this.cleanUpFinishedHandlers();
|
70
|
+
if (!this.awaitingHandlers.has(handler)) {
|
71
|
+
this.cleanupAwaitingHandlers(handler);
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
private isFinishedState(state: State) {
|
76
|
+
return [State.END, State.FAILED, State.CANCELLED].includes(state)
|
77
|
+
}
|
78
|
+
|
79
|
+
private tryActivate(handler: GestureHandler): void {
|
80
|
+
const logger = this.logger.cloneAndJoinPrefix(`tryActivate(${handler.getTag()})`)
|
81
|
+
logger.debug({
|
82
|
+
gestureHandlers: this.gestureHandlers.map(gh => gh.getTag()),
|
83
|
+
awaitingHandlers: Array.from(this.awaitingHandlers).map(gh => gh.getTag()),
|
84
|
+
handlersToCancel: this.handlersToCancel.map(gh => gh.getTag())
|
85
|
+
})
|
86
|
+
if (this.shouldBeCancelledByFinishedHandler(handler)) {
|
87
|
+
logger.debug("failed to activate - cancelling")
|
88
|
+
handler.cancel();
|
89
|
+
return;
|
90
|
+
}
|
91
|
+
if (this.hasOtherHandlerToWaitFor(handler)) {
|
92
|
+
this.addAwaitingHandler(handler);
|
93
|
+
logger.debug("request ignored - has other handler waiting")
|
94
|
+
return;
|
95
|
+
}
|
96
|
+
const handlerState = handler.getState();
|
97
|
+
if (handlerState === State.CANCELLED || handlerState === State.FAILED) {
|
98
|
+
logger.debug("request ignored - handler is in cancelled or failed state")
|
99
|
+
return;
|
100
|
+
}
|
101
|
+
if (this.shouldActivate(handler)) {
|
102
|
+
logger.debug("activating")
|
103
|
+
this.makeActive(handler);
|
104
|
+
return;
|
105
|
+
}
|
106
|
+
if (handlerState === State.ACTIVE) {
|
107
|
+
logger.debug("failed to activate - handler is already active, marking as fail")
|
108
|
+
handler.fail();
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
if (handlerState === State.BEGAN) {
|
112
|
+
logger.debug("handler is in BEGAN state but shouldActivate returned false - cancelling")
|
113
|
+
handler.cancel();
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
private shouldBeCancelledByFinishedHandler(
|
118
|
+
handler: GestureHandler
|
119
|
+
): boolean {
|
120
|
+
const shouldBeCancelled = (otherHandler: GestureHandler) => {
|
121
|
+
return (
|
122
|
+
handler.shouldWaitFor(otherHandler) &&
|
123
|
+
otherHandler.getState() === State.END
|
124
|
+
);
|
125
|
+
};
|
126
|
+
return this.gestureHandlers.some(shouldBeCancelled);
|
127
|
+
}
|
128
|
+
|
129
|
+
private hasOtherHandlerToWaitFor(handler: GestureHandler): boolean {
|
130
|
+
const logger = this.logger.cloneAndJoinPrefix(`hasOtherHandlerToWaitFor(handler=${handler.getTag()})`)
|
131
|
+
for (const otherHandler of this.gestureHandlers) {
|
132
|
+
if (otherHandler === handler) {
|
133
|
+
return false
|
134
|
+
}
|
135
|
+
if (!this.isFinishedState(otherHandler.getState()) && handler.shouldWaitFor(otherHandler)) {
|
136
|
+
logger.debug("true")
|
137
|
+
return true
|
138
|
+
}
|
139
|
+
}
|
140
|
+
logger.debug("false")
|
141
|
+
return false;
|
142
|
+
}
|
143
|
+
|
144
|
+
private addAwaitingHandler(handler: GestureHandler) {
|
145
|
+
const logger = this.logger.cloneAndJoinPrefix(`addAwaitingHandler(handlerTag=${handler.getTag()})`)
|
146
|
+
logger.debug({ awaitingHandlers: this.awaitingHandlers })
|
147
|
+
if (!this.awaitingHandlers.has(handler)) {
|
148
|
+
this.awaitingHandlers.add(handler)
|
149
|
+
handler.setAwaiting(true)
|
150
|
+
handler.setActivationIndex(this.activationIndex++)
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
private shouldActivate(handler: GestureHandler) {
|
155
|
+
for (const otherHandler of this.gestureHandlers) {
|
156
|
+
if (this.shouldHandlerBeCancelledByOtherHandler({ handler, otherHandler })) {
|
157
|
+
return false
|
158
|
+
}
|
159
|
+
}
|
160
|
+
return true
|
161
|
+
}
|
162
|
+
|
163
|
+
private shouldHandlerBeCancelledByOtherHandler({ handler, otherHandler }: {
|
164
|
+
handler: GestureHandler,
|
165
|
+
otherHandler: GestureHandler
|
166
|
+
}): boolean {
|
167
|
+
const logger =
|
168
|
+
this.logger.cloneAndJoinPrefix(`shouldHandlerBeCancelledByOtherHandler(${handler.getTag()}, ${otherHandler.getTag()})`)
|
169
|
+
if (this.canRunSimultaneously(handler, otherHandler)) {
|
170
|
+
logger.debug("false")
|
171
|
+
return false;
|
172
|
+
}
|
173
|
+
if (handler !== otherHandler && (handler.isAwaiting() || handler.getState() === State.ACTIVE)) {
|
174
|
+
const result = handler.shouldBeCancelledByOther(otherHandler)
|
175
|
+
logger.debug(`${result} (1)`)
|
176
|
+
return result
|
177
|
+
}
|
178
|
+
const result = this.checkOverlap(handler, otherHandler)
|
179
|
+
logger.debug(`${result} (2)`)
|
180
|
+
return result;
|
181
|
+
}
|
182
|
+
|
183
|
+
private canRunSimultaneously(handlerA: GestureHandler, handlerB: GestureHandler) {
|
184
|
+
const logger = this.logger.cloneAndJoinPrefix("canRunSimultaneously")
|
185
|
+
const result = handlerA === handlerB
|
186
|
+
|| handlerA.shouldRecognizeSimultaneously(handlerB)
|
187
|
+
|| handlerB.shouldRecognizeSimultaneously(handlerA)
|
188
|
+
|
189
|
+
logger.debug({ result, handlerA: handlerA.getTag(), handlerB: handlerB.getTag() })
|
190
|
+
return result
|
191
|
+
}
|
192
|
+
|
193
|
+
private checkOverlap(
|
194
|
+
handler: GestureHandler,
|
195
|
+
otherHandler: GestureHandler
|
196
|
+
): boolean {
|
197
|
+
// If handlers don't have common pointers, default return value is false.
|
198
|
+
// However, if at least on pointer overlaps with both handlers, we return true
|
199
|
+
// This solves issue in overlapping parents example
|
200
|
+
|
201
|
+
// TODO: Find better way to handle that issue, for example by activation order and handler cancelling
|
202
|
+
|
203
|
+
const handlerPointers: number[] = handler.getTrackedPointersID();
|
204
|
+
const otherPointers: number[] = otherHandler.getTrackedPointersID();
|
205
|
+
let overlap = false;
|
206
|
+
handlerPointers.forEach((pointer: number) => {
|
207
|
+
const handlerX: number = handler.getTracker().getLastX(pointer);
|
208
|
+
const handlerY: number = handler.getTracker().getLastY(pointer);
|
209
|
+
if (
|
210
|
+
handler.getView().isPositionInBounds({ x: handlerX, y: handlerY }) &&
|
211
|
+
otherHandler.getView().isPositionInBounds({ x: handlerX, y: handlerY })
|
212
|
+
) {
|
213
|
+
overlap = true;
|
214
|
+
}
|
215
|
+
});
|
216
|
+
otherPointers.forEach((pointer: number) => {
|
217
|
+
const otherX: number = otherHandler.getTracker().getLastX(pointer);
|
218
|
+
const otherY: number = otherHandler.getTracker().getLastY(pointer);
|
219
|
+
if (
|
220
|
+
handler.getView().isPositionInBounds({ x: otherX, y: otherY }) &&
|
221
|
+
otherHandler.getView().isPositionInBounds({ x: otherX, y: otherY })
|
222
|
+
) {
|
223
|
+
overlap = true;
|
224
|
+
}
|
225
|
+
});
|
226
|
+
return overlap;
|
227
|
+
}
|
228
|
+
|
229
|
+
private makeActive(handler: GestureHandler): void {
|
230
|
+
handler.setActive(true)
|
231
|
+
handler.setShouldResetProgress(true)
|
232
|
+
handler.setActivationIndex(this.activationIndex++)
|
233
|
+
for (const otherHandler of this.gestureHandlers) {
|
234
|
+
if (this.shouldHandlerBeCancelledByOtherHandler({ handler: otherHandler, otherHandler: handler })) {
|
235
|
+
this.handlersToCancel.push(otherHandler)
|
236
|
+
}
|
237
|
+
}
|
238
|
+
for (let i = this.handlersToCancel.length - 1; i >= 0; --i) {
|
239
|
+
this.handlersToCancel[i]?.cancel();
|
240
|
+
}
|
241
|
+
this.handlersToCancel = []
|
242
|
+
for (const awaitingHandler of this.awaitingHandlers) {
|
243
|
+
if (this.shouldHandlerBeCancelledByOtherHandler({ handler: awaitingHandler, otherHandler: handler })) {
|
244
|
+
awaitingHandler.cancel();
|
245
|
+
awaitingHandler.setAwaiting(true);
|
246
|
+
}
|
247
|
+
}
|
248
|
+
const currentState = handler.getState()
|
249
|
+
handler.sendEvent({ newState: State.ACTIVE, oldState: State.BEGAN })
|
250
|
+
if (currentState !== State.ACTIVE) {
|
251
|
+
handler.sendEvent({ newState: State.END, oldState: State.ACTIVE })
|
252
|
+
if (currentState !== State.END) {
|
253
|
+
handler.sendEvent({ newState: State.UNDETERMINED, oldState: State.END })
|
254
|
+
}
|
255
|
+
}
|
256
|
+
if (handler.isAwaiting()) {
|
257
|
+
handler.setAwaiting(false)
|
258
|
+
this.awaitingHandlers.delete(handler)
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
private cleanUpHandlers(handler: GestureHandler) {
|
263
|
+
this.cleanUpFinishedHandlers()
|
264
|
+
if (this.awaitingHandlers.has(handler)) {
|
265
|
+
this.cleanupAwaitingHandlers(handler);
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
private cleanUpFinishedHandlers(): void {
|
270
|
+
for (let i = this.gestureHandlers.length - 1; i >= 0; --i) {
|
271
|
+
const handler = this.gestureHandlers[i];
|
272
|
+
if (!handler) {
|
273
|
+
continue;
|
274
|
+
}
|
275
|
+
if (this.isFinishedState(handler.getState()) && !handler.isAwaiting()) {
|
276
|
+
this.gestureHandlers.splice(i, 1);
|
277
|
+
this.cleanUpHandler(handler);
|
278
|
+
}
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
282
|
+
private cleanupAwaitingHandlers(handler: GestureHandler): void {
|
283
|
+
const logger = this.logger.cloneAndJoinPrefix(`cleanupAwaitingHandlers(handler=${handler.getTag()})`)
|
284
|
+
logger.debug({ awaitingHandlers: this.awaitingHandlers })
|
285
|
+
for (const awaitingHandler of this.awaitingHandlers) {
|
286
|
+
if (
|
287
|
+
awaitingHandler.isAwaiting() &&
|
288
|
+
awaitingHandler.shouldWaitFor(handler)
|
289
|
+
) {
|
290
|
+
this.cleanUpHandler(awaitingHandler);
|
291
|
+
this.awaitingHandlers.delete(awaitingHandler)
|
292
|
+
}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
private cleanUpHandler(handler: GestureHandler) {
|
297
|
+
handler.reset();
|
298
|
+
handler.setActive(false);
|
299
|
+
handler.setAwaiting(false);
|
300
|
+
handler.setActivationIndex(Number.MAX_VALUE);
|
301
|
+
}
|
302
|
+
|
303
|
+
public registerHandlerIfNotPresent(handler: GestureHandler) {
|
304
|
+
this.logger.info(`registerHandlerIfNotPresent(${handler.getTag()})`)
|
305
|
+
if (this.gestureHandlers.includes(handler)) {
|
306
|
+
return;
|
307
|
+
}
|
308
|
+
this.gestureHandlers.push(handler);
|
309
|
+
handler.setActive(false);
|
310
|
+
handler.setAwaiting(false);
|
311
|
+
handler.setActivationIndex(Number.MAX_SAFE_INTEGER);
|
312
|
+
}
|
313
|
+
|
314
|
+
/**
|
315
|
+
This function is called when handler receives touchdown event
|
316
|
+
If handler is using mouse or pen as a pointer and any handler receives touch event,
|
317
|
+
mouse/pen event disappears - it doesn't send onPointerCancel nor onPointerUp (and others)
|
318
|
+
This became a problem because handler was left at active state without any signal to end or fail
|
319
|
+
To handle this, when new touch event is received, we loop through active handlers and check which type of
|
320
|
+
pointer they're using. If there are any handler with mouse/pen as a pointer, we cancel them
|
321
|
+
*/
|
322
|
+
public cancelMouseAndPenGestures(currentHandler: GestureHandler): void {
|
323
|
+
this.logger.info("cancelMouseAndPenGestures")
|
324
|
+
this.gestureHandlers.forEach((handler: GestureHandler) => {
|
325
|
+
if (handler.getPointerType() !== PointerType.MOUSE && handler.getPointerType() !== PointerType.PEN) {
|
326
|
+
return;
|
327
|
+
}
|
328
|
+
|
329
|
+
if (handler !== currentHandler) {
|
330
|
+
handler.cancel();
|
331
|
+
} else {
|
332
|
+
// Handler that received touch event should have its pointer tracker reset
|
333
|
+
// This allows handler to smoothly change from mouse/pen to touch
|
334
|
+
// The drawback is, that when we try to use mouse/pen one more time, it doesn't send onPointerDown at the first time
|
335
|
+
// so it is required to click two times to get handler to work
|
336
|
+
//
|
337
|
+
// However, handler will receive manually created onPointerEnter that is triggered in EventManager in onPointerMove method.
|
338
|
+
// There may be possibility to use that fact to make handler respond properly to first mouse click
|
339
|
+
handler.getTracker().resetTracker();
|
340
|
+
}
|
341
|
+
});
|
342
|
+
}
|
343
|
+
}
|
344
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import { GestureHandler } from "./GestureHandler"
|
2
|
+
import { View } from "./View"
|
3
|
+
import { RNGHLogger } from "./RNGHLogger"
|
4
|
+
import { ViewRegistry } from "./ViewRegistry"
|
5
|
+
|
6
|
+
export class GestureHandlerRegistry {
|
7
|
+
private gestureHandlerByHandlerTag: Map<number, GestureHandler> = new Map()
|
8
|
+
private gestureHandlersByViewTag: Map<number, Set<GestureHandler>> = new Map()
|
9
|
+
private viewRegistry: ViewRegistry | undefined
|
10
|
+
private logger: RNGHLogger
|
11
|
+
|
12
|
+
constructor(viewRegistry: ViewRegistry | undefined, logger: RNGHLogger) {
|
13
|
+
this.logger = logger.cloneAndJoinPrefix("GestureHandlerRegistry")
|
14
|
+
this.viewRegistry = viewRegistry
|
15
|
+
}
|
16
|
+
|
17
|
+
public addGestureHandler(gestureHandler: GestureHandler) {
|
18
|
+
this.gestureHandlerByHandlerTag.set(gestureHandler.getTag(), gestureHandler)
|
19
|
+
}
|
20
|
+
|
21
|
+
public bindGestureHandlerWithView(gestureHandlerTag: number, view: View) {
|
22
|
+
this.logger.cloneAndJoinPrefix("bindGestureHandlerWithView").debug({gestureHandlerTag, viewTag: view.getTag()})
|
23
|
+
const viewTag = view.getTag()
|
24
|
+
if (!this.gestureHandlersByViewTag.has(viewTag))
|
25
|
+
this.gestureHandlersByViewTag.set(viewTag, new Set())
|
26
|
+
const gestureHandler = this.gestureHandlerByHandlerTag.get(gestureHandlerTag)
|
27
|
+
this.gestureHandlersByViewTag.get(viewTag).add(gestureHandler)
|
28
|
+
gestureHandler.onViewAttached(view)
|
29
|
+
}
|
30
|
+
|
31
|
+
public getGestureHandlersByViewTag(viewTag: number): GestureHandler[] {
|
32
|
+
return Array.from(this.gestureHandlersByViewTag.get(viewTag) ?? [])
|
33
|
+
}
|
34
|
+
|
35
|
+
public removeGestureHandlerByHandlerTag(handlerTag: number) {
|
36
|
+
const gestureHandler = this.gestureHandlerByHandlerTag.get(handlerTag)
|
37
|
+
if (!gestureHandler) {
|
38
|
+
return;
|
39
|
+
}
|
40
|
+
const viewTag = gestureHandler.getView()?.getTag();
|
41
|
+
if (viewTag) {
|
42
|
+
const gestureHandlers = this.gestureHandlersByViewTag.get(viewTag)
|
43
|
+
if (gestureHandlers) {
|
44
|
+
gestureHandlers.delete(gestureHandler)
|
45
|
+
if (gestureHandlers.size === 0) {
|
46
|
+
this.gestureHandlersByViewTag.delete(viewTag)
|
47
|
+
this.viewRegistry?.deleteByTag(viewTag)
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
if (gestureHandler.getView()) {
|
52
|
+
// Handler is in "prepared" state which means it is registered in the orchestrator and can
|
53
|
+
// receive touch events. This means that before we remove it from the registry we need to
|
54
|
+
// "cancel" it so that orchestrator does no longer keep a reference to it.
|
55
|
+
gestureHandler.cancel()
|
56
|
+
}
|
57
|
+
this.gestureHandlerByHandlerTag.delete(handlerTag)
|
58
|
+
}
|
59
|
+
|
60
|
+
public getGestureHandlerByHandlerTag(handlerTag: number): GestureHandler {
|
61
|
+
return this.gestureHandlerByHandlerTag.get(handlerTag)
|
62
|
+
}
|
63
|
+
}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
export interface HitSlop {
|
2
|
+
left?: number;
|
3
|
+
right?: number;
|
4
|
+
top?: number;
|
5
|
+
bottom?: number;
|
6
|
+
horizontal?: number;
|
7
|
+
vertical?: number;
|
8
|
+
width?: number;
|
9
|
+
height?: number;
|
10
|
+
}
|
11
|
+
|
12
|
+
const RIGHT = 1;
|
13
|
+
const LEFT = 2;
|
14
|
+
const UP = 4;
|
15
|
+
const DOWN = 8;
|
16
|
+
|
17
|
+
export const Directions = {
|
18
|
+
RIGHT: RIGHT,
|
19
|
+
LEFT: LEFT,
|
20
|
+
UP: UP,
|
21
|
+
DOWN: DOWN,
|
22
|
+
} as const;
|
23
|
+
|
24
|
+
export type Directions = typeof Directions[keyof typeof Directions];
|
25
|
+
|
26
|
+
export const DiagonalDirections = {
|
27
|
+
UP_RIGHT: UP | RIGHT,
|
28
|
+
DOWN_RIGHT: DOWN | RIGHT,
|
29
|
+
UP_LEFT: UP | LEFT,
|
30
|
+
DOWN_LEFT: DOWN | LEFT,
|
31
|
+
} as const;
|
32
|
+
|
33
|
+
export type DiagonalDirections =
|
34
|
+
typeof DiagonalDirections[keyof typeof DiagonalDirections];
|
35
|
+
|
36
|
+
export enum PointerType {
|
37
|
+
NONE = 'none',
|
38
|
+
MOUSE = 'mouse',
|
39
|
+
TOUCH = 'touch',
|
40
|
+
PEN = 'pen',
|
41
|
+
}
|
42
|
+
|
43
|
+
export enum EventType {
|
44
|
+
DOWN = "DOWN",
|
45
|
+
ADDITIONAL_POINTER_DOWN = "ADDITIONAL_POINTER_DOWN",
|
46
|
+
UP = "UP",
|
47
|
+
ADDITIONAL_POINTER_UP = "ADDITIONAL_POINTER_UP",
|
48
|
+
MOVE = "MOVE",
|
49
|
+
ENTER = "ENTER",
|
50
|
+
OUT = "OUT",
|
51
|
+
CANCEL = "CANCEL",
|
52
|
+
}
|
53
|
+
|
54
|
+
export type Touch = {id: number, x: number, y: number, absoluteX: number, absoluteY: number}
|
55
|
+
|
56
|
+
export enum TouchEventType {
|
57
|
+
UNDETERMINED = 0,
|
58
|
+
DOWN = 1,
|
59
|
+
MOVE = 2,
|
60
|
+
UP = 3,
|
61
|
+
CANCELLED = 4,
|
62
|
+
}
|
63
|
+
|
64
|
+
export interface IncomingEvent {
|
65
|
+
x: number;
|
66
|
+
y: number;
|
67
|
+
offsetX: number;
|
68
|
+
offsetY: number;
|
69
|
+
pointerId: number;
|
70
|
+
eventType: EventType;
|
71
|
+
pointerType: PointerType;
|
72
|
+
buttons: number;
|
73
|
+
time: number;
|
74
|
+
allTouches?: Touch[];
|
75
|
+
changedTouches?: Touch[];
|
76
|
+
touchEventType?: TouchEventType;
|
77
|
+
}
|
78
|
+
|
@@ -0,0 +1,144 @@
|
|
1
|
+
import { GestureHandler, Handler, GestureConfig as Config, GHTag } from "./GestureHandler"
|
2
|
+
import { RNGHLogger } from "./RNGHLogger"
|
3
|
+
|
4
|
+
export class InteractionManager {
|
5
|
+
private readonly waitForRelations: Map<GHTag, Set<GHTag>> = new Map()
|
6
|
+
private readonly simultaneousRelations: Map<GHTag, GHTag[]> = new Map()
|
7
|
+
private readonly blocksHandlersRelations: Map<GHTag, GHTag[]> = new Map();
|
8
|
+
|
9
|
+
private logger: RNGHLogger
|
10
|
+
|
11
|
+
constructor(logger: RNGHLogger) {
|
12
|
+
this.logger = logger.cloneAndJoinPrefix("InteractionManager")
|
13
|
+
}
|
14
|
+
|
15
|
+
public configureInteractions(handler: GestureHandler, config: Config) {
|
16
|
+
this.dropRelationsForHandlerWithTag(handler.getTag());
|
17
|
+
|
18
|
+
if (config.waitFor) {
|
19
|
+
const waitFor = new Set<GHTag>();
|
20
|
+
config.waitFor.forEach((otherHandler: Handler): void => {
|
21
|
+
// New API reference
|
22
|
+
if (typeof otherHandler === 'number') {
|
23
|
+
waitFor.add(otherHandler);
|
24
|
+
} else {
|
25
|
+
// Old API reference
|
26
|
+
waitFor.add(otherHandler.handlerTag);
|
27
|
+
}
|
28
|
+
});
|
29
|
+
|
30
|
+
this.waitForRelations.set(handler.getTag(), waitFor);
|
31
|
+
}
|
32
|
+
|
33
|
+
if (config.simultaneousHandlers) {
|
34
|
+
const simultaneousHandlers: number[] = [];
|
35
|
+
config.simultaneousHandlers.forEach((otherHandler: Handler): void => {
|
36
|
+
if (typeof otherHandler === 'number') {
|
37
|
+
simultaneousHandlers.push(otherHandler);
|
38
|
+
} else {
|
39
|
+
simultaneousHandlers.push(otherHandler.handlerTag);
|
40
|
+
}
|
41
|
+
});
|
42
|
+
|
43
|
+
this.simultaneousRelations.set(handler.getTag(), simultaneousHandlers);
|
44
|
+
}
|
45
|
+
|
46
|
+
if (config.blocksHandlers) {
|
47
|
+
const blocksHandlers: number[] = [];
|
48
|
+
config.blocksHandlers.forEach((otherHandler: Handler): void => {
|
49
|
+
if (typeof otherHandler === 'number') {
|
50
|
+
blocksHandlers.push(otherHandler);
|
51
|
+
} else {
|
52
|
+
blocksHandlers.push(otherHandler.handlerTag);
|
53
|
+
}
|
54
|
+
});
|
55
|
+
this.blocksHandlersRelations.set(handler.getTag(), blocksHandlers);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
public shouldWaitForHandlerFailure(
|
60
|
+
handler: GestureHandler,
|
61
|
+
otherHandler: GestureHandler
|
62
|
+
): boolean {
|
63
|
+
const logger = this.logger.cloneAndJoinPrefix(`shouldWaitForHandlerFailure(${handler.getTag()}, ${otherHandler.getTag()})`)
|
64
|
+
const waitFor = this.waitForRelations.get(
|
65
|
+
handler.getTag()
|
66
|
+
);
|
67
|
+
logger.debug({waitFor: Array.from(waitFor ?? [])})
|
68
|
+
if (!waitFor) {
|
69
|
+
logger.debug("false")
|
70
|
+
return false;
|
71
|
+
}
|
72
|
+
|
73
|
+
let shouldWait = false;
|
74
|
+
waitFor.forEach((tag: number): void => {
|
75
|
+
if (tag === otherHandler.getTag()) {
|
76
|
+
shouldWait = true;
|
77
|
+
return; //Returns from callback
|
78
|
+
}
|
79
|
+
});
|
80
|
+
logger.debug(shouldWait)
|
81
|
+
return shouldWait;
|
82
|
+
}
|
83
|
+
|
84
|
+
public shouldRecognizeSimultaneously(
|
85
|
+
handler: GestureHandler,
|
86
|
+
otherHandler: GestureHandler
|
87
|
+
): boolean {
|
88
|
+
const logger = this.logger.cloneAndJoinPrefix(`shouldRecognizeSimultaneously(${handler.getTag()}, ${otherHandler.getTag()})`)
|
89
|
+
const simultaneousHandlers: number[] | undefined =
|
90
|
+
this.simultaneousRelations.get(handler.getTag());
|
91
|
+
if (!simultaneousHandlers) {
|
92
|
+
logger.debug(`false - Handler ${handler.getTag()} doesn't have simultaneousRelations specified`)
|
93
|
+
return false;
|
94
|
+
}
|
95
|
+
let shouldRecognizeSimultaneously = false;
|
96
|
+
simultaneousHandlers.forEach((tag: number): void => {
|
97
|
+
if (tag === otherHandler.getTag()) {
|
98
|
+
shouldRecognizeSimultaneously = true;
|
99
|
+
return;
|
100
|
+
}
|
101
|
+
});
|
102
|
+
logger.debug(`${shouldRecognizeSimultaneously} ${JSON.stringify({ simultaneousHandlers })}`)
|
103
|
+
return shouldRecognizeSimultaneously;
|
104
|
+
}
|
105
|
+
|
106
|
+
public shouldRequireHandlerToWaitForFailure(
|
107
|
+
handler: GestureHandler,
|
108
|
+
otherHandler: GestureHandler
|
109
|
+
): boolean {
|
110
|
+
const waitFor: number[] | undefined = this.blocksHandlersRelations.get(
|
111
|
+
handler.getTag()
|
112
|
+
);
|
113
|
+
|
114
|
+
return (
|
115
|
+
waitFor?.find((tag: number) => {
|
116
|
+
return tag === otherHandler.getTag();
|
117
|
+
}) !== undefined
|
118
|
+
);
|
119
|
+
}
|
120
|
+
|
121
|
+
public shouldHandlerBeCancelledBy(
|
122
|
+
handler: GestureHandler,
|
123
|
+
otherHandler: GestureHandler
|
124
|
+
): boolean {
|
125
|
+
const logger = this.logger.cloneAndJoinPrefix(`shouldHandlerBeCancelledBy(handler=${handler.getTag()}, otherHandler=${otherHandler.getTag()})`)
|
126
|
+
// We check constructor name instead of using `instanceof` in order do avoid circular dependencies
|
127
|
+
// const isNativeHandler =
|
128
|
+
// otherHandler.constructor.name === 'NativeViewGestureHandler';
|
129
|
+
// const isActive = otherHandler.getState() === State.ACTIVE;
|
130
|
+
// const isButton = otherHandler.isButton?.() === true;
|
131
|
+
// return isNativeHandler && isActive && !isButton;
|
132
|
+
return false
|
133
|
+
}
|
134
|
+
|
135
|
+
public dropRelationsForHandlerWithTag(handlerTag: number): void {
|
136
|
+
this.waitForRelations.delete(handlerTag);
|
137
|
+
this.simultaneousRelations.delete(handlerTag);
|
138
|
+
}
|
139
|
+
|
140
|
+
public reset() {
|
141
|
+
this.waitForRelations.clear();
|
142
|
+
this.simultaneousRelations.clear();
|
143
|
+
}
|
144
|
+
}
|