From 7eb82f32a26a55e72da7d156a0c19bf8b6887ef7 Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Mon, 10 Sep 2018 11:19:28 -0700 Subject: [PATCH] Implement onPress for Virtual Text nodes Summary: When text nodes are nested, as below, `onPress` handlers need to be correctly invoked: ``` render() { return ( console.warn('hi')}> hi console.warn('ramanpreet')}>ramanpreet ); } ``` In the above example, clicking on "hi" should warn "hi", and clicking on "ramanpreet" should warn "ramanpreet". This diff implements that behaviour. Reviewed By: shergin Differential Revision: D9696905 fbshipit-source-id: 2daf24e76c3b3c37aa36cd1540e54876a367faf7 --- .../Text/RCTParagraphComponentView.mm | 26 ++++++++++++++++++- .../platform/ios/RCTTextLayoutManager.h | 5 ++++ .../platform/ios/RCTTextLayoutManager.mm | 26 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) 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