Skip to content

Commit 8b5d4a0

Browse files
authored
Emit coordinates in Native gesture events (#4302)
## Description This PR adds absolute and relative coordinates to `Native` gesture events. It alsol preserves current cancelation logic - based on `pointerInside`. ## Test plan Add `console.log` to `Touchable` implementation and check that coordinates are present.
1 parent 5b70c4b commit 8b5d4a0

6 files changed

Lines changed: 107 additions & 55 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
package com.swmansion.gesturehandler.react.events.eventbuilders
22

33
import com.facebook.react.bridge.WritableMap
4+
import com.facebook.react.uimanager.PixelUtil
45
import com.swmansion.gesturehandler.core.NativeViewGestureHandler
56

67
class NativeGestureHandlerEventDataBuilder(handler: NativeViewGestureHandler) :
78
GestureHandlerEventDataBuilder<NativeViewGestureHandler>(handler) {
89
private val pointerInside: Boolean = handler.isWithinBounds
10+
private val x: Float = handler.lastRelativePositionX
11+
private val y: Float = handler.lastRelativePositionY
12+
private val absoluteX: Float = handler.lastPositionInWindowX
13+
private val absoluteY: Float = handler.lastPositionInWindowY
914

1015
override fun buildEventData(eventData: WritableMap) {
1116
super.buildEventData(eventData)
1217

1318
eventData.putBoolean("pointerInside", pointerInside)
19+
20+
with(eventData) {
21+
putDouble("x", PixelUtil.toDIPFromPixel(x).toDouble())
22+
putDouble("y", PixelUtil.toDIPFromPixel(y).toDouble())
23+
putDouble("absoluteX", PixelUtil.toDIPFromPixel(absoluteX).toDouble())
24+
putDouble("absoluteY", PixelUtil.toDIPFromPixel(absoluteY).toDouble())
25+
}
1426
}
1527
}

packages/react-native-gesture-handler/apple/Handlers/RNNativeViewHandler.mm

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,19 @@ - (void)unbindFromView
197197
[super unbindFromView];
198198
}
199199

200-
- (BOOL)shouldSuppressActiveEvent:(RNGestureHandlerEventExtraData *)extraData
200+
- (RNGestureHandlerEventExtraData *)extraDataForView:(UIView *)sender
201+
event:(UIEvent *)event
202+
pointerInside:(BOOL)pointerInside
201203
{
202-
if (_lastActiveExtraData != nil && [_lastActiveExtraData.data isEqualToDictionary:extraData.data]) {
203-
return YES;
204-
}
205-
206-
_lastActiveExtraData = extraData;
207-
return NO;
204+
RNGHUITouch *touch = [[event allTouches] anyObject];
205+
CGPoint position = touch ? [touch locationInView:sender] : CGPointZero;
206+
CGPoint absolutePosition = touch ? [touch locationInView:nil] : [sender convertPoint:CGPointZero toView:nil];
207+
208+
return [RNGestureHandlerEventExtraData forPointerInside:pointerInside
209+
withPosition:position
210+
withAbsolutePosition:absolutePosition
211+
withNumberOfTouches:event.allTouches.count
212+
withPointerType:_pointerType];
208213
}
209214

