diff --git a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm index d9e55caf1..bd0b5d914 100644 --- a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -84,4 +84,28 @@ using namespace facebook::react; return RCTNSStringFromString(_paragraphLocalData->getAttributedString().getString()); } -@end +- (SharedTouchEventEmitter)touchEventEmitterAtPoint:(CGPoint)point +{ + if (!_paragraphLocalData) { + return _eventEmitter; + } + + SharedTextLayoutManager textLayoutManager = _paragraphLocalData->getTextLayoutManager(); + RCTTextLayoutManager *nativeTextLayoutManager = (__bridge RCTTextLayoutManager *)textLayoutManager->getNativeTextLayoutManager(); + CGRect frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame()); + + SharedShadowNode textShadowNode = [nativeTextLayoutManager getParentShadowNodeWithAttributeString:_paragraphLocalData->getAttributedString() + paragraphAttributes:_paragraphAttributes + frame:frame + atPoint:point]; + + if (!textShadowNode) { + return _eventEmitter; + } + + SharedEventEmitter eventEmitter = textShadowNode->getEventEmitter(); + assert(std::dynamic_pointer_cast(eventEmitter)); + return std::static_pointer_cast(eventEmitter); +} + + @end diff --git a/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.h b/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.h index 8a745aa4a..92e76b283 100644 --- a/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.h +++ b/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.h @@ -27,6 +27,11 @@ NS_ASSUME_NONNULL_BEGIN paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes frame:(CGRect)frame; +- (facebook::react::SharedShadowNode)getParentShadowNodeWithAttributeString:(facebook::react::AttributedString)attributedString + paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes + frame:(CGRect)frame + atPoint:(CGPoint)point; + @end NS_ASSUME_NONNULL_END diff --git a/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm b/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm index 33a2fdb70..3534927bc 100644 --- a/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm +++ b/ReactCommon/fabric/textlayoutmanager/platform/ios/RCTTextLayoutManager.mm @@ -93,4 +93,30 @@ static NSLineBreakMode RCTNSLineBreakModeFromWritingDirection(EllipsizeMode elli return textStorage; } +- (SharedShadowNode)getParentShadowNodeWithAttributeString:(AttributedString)attributedString + paragraphAttributes:(ParagraphAttributes)paragraphAttributes + frame:(CGRect)frame + atPoint:(CGPoint)point { + NSTextStorage *textStorage = + [self _textStorageAndLayoutManagerWithAttributesString:RCTNSAttributedStringFromAttributedString(attributedString) + paragraphAttributes:paragraphAttributes + size:frame.size]; + NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject; + NSTextContainer *textContainer = layoutManager.textContainers.firstObject; + + CGFloat fraction; + NSUInteger characterIndex = [layoutManager characterIndexForPoint:point + inTextContainer:textContainer + fractionOfDistanceBetweenInsertionPoints:&fraction]; + + // If the point is not before (fraction == 0.0) the first character and not + // after (fraction == 1.0) the last character, then the attribute is valid. + if (textStorage.length > 0 && (fraction > 0 || characterIndex > 0) && (fraction < 1 || characterIndex < textStorage.length - 1)) { + RCTSharedShadowNodeWrapper *parentShadowNode = (RCTSharedShadowNodeWrapper *)[textStorage attribute:RCTAttributedStringParentShadowNode atIndex:characterIndex effectiveRange:NULL]; + return parentShadowNode.node; + } + + return nil; +} + @end