From 0c150b2289818267c940b6e726ec2f7725659817 Mon Sep 17 00:00:00 2001 From: Vincent Riemer Date: Wed, 18 Jan 2023 13:43:21 -0800 Subject: [PATCH] Add explicit support for Apple Pencil hovering Summary: Changelog: [IOS][ADDED] - Add explicit support for M2 iPad Apple Pencil hovering in the Pointer Events implementation I personally am not a huge fan of how this has to be implemented but it's literally the way Apple suggests to differentiate between mouse and pen hover events. Reviewed By: lunaleaps Differential Revision: D40962916 fbshipit-source-id: a6fb5bcfe19bcf500021922baac3fa7c4375a874 --- React/Fabric/RCTSurfaceTouchHandler.mm | 57 ++++++++++++++++++-------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/React/Fabric/RCTSurfaceTouchHandler.mm b/React/Fabric/RCTSurfaceTouchHandler.mm index c6b7fd9dcaf8ff..1fd0fda0fffed3 100644 --- a/React/Fabric/RCTSurfaceTouchHandler.mm +++ b/React/Fabric/RCTSurfaceTouchHandler.mm @@ -374,18 +374,17 @@ static PointerEvent CreatePointerEventFromActiveTouch(ActiveTouch activeTouch, R } static PointerEvent CreatePointerEventFromIncompleteHoverData( + int pointerId, + std::string pointerType, CGPoint clientLocation, CGPoint screenLocation, CGPoint offsetLocation, UIKeyModifierFlags modifierFlags) { PointerEvent event = {}; - // "touch" events produced from a mouse cursor on iOS always have the ID 0 so - // we can just assume that here since these sort of hover events only ever come - // from the mouse - event.pointerId = kMousePointerId; + event.pointerId = pointerId; event.pressure = 0.0; - event.pointerType = "mouse"; + event.pointerType = pointerType; event.clientPoint = RCTPointFromCGPoint(clientLocation); event.screenPoint = RCTPointFromCGPoint(screenLocation); event.offsetPoint = RCTPointFromCGPoint(offsetLocation); @@ -473,7 +472,9 @@ @implementation RCTSurfaceTouchHandler { __weak UIView *_rootComponentView; IdentifierPool<11> _identifierPool; - UIHoverGestureRecognizer *_hoverRecognizer API_AVAILABLE(ios(13.0)); + UIHoverGestureRecognizer *_mouseHoverRecognizer API_AVAILABLE(ios(13.0)); + UIHoverGestureRecognizer *_penHoverRecognizer API_AVAILABLE(ios(13.0)); + NSMutableDictionary *> *_currentlyHoveredViewsPerPointer; int _primaryTouchPointerId; @@ -492,7 +493,9 @@ - (instancetype)init self.delegate = self; - _hoverRecognizer = nil; + _mouseHoverRecognizer = nil; + _penHoverRecognizer = nil; + _currentlyHoveredViewsPerPointer = [[NSMutableDictionary alloc] init]; _primaryTouchPointerId = -1; } @@ -510,9 +513,14 @@ - (void)attachToView:(UIView *)view _rootComponentView = view; if (RCTGetDispatchW3CPointerEvents()) { - if (@available(iOS 13.0, *)) { - _hoverRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(hovering:)]; - [view addGestureRecognizer:_hoverRecognizer]; + if (@available(iOS 13.4, *)) { + _mouseHoverRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(mouseHovering:)]; + _mouseHoverRecognizer.allowedTouchTypes = @[ @(UITouchTypeIndirectPointer) ]; + [view addGestureRecognizer:_mouseHoverRecognizer]; + + _penHoverRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(penHovering:)]; + _penHoverRecognizer.allowedTouchTypes = @[ @(UITouchTypePencil) ]; + [view addGestureRecognizer:_penHoverRecognizer]; } } } @@ -525,9 +533,14 @@ - (void)detachFromView:(UIView *)view [view removeGestureRecognizer:self]; _rootComponentView = nil; - if (_hoverRecognizer != nil) { - [view removeGestureRecognizer:_hoverRecognizer]; - _hoverRecognizer = nil; + if (_mouseHoverRecognizer != nil) { + [view removeGestureRecognizer:_mouseHoverRecognizer]; + _mouseHoverRecognizer = nil; + } + + if (_penHoverRecognizer != nil) { + [view removeGestureRecognizer:_penHoverRecognizer]; + _penHoverRecognizer = nil; } } @@ -845,7 +858,19 @@ - (void)_cancelTouches [self setEnabled:YES]; } -- (void)hovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0)) +- (void)penHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0)) +{ + [self hovering:recognizer pointerId:kPencilPointerId pointerType:"pen"]; +} + +- (void)mouseHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0)) +{ + [self hovering:recognizer pointerId:kMousePointerId pointerType:"mouse"]; +} + +- (void)hovering:(UIHoverGestureRecognizer *)recognizer + pointerId:(int)pointerId + pointerType:(std::string)pointerType API_AVAILABLE(ios(13.0)) { UIView *listenerView = recognizer.view; CGPoint clientLocation = [recognizer locationInView:listenerView]; @@ -864,8 +889,8 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0)) modifierFlags = 0; } - PointerEvent event = - CreatePointerEventFromIncompleteHoverData(clientLocation, screenLocation, offsetLocation, modifierFlags); + PointerEvent event = CreatePointerEventFromIncompleteHoverData( + pointerId, pointerType, clientLocation, screenLocation, offsetLocation, modifierFlags); NSOrderedSet *eventPathViews = [self handleIncomingPointerEvent:event onView:targetView]; SharedTouchEventEmitter eventEmitter = GetTouchEmitterFromView(targetView, offsetLocation);