Skip to content

Commit 39ada29

Browse files
authored
fix(0.76, macOS): reconcile coordinate systems for mouse events (#2403) (#2405)
1 parent fb188d1 commit 39ada29

File tree

5 files changed

+26
-21
lines changed

5 files changed

+26
-21
lines changed

packages/react-native/React/Base/RCTTouchHandler.m

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ - (void)_recordNewTouches:(NSSet *)touches
128128
#else // [macOS
129129
// -[NSView hitTest:] takes coordinates in a view's superview coordinate system.
130130
// The assumption here is that a RCTUIView/RCTSurfaceView will always have a superview.
131-
CGPoint touchLocation = [self.view.superview convertPoint:touch.locationInWindow fromView:nil];
132-
NSView *targetView = [self.view hitTest:touchLocation];
131+
NSView *superview = [[self view] superview];
132+
const CGPoint touchLocationInSuperview = [superview convertPoint:touch.locationInWindow fromView:nil];
133+
NSView *targetView = [self.view hitTest:touchLocationInSuperview];
133134
// Don't record clicks on scrollbars.
134135
if ([targetView isKindOfClass:[NSScroller class]]) {
135136
continue;
@@ -148,8 +149,7 @@ - (void)_recordNewTouches:(NSSet *)touches
148149
} else {
149150
_shouldSendMouseUpOnSystemBehalf = NO;
150151
}
151-
touchLocation = [targetView convertPoint:touchLocation fromView:self.view.superview];
152-
152+
153153
while (targetView) {
154154
BOOL isUserInteractionEnabled = NO;
155155
if ([((RCTUIView*)targetView) respondsToSelector:@selector(isUserInteractionEnabled)]) { // [macOS]
@@ -161,7 +161,8 @@ - (void)_recordNewTouches:(NSSet *)touches
161161
targetView = targetView.superview;
162162
}
163163

164-
NSNumber *reactTag = [targetView reactTagAtPoint:touchLocation];
164+
const CGPoint touchLocationInSelf = [targetView convertPoint:touchLocationInSuperview fromView:self.view.superview];
165+
NSNumber *reactTag = [targetView reactTagAtPoint:touchLocationInSelf];
165166
BOOL isUserInteractionEnabled = NO;
166167
if ([((RCTUIView*)targetView) respondsToSelector:@selector(isUserInteractionEnabled)]) { // [macOS]
167168
isUserInteractionEnabled = ((RCTUIView*)targetView).isUserInteractionEnabled; // [macOS]

packages/react-native/React/Base/RCTUIKit.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,10 @@ CGPathRef UIBezierPathCreateCGPathRef(UIBezierPath *path);
486486

487487
NS_INLINE RCTPlatformView *RCTUIViewHitTestWithEvent(RCTPlatformView *view, CGPoint point, __unused UIEvent *__nullable event)
488488
{
489-
return [view hitTest:point];
489+
// [macOS IMPORTANT -- point is in local coordinate space, but OSX expects super coordinate space for hitTest:
490+
NSView *superview = [view superview];
491+
NSPoint pointInSuperview = superview != nil ? [view convertPoint:point toView:superview] : point;
492+
return [view hitTest:pointInSuperview];
490493
}
491494

492495
BOOL RCTUIViewSetClipsToBounds(RCTPlatformView *view);

packages/react-native/React/Base/macOS/RCTUIKit.m

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,10 @@ - (void)setTransform:(CGAffineTransform)transform
454454

455455
- (NSView *)hitTest:(NSPoint)point
456456
{
457-
return [self hitTest:NSPointToCGPoint(point) withEvent:nil];
457+
// IMPORTANT point is passed in super coordinates by OSX, but expected to be passed in local coordinates
458+
NSView *superview = [self superview];
459+
NSPoint pointInSelf = superview != nil ? [self convertPoint:point fromView:superview] : point;
460+
return [self hitTest:pointInSelf withEvent:nil];
458461
}
459462

460463
- (BOOL)wantsUpdateLayer
@@ -505,7 +508,11 @@ - (BOOL)becomeFirstResponder
505508

506509
- (NSView *)hitTest:(CGPoint)point withEvent:(__unused UIEvent *)event
507510
{
508-
return self.userInteractionEnabled ? [super hitTest:NSPointFromCGPoint(point)] : nil;
511+
// [macOS
512+
// IMPORTANT point is expected to be passed in local coordinates, but OSX expects point to be super
513+
NSView *superview = [self superview];
514+
NSPoint pointInSuperview = superview != nil ? [self convertPoint:point toView:superview] : point;
515+
return self.userInteractionEnabled ? [super hitTest:pointInSuperview] : nil;
509516
}
510517

511518
- (BOOL)pointInside:(CGPoint)point withEvent:(__unused UIEvent *)event

packages/react-native/React/Views/RCTView.m

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -252,17 +252,8 @@ - (RCTPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event // [macOS
252252
// of the hit view will return YES from -pointInside:withEvent:). See:
253253
// - https://developer.apple.com/library/ios/qa/qa2013/qa1812.html
254254
for (RCTUIView *subview in [sortedSubviews reverseObjectEnumerator]) { // [macOS]
255-
CGPoint pointForHitTest = CGPointZero; // [macOS
256-
#if !TARGET_OS_OSX // [macOS]
257-
pointForHitTest = [subview convertPoint:point fromView:self];
258-
#else // [macOS
259-
if ([subview isKindOfClass:[RCTView class]]) {
260-
pointForHitTest = [subview convertPoint:point fromView:self];
261-
} else {
262-
pointForHitTest = point;
263-
}
264-
#endif // macOS]
265-
hitSubview = RCTUIViewHitTestWithEvent(subview, pointForHitTest, event); // macOS]
255+
CGPoint pointForHitTest = [subview convertPoint:point fromView:self];
256+
hitSubview = RCTUIViewHitTestWithEvent(subview, pointForHitTest, event); // [macOS]
266257
if (hitSubview != nil) {
267258
break;
268259
}

packages/react-native/React/Views/UIView+React.m

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -573,10 +573,13 @@ - (NSString *)react_recursiveDescription
573573

574574
// [macOS
575575
#pragma mark - Hit testing
576-
#if TARGET_OS_OSX
576+
#if TARGET_OS_OSX
577+
// IMPORTANT -- point is in local coordinate space, unlike the hitTest: version from OSX which is in super's coordinate space
577578
- (RCTPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
578579
{
579-
return [self hitTest:point];
580+
NSView *superview = [self superview];
581+
NSPoint pointInSuper = superview != nil ? [self convertPoint:point toView:superview] : point;
582+
return [self hitTest:pointInSuper];
580583
}
581584
#endif // macOS]
582585

0 commit comments

Comments
 (0)