210215
- (void)sendActiveStateEventIfChangedForView:(UIView *)sender extraData:(RNGestureHandlerEventExtraData *)extraData
@@ -218,21 +223,19 @@ - (void)sendActiveStateEventIfChangedForView:(UIView *)sender extraData:(RNGestu
218223

219224
- (void)handleSwitch:(UIView *)sender
220225
{
221-
[self sendEventsInState:RNGestureHandlerStateBegan
222-
forViewWithTag:sender.reactTag
223-
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES
224-
withNumberOfTouches:1
225-
withPointerType:_pointerType]];
226-
[self sendEventsInState:RNGestureHandlerStateActive
227-
forViewWithTag:sender.reactTag
228-
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES
229-
withNumberOfTouches:1
230-
withPointerType:_pointerType]];
231-
[self sendEventsInState:RNGestureHandlerStateEnd
232-
forViewWithTag:sender.reactTag
233-
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES
234-
withNumberOfTouches:1
235-
withPointerType:_pointerType]];
226+
CGPoint center = CGPointMake(CGRectGetMidX(sender.bounds), CGRectGetMidY(sender.bounds));
227+
CGPoint absoluteCenter = [sender convertPoint:center toView:nil];
228+
RNGestureHandlerEventExtraData * (^extraData)(void) = ^{
229+
return [RNGestureHandlerEventExtraData forPointerInside:YES
230+
withPosition:center
231+
withAbsolutePosition:absoluteCenter
232+
withNumberOfTouches:1
233+
withPointerType:self->_pointerType];
234+
};
235+
236+
[self sendEventsInState:RNGestureHandlerStateBegan forViewWithTag:sender.reactTag withExtraData:extraData()];
237+
[self sendEventsInState:RNGestureHandlerStateActive forViewWithTag:sender.reactTag withExtraData:extraData()];
238+
[self sendEventsInState:RNGestureHandlerStateEnd forViewWithTag:sender.reactTag withExtraData:extraData()];
236239

237240
[self reset];
238241
}
@@ -262,9 +265,7 @@ - (void)handleTouchDown:(UIView *)sender forEvent:(UIEvent *)event
262265

263266
[self sendEventsInState:RNGestureHandlerStateBegan
264267
forViewWithTag:sender.reactTag
265-
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES
266-
withNumberOfTouches:event.allTouches.count
267-
withPointerType:_pointerType]];
268+
withExtraData:[self extraDataForView:sender event:event pointerInside:YES]];
268269
}
269270

270271
- (void)handleTouchUpOutside:(UIView *)sender forEvent:(UIEvent *)event
@@ -275,16 +276,12 @@ - (void)handleTouchUpOutside:(UIView *)sender forEvent:(UIEvent *)event
275276

276277
[self sendEventsInState:RNGestureHandlerStateEnd
277278
forViewWithTag:sender.reactTag
278-
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO
279-
withNumberOfTouches:event.allTouches.count
280-
withPointerType:_pointerType]];
279+
withExtraData:[self extraDataForView:sender event:event pointerInside:NO]];
281280
}
282281

283282
- (void)handleTouchUpInside:(UIView *)sender forEvent:(UIEvent *)event
284283
{
285-
RNGestureHandlerEventExtraData *extraData = [RNGestureHandlerEventExtraData forPointerInside:YES
286-
withNumberOfTouches:event.allTouches.count
287-
withPointerType:_pointerType];
284+
RNGestureHandlerEventExtraData *extraData = [self extraDataForView:sender event:event pointerInside:YES];
288285

289286
[self sendActiveStateEventIfChangedForView:sender extraData:extraData];
290287
[self sendEventsInState:RNGestureHandlerStateEnd forViewWithTag:sender.reactTag withExtraData:extraData];
@@ -298,26 +295,20 @@ - (void)handleDragExit:(UIView *)sender forEvent:(UIEvent *)event
298295
[control cancelTrackingWithEvent:event];
299296
} else {
300297
[self sendActiveStateEventIfChangedForView:sender
301-
extraData:[RNGestureHandlerEventExtraData forPointerInside:NO
302-
withNumberOfTouches:event.allTouches.count
303-
withPointerType:_pointerType]];
298+
extraData:[self extraDataForView:sender event:event pointerInside:NO]];
304299
}
305300
}
306301

307302
- (void)handleDragEnter:(UIView *)sender forEvent:(UIEvent *)event
308303
{
309304
[self sendActiveStateEventIfChangedForView:sender
310-
extraData:[RNGestureHandlerEventExtraData forPointerInside:YES
311-
withNumberOfTouches:event.allTouches.count
312-
withPointerType:_pointerType]];
305+
extraData:[self extraDataForView:sender event:event pointerInside:YES]];
313306
}
314307

