diff --git a/React/Base/RCTUIKit.h b/React/Base/RCTUIKit.h index edc2530e459ca1..ce26abf655d499 100644 --- a/React/Base/RCTUIKit.h +++ b/React/Base/RCTUIKit.h @@ -388,6 +388,7 @@ CGPathRef UIBezierPathCreateCGPathRef(UIBezierPath *path); - (void)layoutIfNeeded; - (void)layoutSubviews; +- (NSArray *)reactZIndexSortedSubviews; // [macOS] - (void)setNeedsDisplay; @@ -410,7 +411,10 @@ CGPathRef UIBezierPathCreateCGPathRef(UIBezierPath *path); * Specifies whether focus ring should be drawn when the view has the first responder status. */ @property (nonatomic, assign) BOOL enableFocusRing; - +/** + * The z-index of the view. + */ +@property (nonatomic, assign) NSInteger reactZIndex; @end diff --git a/React/Base/macOS/RCTUIKit.m b/React/Base/macOS/RCTUIKit.m index 1593137d45c392..4206fb1bc3c022 100644 --- a/React/Base/macOS/RCTUIKit.m +++ b/React/Base/macOS/RCTUIKit.m @@ -402,6 +402,27 @@ - (void)layoutSubviews [super layout]; } +- (NSArray *)reactZIndexSortedSubviews +{ + // Check if sorting is required - in most cases it won't be. + BOOL sortingRequired = NO; + for (RCTUIView *subview in self.subviews) { + if (subview.reactZIndex != 0) { + sortingRequired = YES; + break; + } + } + return sortingRequired ? [self.subviews sortedArrayUsingComparator:^NSComparisonResult(RCTUIView *a, RCTUIView *b) { + if (a.reactZIndex > b.reactZIndex) { + return NSOrderedDescending; + } else { + // Ensure sorting is stable by treating equal zIndex as ascending so + // that original order is preserved. + return NSOrderedAscending; + } + }] : self.subviews; +} + - (void)setNeedsDisplay { self.needsDisplay = YES; diff --git a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index c1b9956913c2e0..5171cf362e7df4 100644 --- a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -447,6 +447,7 @@ - (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(NSSetpointerEvents == PointerEventsMode::None || _props->pointerEvents == PointerEventsMode::BoxOnly); + if (needsHitSubview && (![self clipsToBounds] || isPointInside)) { + // Take z-index into account when calculating the touch target. + NSArray *sortedSubviews = [self reactZIndexSortedSubviews]; // [macOS] + + // The default behaviour of UIKit is that if a view does not contain a point, + // then no subviews will be returned from hit testing, even if they contain + // the hit point. By doing hit testing directly on the subviews, we bypass + // the strict containment policy (i.e., UIKit guarantees that every ancestor + // of the hit view will return YES from -pointInside:withEvent:). See: + // - https://developer.apple.com/library/ios/qa/qa2013/qa1812.html + for (RCTUIView *subview in [sortedSubviews reverseObjectEnumerator]) { // [macOS] + CGPoint pointForHitTest = CGPointZero; + if ([subview isKindOfClass:[RCTUIView class]]) { // [macOS] + pointForHitTest = [subview convertPoint:point fromView:self]; + } else { + pointForHitTest = point; + } + hitSubview = (RCTUIView *)[subview hitTest:pointForHitTest]; // [macOS] + if (hitSubview != nil) { + break; + } + } + } + + RCTUIView *hitView = (isPointInside ? self : nil); // [macOS] + + switch (_props->pointerEvents) { + case PointerEventsMode::None: + return nil; + case PointerEventsMode::Auto: + return hitSubview ?: hitView; + case PointerEventsMode::BoxOnly: + return hitView; + case PointerEventsMode::BoxNone: + return hitSubview; + default: + return hitSubview ?: hitView; + } +} +#endif // macOS] static RCTCornerRadii RCTCornerRadiiFromBorderRadii(BorderRadii borderRadii) { diff --git a/React/Fabric/Mounting/RCTComponentViewDescriptor.h b/React/Fabric/Mounting/RCTComponentViewDescriptor.h index 62e3a5041837f5..18ccfbd2822398 100644 --- a/React/Fabric/Mounting/RCTComponentViewDescriptor.h +++ b/React/Fabric/Mounting/RCTComponentViewDescriptor.h @@ -22,7 +22,6 @@ class RCTComponentViewDescriptor final { * Associated (and owned) native view instance. */ __strong RCTUIView *view = nil; // [macOS] - NSInteger tag = 0; // [macOS] default is 0 /* * Indicates a requirement to call on the view methods from * `RCTMountingTransactionObserving` protocol. diff --git a/React/Fabric/Mounting/RCTComponentViewProtocol.h b/React/Fabric/Mounting/RCTComponentViewProtocol.h index c19b8592212141..0cc6fdaef2e9d4 100644 --- a/React/Fabric/Mounting/RCTComponentViewProtocol.h +++ b/React/Fabric/Mounting/RCTComponentViewProtocol.h @@ -119,6 +119,9 @@ typedef NS_OPTIONS(NSInteger, RNComponentViewUpdateMask) { - (BOOL)isJSResponder; - (void)setIsJSResponder:(BOOL)isJSResponder; +- (NSNumber *)reactTag; // [macOS] +- (void)setReactTag:(NSNumber *)reactTag; // [macOS] + /* * This is broken. Do not use. */ diff --git a/React/Fabric/Mounting/RCTComponentViewRegistry.mm b/React/Fabric/Mounting/RCTComponentViewRegistry.mm index 97bf26f217b698..dc2be14f66af7c 100644 --- a/React/Fabric/Mounting/RCTComponentViewRegistry.mm +++ b/React/Fabric/Mounting/RCTComponentViewRegistry.mm @@ -88,7 +88,7 @@ - (void)preallocateViewComponents #if !TARGET_OS_OSX // [macOS] componentViewDescriptor.view.tag = tag; #else // [macOS - componentViewDescriptor.tag = tag; + componentViewDescriptor.view.reactTag = @(tag); #endif // macOS] auto it = _registry.insert({tag, componentViewDescriptor}); return it.first->second; @@ -107,7 +107,7 @@ - (void)enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle #if !TARGET_OS_OSX // [macOS] componentViewDescriptor.view.tag = 0; #else // [macOS - componentViewDescriptor.tag = 0; + componentViewDescriptor.view.reactTag = @0; #endif // macOS] [self _enqueueComponentViewWithComponentHandle:componentHandle componentViewDescriptor:componentViewDescriptor]; } diff --git a/React/Fabric/Mounting/UIView+ComponentViewProtocol.h b/React/Fabric/Mounting/UIView+ComponentViewProtocol.h index a02c67ed30d2c0..ab4eec1ed4e1d1 100644 --- a/React/Fabric/Mounting/UIView+ComponentViewProtocol.h +++ b/React/Fabric/Mounting/UIView+ComponentViewProtocol.h @@ -41,6 +41,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)setIsJSResponder:(BOOL)isJSResponder; +- (NSNumber *)reactTag; // [macOS] +- (void)setReactTag:(NSNumber *)reactTag; // [macOS] + - (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(nullable NSSet *)props; - (nullable NSSet *)propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN; diff --git a/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm b/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm index 2bbb85fcc147d8..e034bc8b3b9f6b 100644 --- a/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm +++ b/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm @@ -7,6 +7,8 @@ #import "UIView+ComponentViewProtocol.h" +#import // [macOS] + #import #import #import @@ -152,6 +154,16 @@ - (void)setIsJSResponder:(BOOL)isJSResponder // Default implementation does nothing. } +- (NSNumber *)reactTag +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setReactTag:(NSNumber *)reactTag +{ + objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + - (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(nullable NSSet *)propKeys { // Default implementation does nothing. diff --git a/React/Fabric/RCTSurfaceTouchHandler.mm b/React/Fabric/RCTSurfaceTouchHandler.mm index 01ec9be572122e..f0574ea63aa438 100644 --- a/React/Fabric/RCTSurfaceTouchHandler.mm +++ b/React/Fabric/RCTSurfaceTouchHandler.mm @@ -7,7 +7,6 @@ #import "RCTSurfaceTouchHandler.h" -#import // [macOS] #import #import @@ -80,7 +79,7 @@ bool operator()(const ActiveTouch &lhs, const ActiveTouch &rhs) const static void UpdateActiveTouchWithUITouch( ActiveTouch &activeTouch, - RCTUITouch *uiTouch, // [macOS] + RCTUITouch *uiTouch, RCTUIView *rootComponentView, CGPoint rootViewOriginOffset) // [macOS] { @@ -88,6 +87,11 @@ static void UpdateActiveTouchWithUITouch( CGPoint offsetPoint = [uiTouch locationInView:activeTouch.componentView]; CGPoint screenPoint = [uiTouch locationInView:uiTouch.window]; CGPoint pagePoint = [uiTouch locationInView:rootComponentView]; +#else // [macOS + CGPoint offsetPoint = [activeTouch.componentView convertPoint:uiTouch.locationInWindow fromView:nil]; + CGPoint screenPoint = uiTouch.locationInWindow; + CGPoint pagePoint = CGPointMake(screenPoint.x, CGRectGetHeight(rootComponentView.window.frame) - screenPoint.y); +#endif // macOS] pagePoint = CGPointMake(pagePoint.x + rootViewOriginOffset.x, pagePoint.y + rootViewOriginOffset.y); activeTouch.touch.offsetPoint = RCTPointFromCGPoint(offsetPoint); @@ -96,24 +100,59 @@ static void UpdateActiveTouchWithUITouch( activeTouch.touch.timestamp = uiTouch.timestamp; +#if !TARGET_OS_OSX // [macOS] if (RCTForceTouchAvailable()) { activeTouch.touch.force = RCTZeroIfNaN(uiTouch.force / uiTouch.maximumPossibleForce); } -#endif // [macOS] +#else // [macOS + NSEventType type = uiTouch.type; + if (type == NSEventTypeLeftMouseDown || type == NSEventTypeLeftMouseUp || type == NSEventTypeLeftMouseDragged) { + activeTouch.touch.button = 0; + } else if (type == NSEventTypeRightMouseDown || type == NSEventTypeRightMouseUp || type == NSEventTypeRightMouseDragged) { + activeTouch.touch.button = 2; + } + + NSEventModifierFlags modifierFlags = uiTouch.modifierFlags; + if (modifierFlags & NSEventModifierFlagOption) { + activeTouch.touch.altKey = true; + } + if (modifierFlags & NSEventModifierFlagCommand) { + activeTouch.touch.ctrlKey = true; + } + if (modifierFlags & NSEventModifierFlagShift) { + activeTouch.touch.shiftKey = true; + } + if (modifierFlags & NSEventModifierFlagCommand) { + activeTouch.touch.metaKey = true; + } +#endif // macOS] } static ActiveTouch CreateTouchWithUITouch(RCTUITouch *uiTouch, RCTUIView *rootComponentView, CGPoint rootViewOriginOffset) // [macOS] { ActiveTouch activeTouch = {}; -#if !TARGET_OS_OSX // [macOS] // Find closest Fabric-managed touchable view +#if !TARGET_OS_OSX // [macOS] RCTUIView *componentView = uiTouch.view; // [macOS] +#else // [macOS + CGPoint touchLocation = [rootComponentView.superview convertPoint:uiTouch.locationInWindow fromView:nil]; + RCTUIView *componentView = (RCTUIView *) [rootComponentView hitTest:touchLocation]; // [macOS] +#endif // macOS] while (componentView) { +#if !TARGET_OS_OSX // [macOS] + CGPoint offsetPoint = [uiTouch locationInView:componentView]; +#else // [macOS + CGPoint offsetPoint = [componentView convertPoint:uiTouch.locationInWindow fromView:nil]; +#endif // macOS] if ([componentView respondsToSelector:@selector(touchEventEmitterAtPoint:)]) { activeTouch.eventEmitter = [(id)componentView - touchEventEmitterAtPoint:[uiTouch locationInView:componentView]]; + touchEventEmitterAtPoint:offsetPoint]; +#if !TARGET_OS_OSX // [macOS] activeTouch.touch.target = (Tag)componentView.tag; +#else // [macOS + activeTouch.touch.target = (Tag)(componentView.reactTag.intValue); +#endif // macOS] activeTouch.componentView = componentView; break; } @@ -121,18 +160,16 @@ static ActiveTouch CreateTouchWithUITouch(RCTUITouch *uiTouch, RCTUIView *rootCo } UpdateActiveTouchWithUITouch(activeTouch, uiTouch, rootComponentView, rootViewOriginOffset); -#endif // [macOS] return activeTouch; } +#if !TARGET_OS_OSX // [macOS] static BOOL AllTouchesAreCancelledOrEnded(NSSet *touches) // [macOS] { for (RCTUITouch *touch in touches) { // [macOS] -#if !TARGET_OS_OSX // [macOS] if (touch.phase == UITouchPhaseBegan || touch.phase == UITouchPhaseMoved || touch.phase == UITouchPhaseStationary) { return NO; } -#endif // [macOS] } return YES; } @@ -140,14 +177,13 @@ static BOOL AllTouchesAreCancelledOrEnded(NSSet *touches) // [macO static BOOL AnyTouchesChanged(NSSet *touches) // [macOS] { for (RCTUITouch *touch in touches) { // [macOS] -#if !TARGET_OS_OSX // [macOS] if (touch.phase == UITouchPhaseBegan || touch.phase == UITouchPhaseMoved) { return YES; } -#endif // [macOS] } return NO; } +#endif // [macOS] /** * Surprisingly, `__unsafe_unretained id` pointers are not regular pointers @@ -167,8 +203,12 @@ @interface RCTSurfaceTouchHandler () @end @implementation RCTSurfaceTouchHandler { +#if !TARGET_OS_OSX // [macOS] std::unordered_map<__unsafe_unretained RCTUITouch *, ActiveTouch, PointerHasher<__unsafe_unretained RCTUITouch *>> - _activeTouches; // [macOS] + _activeTouches; +#else // [macOS + std::unordered_map _activeTouches; +#endif // macOS] /* * We hold the view weakly to prevent a retain cycle. @@ -220,14 +260,22 @@ - (void)_registerTouches:(NSSet *)touches // [macOS] for (RCTUITouch *touch in touches) { // [macOS] auto activeTouch = CreateTouchWithUITouch(touch, _rootComponentView, _viewOriginOffset); activeTouch.touch.identifier = _identifierPool.dequeue(); +#if !TARGET_OS_OSX // [macOS] _activeTouches.emplace(touch, activeTouch); +#else // [macOS + _activeTouches.emplace(touch.eventNumber, activeTouch); +#endif // macOS] } } - (void)_updateTouches:(NSSet *)touches // [macOS] { for (RCTUITouch *touch in touches) { // [macOS] +#if !TARGET_OS_OSX // [macOS] auto iterator = _activeTouches.find(touch); +#else // [macOS + auto iterator = _activeTouches.find(touch.eventNumber); +#endif // macOS] assert(iterator != _activeTouches.end() && "Inconsistency between local and UIKit touch registries"); if (iterator == _activeTouches.end()) { continue; @@ -240,14 +288,22 @@ - (void)_updateTouches:(NSSet *)touches // [macOS] - (void)_unregisterTouches:(NSSet *)touches // [macOS] { for (RCTUITouch *touch in touches) { // [macOS] +#if !TARGET_OS_OSX // [macOS] auto iterator = _activeTouches.find(touch); +#else // [macOS + auto iterator = _activeTouches.find(touch.eventNumber); +#endif // macOS] assert(iterator != _activeTouches.end() && "Inconsistency between local and UIKit touch registries"); if (iterator == _activeTouches.end()) { continue; } auto &activeTouch = iterator->second; _identifierPool.enqueue(activeTouch.touch.identifier); +#if !TARGET_OS_OSX // [macOS] _activeTouches.erase(touch); +#else // [macOS + _activeTouches.erase(touch.eventNumber); +#endif // macOS] } } @@ -256,8 +312,12 @@ - (void)_unregisterTouches:(NSSet *)touches // [macOS] std::vector activeTouches; activeTouches.reserve(touches.count); - for (RCTUITouch *touch in touches) { // [macOS] + for (RCTUITouch *touch in touches) { +#if !TARGET_OS_OSX // [macOS] auto iterator = _activeTouches.find(touch); +#else // [macOS + auto iterator = _activeTouches.find(touch.eventNumber); +#endif // macOS] assert(iterator != _activeTouches.end() && "Inconsistency between local and UIKit touch registries"); if (iterator == _activeTouches.end()) { continue; @@ -325,9 +385,10 @@ - (void)_dispatchActiveTouches:(std::vector)activeTouches eventType #pragma mark - `UIResponder`-ish touch-delivery methods +#if !TARGET_OS_OSX // [macOS] + - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event // [macOS] { -#if !TARGET_OS_OSX // [macOS] [super touchesBegan:touches withEvent:event]; [self _registerTouches:touches]; @@ -338,24 +399,20 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event / } else if (self.state == UIGestureRecognizerStateBegan) { self.state = UIGestureRecognizerStateChanged; } -#endif // [macOS] } -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event // [macOS] +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { -#if !TARGET_OS_OSX // [macOS] [super touchesMoved:touches withEvent:event]; [self _updateTouches:touches]; [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchMove]; self.state = UIGestureRecognizerStateChanged; -#endif // [macOS] } -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event // [macOS] +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { -#if !TARGET_OS_OSX // [macOS] [super touchesEnded:touches withEvent:event]; [self _updateTouches:touches]; @@ -367,12 +424,10 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event / } else if (AnyTouchesChanged(event.allTouches)) { self.state = UIGestureRecognizerStateChanged; } -#endif // [macOS] } -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event // [macOS] +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { -#if !TARGET_OS_OSX // [macOS] [super touchesCancelled:touches withEvent:event]; [self _updateTouches:touches]; @@ -384,9 +439,99 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)eve } else if (AnyTouchesChanged(event.allTouches)) { self.state = UIGestureRecognizerStateChanged; } -#endif // [macOS] } +#else // [macOS + +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + // This will only be called if the hit-tested view returns YES for acceptsFirstMouse, + // therefore asking it again would be redundant. + return YES; +} + +- (void)mouseDown:(NSEvent *)event +{ + [super mouseDown:event]; + + { + NSSet* touches = [NSSet setWithObject:event]; + [self _registerTouches:touches]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchStart]; + + if (self.state == NSGestureRecognizerStatePossible) { + self.state = NSGestureRecognizerStateBegan; + } else if (self.state == NSGestureRecognizerStateBegan) { + self.state = NSGestureRecognizerStateChanged; + } + } +} + +- (void)rightMouseDown:(NSEvent *)event +{ + [super rightMouseDown:event]; + + { + NSSet* touches = [NSSet setWithObject:event]; + [self _registerTouches:touches]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchStart]; + + if (self.state == NSGestureRecognizerStatePossible) { + self.state = NSGestureRecognizerStateBegan; + } else if (self.state == NSGestureRecognizerStateBegan) { + self.state = NSGestureRecognizerStateChanged; + } + } +} + +- (void)mouseDragged:(NSEvent *)event +{ + [super mouseDragged:event]; + + NSSet* touches = [NSSet setWithObject:event]; + [self _updateTouches:touches]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchMove]; + + self.state = NSGestureRecognizerStateChanged; +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [super rightMouseDragged:event]; + + NSSet* touches = [NSSet setWithObject:event]; + [self _updateTouches:touches]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchMove]; + + self.state = NSGestureRecognizerStateChanged; +} + +- (void)mouseUp:(NSEvent *)event +{ + [super mouseUp:event]; + + NSSet* touches = [NSSet setWithObject:event]; + [self _updateTouches:touches]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchEnd]; + [self _unregisterTouches:touches]; + + self.state = NSGestureRecognizerStateEnded; +} + +- (void)rightMouseUp:(NSEvent *)event +{ + [super rightMouseUp:event]; + + NSSet* touches = [NSSet setWithObject:event]; + [self _updateTouches:touches]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchEnd]; + [self _unregisterTouches:touches]; + + self.state = NSGestureRecognizerStateEnded; +} + +#endif // macOS] + - (void)reset { [super reset]; diff --git a/ReactCommon/react/renderer/components/view/Touch.cpp b/ReactCommon/react/renderer/components/view/Touch.cpp index 15e5bfaaee55a9..ea52897146a08d 100644 --- a/ReactCommon/react/renderer/components/view/Touch.cpp +++ b/ReactCommon/react/renderer/components/view/Touch.cpp @@ -27,6 +27,11 @@ std::vector getDebugProps( {"target", getDebugDescription(touch.target, options)}, {"force", getDebugDescription(touch.force, options)}, {"timestamp", getDebugDescription(touch.timestamp, options)}, + {"button", getDebugDescription(touch.button, options)}, // [macOS] + {"altKey", getDebugDescription(touch.altKey, options)}, // [macOS] + {"ctrlKey", getDebugDescription(touch.ctrlKey, options)}, // [macOS] + {"shiftKey", getDebugDescription(touch.shiftKey, options)}, // [macOS] + {"metaKey", getDebugDescription(touch.metaKey, options)}, // [macOS] }; } diff --git a/ReactCommon/react/renderer/components/view/Touch.h b/ReactCommon/react/renderer/components/view/Touch.h index 13383a0e5bbcb5..89c557b64a74a1 100644 --- a/ReactCommon/react/renderer/components/view/Touch.h +++ b/ReactCommon/react/renderer/components/view/Touch.h @@ -56,6 +56,33 @@ struct Touch { */ Float timestamp; + // [macOS + /* + * The button indicating which pointer is used. + */ + int button; + + /* + * A flag indicating if the alt key is pressed. + */ + bool altKey; + + /* + * A flag indicating if the control key is pressed. + */ + bool ctrlKey; + + /* + * A flag indicating if the shift key is pressed. + */ + bool shiftKey; + + /* + * A flag indicating if the meta key is pressed. + */ + bool metaKey; + // macOS] + /* * The particular implementation of `Hasher` and (especially) `Comparator` * make sense only when `Touch` object is used as a *key* in indexed diff --git a/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp b/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp index d94144c8715b66..870336868c9df7 100644 --- a/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp @@ -26,6 +26,11 @@ static void setTouchPayloadOnObject( object.setProperty(runtime, "target", touch.target); object.setProperty(runtime, "timestamp", touch.timestamp * 1000); object.setProperty(runtime, "force", touch.force); + object.setProperty(runtime, "button", touch.button); // [macOS] + object.setProperty(runtime, "altKey", touch.altKey); // [macOS] + object.setProperty(runtime, "ctrlKey", touch.ctrlKey); // [macOS] + object.setProperty(runtime, "shiftKey", touch.shiftKey); // [macOS] + object.setProperty(runtime, "metaKey", touch.metaKey); // [macOS] } static jsi::Value touchesPayload(