315308
- (void)handleDragInside:(UIView *)sender forEvent:(UIEvent *)event
316309
{
317310
[self sendActiveStateEventIfChangedForView:sender
318-
extraData:[RNGestureHandlerEventExtraData forPointerInside:YES
319-
withNumberOfTouches:event.allTouches.count
320-
withPointerType:_pointerType]];
311+
extraData:[self extraDataForView:sender event:event pointerInside:YES]];
321312
}
322313

323314
- (void)handleDragOutside:(UIView *)sender forEvent:(UIEvent *)event
@@ -327,36 +318,61 @@ - (void)handleDragOutside:(UIView *)sender forEvent:(UIEvent *)event
327318
}
328319

329320
[self sendActiveStateEventIfChangedForView:sender
330-
extraData:[RNGestureHandlerEventExtraData forPointerInside:NO
331-
withNumberOfTouches:event.allTouches.count
332-
withPointerType:_pointerType]];
321+
extraData:[self extraDataForView:sender event:event pointerInside:NO]];
333322
}
334323

335324
- (void)handleTouchCancel:(UIView *)sender forEvent:(UIEvent *)event
336325
{
337326
[self sendEventsInState:RNGestureHandlerStateCancelled
338327
forViewWithTag:sender.reactTag
339-
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO
340-
withNumberOfTouches:event.allTouches.count
341-
withPointerType:_pointerType]];
328+
withExtraData:[self extraDataForView:sender event:event pointerInside:NO]];
342329
}
343330

344331
- (BOOL)wantsToAttachDirectlyToView
345332
{
346333
return YES;
347334
}
348335

336+
#endif
337+
349338
- (void)reset
350339
{
351340
[super reset];
352341
_lastActiveExtraData = nil;
353342
}
354343

355-
#endif
344+
- (BOOL)shouldSuppressActiveEvent:(RNGestureHandlerEventExtraData *)extraData
345+
{
346+
NSDictionary *last = _lastActiveExtraData.data;
347+
NSDictionary *current = extraData.data;
348+
349+
if (last != nil && [last[@"pointerInside"] isEqual:current[@"pointerInside"]] &&
350+
[last[@"numberOfPointers"] isEqual:current[@"numberOfPointers"]] &&
351+
[last[@"pointerType"] isEqual:current[@"pointerType"]]) {
352+
return YES;
353+
}
354+
355+
_lastActiveExtraData = extraData;
356+
return NO;
357+
}
356358

357359
- (RNGestureHandlerEventExtraData *)eventExtraData:(RNDummyGestureRecognizer *)recognizer
358360
{
361+
#if TARGET_OS_OSX
362+
// macOS window coordinates are bottom-left origin; flip Y to match RN's
363+
// top-left origin, mirroring RNGestureHandlerPointerTracker.
364+
CGFloat windowHeight = recognizer.view.window.contentView.frame.size.height;
365+
CGPoint yFlippedAbsolute = [recognizer locationInView:nil];
366+
CGPoint absolutePosition = CGPointMake(yFlippedAbsolute.x, windowHeight - yFlippedAbsolute.y);
367+
CGPoint position = [recognizer.view convertPoint:absolutePosition fromView:recognizer.view.window.contentView];
368+
#else
369+
CGPoint position = [recognizer locationInView:recognizer.view];
370+
CGPoint absolutePosition = [recognizer locationInView:nil];
371+
#endif
372+
359373
return [RNGestureHandlerEventExtraData forPointerInside:[self containsPointInView]
374+
withPosition:position
375+
withAbsolutePosition:absolutePosition
360376
withNumberOfTouches:1
361377
withPointerType:RNGestureHandlerMouse];
362378
}

packages/react-native-gesture-handler/apple/RNGestureHandlerEvents.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
withNumberOfTouches:(NSUInteger)numberOfTouches
5656
withPointerType:(NSInteger)pointerType;
5757
+ (RNGestureHandlerEventExtraData *)forPointerInside:(BOOL)pointerInside
58+
withPosition:(CGPoint)position
59+
withAbsolutePosition:(CGPoint)absolutePosition
5860
withNumberOfTouches:(NSUInteger)numberOfTouches
5961
withPointerType:(NSInteger)pointerType;
6062
@end

packages/react-native-gesture-handler/apple/RNGestureHandlerEvents.mm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,17 @@ + (RNGestureHandlerEventExtraData *)forEventType:(RNGHTouchEventType)eventType
157157
}
158158

159159
+ (RNGestureHandlerEventExtraData *)forPointerInside:(BOOL)pointerInside
160+
withPosition:(CGPoint)position
161+
withAbsolutePosition:(CGPoint)absolutePosition
160162
withNumberOfTouches:(NSUInteger)numberOfTouches
161163
withPointerType:(NSInteger)pointerType
162164
{
163165
return [[RNGestureHandlerEventExtraData alloc] initWithData:@{
164166
@"pointerInside" : @(pointerInside),
167+
@"x" : @(position.x),
168+
@"y" : @(position.y),
169+
@"absoluteX" : @(absolutePosition.x),
170+
@"absoluteY" : @(absolutePosition.y),
165171
@"numberOfPointers" : @(numberOfTouches),
166172
@"pointerType" : @(pointerType)
167173
}];

packages/react-native-gesture-handler/src/v3/hooks/gestures/native/NativeTypes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ export const NativeHandlerNativeProperties = new Set<
4040

4141
export type NativeHandlerData = {
4242
pointerInside: boolean;
43+
x: number;
44+
y: number;
45+
absoluteX: number;
46+
absoluteY: number;
4347
};
4448

4549
export type NativeGestureProperties =

packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Platform } from 'react-native';
22

33
import { type ActionType } from '../../ActionType';
44
import { State } from '../../State';
5-
import { deepEqual } from '../../utils';
65
import type { NativeHandlerData } from '../../v3/hooks/gestures/native/NativeTypes';
76
import type { HandlerData } from '../../v3/types';
87
import { SingleGestureName } from '../../v3/types';
@@ -284,20 +283,33 @@ export default class NativeViewGestureHandler extends GestureHandler {
284283
}
285284

286285
protected override transformNativeEvent(): Record<string, unknown> {
286+
const absolute = this.tracker.getAbsoluteCoordsAverage();
287+
const relative = this.tracker.getRelativeCoordsAverage();
287288
return {
288-
pointerInside: this.delegate.isPointerInBounds(
289-
this.tracker.getAbsoluteCoordsAverage()
290-
),
289+
pointerInside: this.delegate.isPointerInBounds(absolute),
290+
x: relative.x,
291+
y: relative.y,
292+
absoluteX: absolute.x,
293+
absoluteY: absolute.y,
291294
};
292295
}
293296

297+
private arePointerStatesEqual(
298+
a: HandlerData<NativeHandlerData>,
299+
b: HandlerData<NativeHandlerData>
300+
): boolean {
301+
return (
302+
a.pointerInside === b.pointerInside &&
303+
a.numberOfPointers === b.numberOfPointers &&
304+
a.pointerType === b.pointerType
305+
);
306+
}
307+
294308
protected override shouldSuppressActiveUpdate(
295309
handlerData: HandlerData<NativeHandlerData>
296310
): boolean {
297-
if (
298-
this.lastActiveHandlerData &&
299-
deepEqual(this.lastActiveHandlerData, handlerData)
300-
) {
311+
const last = this.lastActiveHandlerData;
312+
if (last && this.arePointerStatesEqual(last, handlerData)) {
301313
return true;
302314
}
303315
this.lastActiveHandlerData = handlerData;

0 commit comments

Comments
 (0)