From 83d7b4a3fc4309e6a353f2867959f01b43f5457d Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 12 May 2023 01:20:28 +0500 Subject: [PATCH 01/19] demo implementation of substring measurement calculation on ios --- .../Libraries/Text/Text/RCTTextShadowView.h | 1 + .../Libraries/Text/Text/RCTTextViewManager.m | 1 + .../Libraries/Text/TextNativeComponent.js | 1 + .../react-native/Libraries/Text/TextProps.js | 5 ++++ .../react/views/text/ReactTextShadowNode.java | 8 ++++++ .../components/text/ParagraphEventEmitter.cpp | 18 ++++++++++--- .../components/text/ParagraphEventEmitter.h | 3 ++- .../text/ParagraphLayoutManager.cpp | 7 ++--- .../components/text/ParagraphLayoutManager.h | 5 ++-- .../components/text/ParagraphProps.cpp | 9 +++++++ .../renderer/components/text/ParagraphProps.h | 6 +++++ .../components/text/ParagraphShadowNode.cpp | 10 ++++--- .../textlayoutmanager/TextMeasureCache.h | 6 +++++ .../textlayoutmanager/RCTTextLayoutManager.h | 8 +++--- .../textlayoutmanager/RCTTextLayoutManager.mm | 27 ++++++++++++++++--- .../textlayoutmanager/TextLayoutManager.h | 5 ++-- .../textlayoutmanager/TextLayoutManager.mm | 14 +++++++--- 17 files changed, 107 insertions(+), 27 deletions(-) diff --git a/packages/react-native/Libraries/Text/Text/RCTTextShadowView.h b/packages/react-native/Libraries/Text/Text/RCTTextShadowView.h index f3202b5fd37315..e67ae498470f3d 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextShadowView.h +++ b/packages/react-native/Libraries/Text/Text/RCTTextShadowView.h @@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL adjustsFontSizeToFit; @property (nonatomic, assign) CGFloat minimumFontScale; @property (nonatomic, copy) RCTDirectEventBlock onTextLayout; +@property (nonatomic, copy) NSArray *textLayoutConfig; - (void)uiManagerWillPerformMounting; diff --git a/packages/react-native/Libraries/Text/Text/RCTTextViewManager.m b/packages/react-native/Libraries/Text/Text/RCTTextViewManager.m index 7b53c80d557acc..171312ccdd73fd 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextViewManager.m +++ b/packages/react-native/Libraries/Text/Text/RCTTextViewManager.m @@ -32,6 +32,7 @@ @implementation RCTTextViewManager { RCT_REMAP_SHADOW_PROPERTY(minimumFontScale, minimumFontScale, CGFloat) RCT_EXPORT_SHADOW_PROPERTY(onTextLayout, RCTDirectEventBlock) +RCT_EXPORT_SHADOW_PROPERTY(textLayoutConfig, NSArray) RCT_EXPORT_VIEW_PROPERTY(selectable, BOOL) diff --git a/packages/react-native/Libraries/Text/TextNativeComponent.js b/packages/react-native/Libraries/Text/TextNativeComponent.js index 812f334806eb73..ff1a798289632c 100644 --- a/packages/react-native/Libraries/Text/TextNativeComponent.js +++ b/packages/react-native/Libraries/Text/TextNativeComponent.js @@ -44,6 +44,7 @@ const textViewConfig = { minimumFontScale: true, textBreakStrategy: true, onTextLayout: true, + textLayoutConfig: true, onInlineViewLayout: true, dataDetectorType: true, android_hyphenationFrequency: true, diff --git a/packages/react-native/Libraries/Text/TextProps.js b/packages/react-native/Libraries/Text/TextProps.js index bb9348f6a55e4f..c22728c862ba4a 100644 --- a/packages/react-native/Libraries/Text/TextProps.js +++ b/packages/react-native/Libraries/Text/TextProps.js @@ -168,6 +168,11 @@ export type TextProps = $ReadOnly<{| onMoveShouldSetResponder?: ?() => boolean, onTextLayout?: ?(event: TextLayoutEvent) => mixed, + /** + * Regions for text layout tracking + */ + textLayoutConfig?: ?$ReadOnlyArray, + /** * Defines how far your touch may move off of the button, before * deactivating the button. diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java index 419eadb7450a4b..89c654c2ac43ae 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java @@ -24,6 +24,7 @@ import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.ReadableArray; import com.facebook.react.uimanager.NativeViewHierarchyOptimizer; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactShadowNode; @@ -40,6 +41,7 @@ import com.facebook.yoga.YogaMeasureOutput; import com.facebook.yoga.YogaNode; import java.util.ArrayList; +import com.facebook.common.logging.FLog; /** * {@link ReactBaseTextShadowNode} concrete class for anchor {@code Text} node. @@ -58,6 +60,7 @@ public class ReactTextShadowNode extends ReactBaseTextShadowNode { private @Nullable Spannable mPreparedSpannableText; private boolean mShouldNotifyOnTextLayout; + private ReadableArray mTextLayoutConfig; private final YogaMeasureFunction mTextMeasureFunction = new YogaMeasureFunction() { @@ -357,6 +360,11 @@ public void setShouldNotifyOnTextLayout(boolean shouldNotifyOnTextLayout) { mShouldNotifyOnTextLayout = shouldNotifyOnTextLayout; } + @ReactProp(name = "textLayoutConfig") + public void setTextLayoutConfig(ReadableArray textLayoutConfig) { + mTextLayoutConfig = textLayoutConfig; + } + @Override public Iterable calculateLayoutOnChildren() { // Run flexbox on and return the descendants which are inline views. diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp index 3ae6e664598cd8..829f1cb22b00c0 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp @@ -11,9 +11,11 @@ namespace facebook::react { static jsi::Value linesMeasurementsPayload( jsi::Runtime &runtime, - LinesMeasurements const &linesMeasurements) { + LinesMeasurements const &linesMeasurements, + RegionsMeasurements const ®ionsMeasurements) { auto payload = jsi::Object(runtime); auto lines = jsi::Array(runtime, linesMeasurements.size()); + auto regions = jsi::Array(runtime, regionsMeasurements.size()); for (size_t i = 0; i < linesMeasurements.size(); ++i) { auto const &lineMeasurement = linesMeasurements[i]; @@ -29,24 +31,32 @@ static jsi::Value linesMeasurementsPayload( jsiLine.setProperty(runtime, "xHeight", lineMeasurement.xHeight); lines.setValueAtIndex(runtime, i, jsiLine); } + + if (regionsMeasurements.size() == 2) { + regions.setValueAtIndex(runtime, 0, regionsMeasurements[0]); + regions.setValueAtIndex(runtime, 1, regionsMeasurements[1]); + } payload.setProperty(runtime, "lines", lines); + payload.setProperty(runtime, "regions", regions); return payload; } void ParagraphEventEmitter::onTextLayout( - LinesMeasurements const &linesMeasurements) const { + LinesMeasurements const &linesMeasurements, + RegionsMeasurements const ®ionsMeasurements) const { { std::lock_guard guard(linesMeasurementsMutex_); if (linesMeasurementsMetrics_ == linesMeasurements) { return; } linesMeasurementsMetrics_ = linesMeasurements; + regionsMeasurementsMetrics_ = regionsMeasurements; } - dispatchEvent("textLayout", [linesMeasurements](jsi::Runtime &runtime) { - return linesMeasurementsPayload(runtime, linesMeasurements); + dispatchEvent("textLayout", [linesMeasurements, regionsMeasurements](jsi::Runtime &runtime) { + return linesMeasurementsPayload(runtime, linesMeasurements, regionsMeasurements); }); } diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h index c7e306d75edcd9..c9a044f4da3d95 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h @@ -17,11 +17,12 @@ class ParagraphEventEmitter : public ViewEventEmitter { public: using ViewEventEmitter::ViewEventEmitter; - void onTextLayout(LinesMeasurements const &linesMeasurements) const; + void onTextLayout(LinesMeasurements const &linesMeasurements, RegionsMeasurements const ®ionsMeasurements) const; private: mutable std::mutex linesMeasurementsMutex_; mutable LinesMeasurements linesMeasurementsMetrics_; + mutable RegionsMeasurements regionsMeasurementsMetrics_; }; } // namespace react diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp index 1f4cbc4a4974ee..8a18300fc0bbc5 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp @@ -60,12 +60,13 @@ TextMeasurement ParagraphLayoutManager::measure( } } -LinesMeasurements ParagraphLayoutManager::measureLines( +SegmentedMeasurements ParagraphLayoutManager::measureLines( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, - Size size) const { + Size size, + std::vector textLayoutConfig) const { return textLayoutManager_->measureLines( - attributedString, paragraphAttributes, size); + attributedString, paragraphAttributes, size, textLayoutConfig); } void ParagraphLayoutManager::setTextLayoutManager( diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h index d262db62b8c9ee..b644b5e574388d 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h @@ -28,10 +28,11 @@ class ParagraphLayoutManager { ParagraphAttributes const ¶graphAttributes, LayoutConstraints layoutConstraints) const; - LinesMeasurements measureLines( + SegmentedMeasurements measureLines( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, - Size size) const; + Size size, + std::vector textLayoutConfig) const; void setTextLayoutManager( std::shared_ptr textLayoutManager) const; diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp index 242567dc4b6e63..3cb00f6f69a9fa 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp @@ -39,6 +39,14 @@ ParagraphProps::ParagraphProps( "selectable", sourceProps.isSelectable, false)), + textLayoutConfig( + CoreFeatures::enablePropIteratorSetter ? sourceProps.textLayoutConfig + : convertRawProp( + context, + rawProps, + "textLayoutConfig", + sourceProps.textLayoutConfig, + {})), onTextLayout( CoreFeatures::enablePropIteratorSetter ? sourceProps.onTextLayout : convertRawProp( @@ -123,6 +131,7 @@ void ParagraphProps::setProp( switch (hash) { RAW_SET_PROP_SWITCH_CASE_BASIC(isSelectable); RAW_SET_PROP_SWITCH_CASE_BASIC(onTextLayout); + RAW_SET_PROP_SWITCH_CASE_BASIC(textLayoutConfig); } /* diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h index 86a9224c3e193f..69543594e837c5 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -51,6 +52,11 @@ class ParagraphProps : public ViewProps, public BaseTextProps { */ bool isSelectable{}; + /* + * Defines text layout tracking strategy. + */ + std::vector textLayoutConfig{}; + bool onTextLayout{}; #pragma mark - DebugStringConvertible diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp index 17cb7dd34ad1c1..7a7db87886aa70 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -158,13 +158,15 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { auto measurement = getStateData().paragraphLayoutManager.measure( content.attributedString, content.paragraphAttributes, layoutConstraints); - + if (getConcreteProps().onTextLayout) { - auto linesMeasurements = getStateData().paragraphLayoutManager.measureLines( + auto segmentedMeasurements = getStateData().paragraphLayoutManager.measureLines( content.attributedString, content.paragraphAttributes, - measurement.size); - getConcreteEventEmitter().onTextLayout(linesMeasurements); + measurement.size, + getConcreteProps().textLayoutConfig); + + getConcreteEventEmitter().onTextLayout(segmentedMeasurements.linesMeasurements, segmentedMeasurements.regionsMeasurements); } if (content.attachments.empty()) { diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index 6eb4cdcf1bf59c..8839b3e06bde2b 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -39,6 +39,12 @@ struct LineMeasurement { }; using LinesMeasurements = std::vector; +using RegionsMeasurements = std::vector; + +struct SegmentedMeasurements { + LinesMeasurements linesMeasurements; + RegionsMeasurements regionsMeasurements; +}; /* * Describes a result of text measuring. diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h index b0d906ca10cea8..b04a1b6e208087 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h @@ -45,10 +45,10 @@ using RCTTextLayoutFragmentEnumerationBlock = frame:(CGRect)frame textStorage:(NSTextStorage *_Nullable)textStorage; -- (facebook::react::LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedString)attributedString - paragraphAttributes: - (facebook::react::ParagraphAttributes)paragraphAttributes - size:(CGSize)size; +- (facebook::react::SegmentedMeasurements)getLinesForAttributedString:(facebook::react::AttributedString)attributedString + paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes + size:(CGSize)size + textLayoutConfig:(NSArray*)textLayoutConfig; - (facebook::react::SharedEventEmitter) getEventEmitterWithAttributeString:(facebook::react::AttributedString)attributedString diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm index fc83b1fbb21f08..4d663056900cdf 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm @@ -120,9 +120,10 @@ - (void)drawAttributedString:(AttributedString)attributedString #endif } -- (LinesMeasurements)getLinesForAttributedString:(AttributedString)attributedString - paragraphAttributes:(ParagraphAttributes)paragraphAttributes - size:(CGSize)size +- (SegmentedMeasurements)getLinesForAttributedString:(AttributedString)attributedString + paragraphAttributes:(ParagraphAttributes)paragraphAttributes + size:(CGSize)size + textLayoutConfig:(NSArray*)textLayoutConfig { NSTextStorage *textStorage = [self textStorageForAttributesString:attributedString paragraphAttributes:paragraphAttributes @@ -133,7 +134,9 @@ - (LinesMeasurements)getLinesForAttributedString:(AttributedString)attributedStr NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; std::vector paragraphLines{}; + std::vector regionsMeasurements{}; auto blockParagraphLines = ¶graphLines; + auto blockRegionsMeasurements = ®ionsMeasurements; [layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^( @@ -152,6 +155,19 @@ - (LinesMeasurements)getLinesForAttributedString:(AttributedString)attributedStr auto rect = facebook::react::Rect{ facebook::react::Point{usedRect.origin.x, usedRect.origin.y}, facebook::react::Size{usedRect.size.width, usedRect.size.height}}; + + NSRange lineRange = NSIntersectionRange(lineGlyphRange, NSMakeRange( + [[textLayoutConfig firstObject] integerValue], + [[textLayoutConfig firstObject] integerValue])); + + [layoutManager enumerateEnclosingRectsForGlyphRange:lineRange + withinSelectedGlyphRange:lineRange + inTextContainer:usedTextContainer + usingBlock:^(CGRect enclosingRect, __unused BOOL *anotherStop) { + blockRegionsMeasurements->push_back(enclosingRect.size.width); + blockRegionsMeasurements->push_back(enclosingRect.size.height); + }]; + auto line = LineMeasurement{ std::string([renderedString UTF8String]), rect, @@ -161,7 +177,10 @@ - (LinesMeasurements)getLinesForAttributedString:(AttributedString)attributedStr font.xHeight}; blockParagraphLines->push_back(line); }]; - return paragraphLines; + + SegmentedMeasurements segmentedMeasurements = {paragraphLines, regionsMeasurements}; + + return segmentedMeasurements; } - (NSTextStorage *)textStorageForAttributesString:(AttributedString)attributedString diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h index 095fae58850b8b..8bd1a05d8a70cd 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -40,10 +40,11 @@ class TextLayoutManager { * Measures lines of `attributedString` using native text rendering * infrastructure. */ - LinesMeasurements measureLines( + SegmentedMeasurements measureLines( AttributedString attributedString, ParagraphAttributes paragraphAttributes, - Size size) const; + Size size, + std::vector textLayoutConfig) const; std::shared_ptr getHostTextStorage( AttributedString attributedString, diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm index 4f2e8719565345..ac4f954df820b4 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm @@ -105,15 +105,23 @@ return measurement; } -LinesMeasurements TextLayoutManager::measureLines( +SegmentedMeasurements TextLayoutManager::measureLines( AttributedString attributedString, ParagraphAttributes paragraphAttributes, - Size size) const + Size size, + std::vector textLayoutConfig) const { + id chunks = [NSMutableArray new]; + std::for_each(textLayoutConfig.begin(), textLayoutConfig.end(), ^(int chunk) { + id region = [NSNumber numberWithInteger:chunk]; + [chunks addObject:region]; + }); + RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_); return [textLayoutManager getLinesForAttributedString:attributedString paragraphAttributes:paragraphAttributes - size:{size.width, size.height}]; + size:{size.width, size.height} + textLayoutConfig:chunks]; } } // namespace react From 42e36aea758d7a8992d82f923020add7a9cf72ea Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 12 May 2023 01:58:18 +0500 Subject: [PATCH 02/19] rename textLayoutConfig to textLayoutRegions --- .../Libraries/Text/Text/RCTTextShadowView.h | 2 +- .../Libraries/Text/Text/RCTTextViewManager.m | 2 +- .../react-native/Libraries/Text/TextNativeComponent.js | 2 +- packages/react-native/Libraries/Text/TextProps.js | 2 +- .../facebook/react/views/text/ReactTextShadowNode.java | 8 ++++---- .../components/text/ParagraphLayoutManager.cpp | 4 ++-- .../renderer/components/text/ParagraphLayoutManager.h | 2 +- .../react/renderer/components/text/ParagraphProps.cpp | 10 +++++----- .../react/renderer/components/text/ParagraphProps.h | 2 +- .../renderer/components/text/ParagraphShadowNode.cpp | 2 +- .../renderer/textlayoutmanager/RCTTextLayoutManager.h | 2 +- .../renderer/textlayoutmanager/RCTTextLayoutManager.mm | 6 +++--- .../renderer/textlayoutmanager/TextLayoutManager.h | 2 +- .../renderer/textlayoutmanager/TextLayoutManager.mm | 6 +++--- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/react-native/Libraries/Text/Text/RCTTextShadowView.h b/packages/react-native/Libraries/Text/Text/RCTTextShadowView.h index e67ae498470f3d..983f978b897606 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextShadowView.h +++ b/packages/react-native/Libraries/Text/Text/RCTTextShadowView.h @@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL adjustsFontSizeToFit; @property (nonatomic, assign) CGFloat minimumFontScale; @property (nonatomic, copy) RCTDirectEventBlock onTextLayout; -@property (nonatomic, copy) NSArray *textLayoutConfig; +@property (nonatomic, copy) NSArray *textLayoutRegions; - (void)uiManagerWillPerformMounting; diff --git a/packages/react-native/Libraries/Text/Text/RCTTextViewManager.m b/packages/react-native/Libraries/Text/Text/RCTTextViewManager.m index 171312ccdd73fd..6adacfc38b2b64 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextViewManager.m +++ b/packages/react-native/Libraries/Text/Text/RCTTextViewManager.m @@ -32,7 +32,7 @@ @implementation RCTTextViewManager { RCT_REMAP_SHADOW_PROPERTY(minimumFontScale, minimumFontScale, CGFloat) RCT_EXPORT_SHADOW_PROPERTY(onTextLayout, RCTDirectEventBlock) -RCT_EXPORT_SHADOW_PROPERTY(textLayoutConfig, NSArray) +RCT_EXPORT_SHADOW_PROPERTY(textLayoutRegions, NSArray) RCT_EXPORT_VIEW_PROPERTY(selectable, BOOL) diff --git a/packages/react-native/Libraries/Text/TextNativeComponent.js b/packages/react-native/Libraries/Text/TextNativeComponent.js index ff1a798289632c..167d5e2e624a99 100644 --- a/packages/react-native/Libraries/Text/TextNativeComponent.js +++ b/packages/react-native/Libraries/Text/TextNativeComponent.js @@ -44,7 +44,7 @@ const textViewConfig = { minimumFontScale: true, textBreakStrategy: true, onTextLayout: true, - textLayoutConfig: true, + textLayoutRegions: true, onInlineViewLayout: true, dataDetectorType: true, android_hyphenationFrequency: true, diff --git a/packages/react-native/Libraries/Text/TextProps.js b/packages/react-native/Libraries/Text/TextProps.js index c22728c862ba4a..c3eb6351dee031 100644 --- a/packages/react-native/Libraries/Text/TextProps.js +++ b/packages/react-native/Libraries/Text/TextProps.js @@ -171,7 +171,7 @@ export type TextProps = $ReadOnly<{| /** * Regions for text layout tracking */ - textLayoutConfig?: ?$ReadOnlyArray, + textLayoutRegions?: ?$ReadOnlyArray, /** * Defines how far your touch may move off of the button, before diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java index 89c654c2ac43ae..fc24261040269d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java @@ -60,7 +60,7 @@ public class ReactTextShadowNode extends ReactBaseTextShadowNode { private @Nullable Spannable mPreparedSpannableText; private boolean mShouldNotifyOnTextLayout; - private ReadableArray mTextLayoutConfig; + private ReadableArray mTextLayoutRegions; private final YogaMeasureFunction mTextMeasureFunction = new YogaMeasureFunction() { @@ -360,9 +360,9 @@ public void setShouldNotifyOnTextLayout(boolean shouldNotifyOnTextLayout) { mShouldNotifyOnTextLayout = shouldNotifyOnTextLayout; } - @ReactProp(name = "textLayoutConfig") - public void setTextLayoutConfig(ReadableArray textLayoutConfig) { - mTextLayoutConfig = textLayoutConfig; + @ReactProp(name = "textLayoutRegions") + public void setTextLayoutRegions(ReadableArray textLayoutRegions) { + mTextLayoutRegions = textLayoutRegions; } @Override diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp index 8a18300fc0bbc5..b40f41e39f7918 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp @@ -64,9 +64,9 @@ SegmentedMeasurements ParagraphLayoutManager::measureLines( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, Size size, - std::vector textLayoutConfig) const { + std::vector textLayoutRegions) const { return textLayoutManager_->measureLines( - attributedString, paragraphAttributes, size, textLayoutConfig); + attributedString, paragraphAttributes, size, textLayoutRegions); } void ParagraphLayoutManager::setTextLayoutManager( diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h index b644b5e574388d..d2f6b731b46aa0 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h @@ -32,7 +32,7 @@ class ParagraphLayoutManager { AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, Size size, - std::vector textLayoutConfig) const; + std::vector textLayoutRegions) const; void setTextLayoutManager( std::shared_ptr textLayoutManager) const; diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp index 3cb00f6f69a9fa..734815ab4a70a3 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp @@ -39,13 +39,13 @@ ParagraphProps::ParagraphProps( "selectable", sourceProps.isSelectable, false)), - textLayoutConfig( - CoreFeatures::enablePropIteratorSetter ? sourceProps.textLayoutConfig + textLayoutRegions( + CoreFeatures::enablePropIteratorSetter ? sourceProps.textLayoutRegions : convertRawProp( context, rawProps, - "textLayoutConfig", - sourceProps.textLayoutConfig, + "textLayoutRegions", + sourceProps.textLayoutRegions, {})), onTextLayout( CoreFeatures::enablePropIteratorSetter ? sourceProps.onTextLayout @@ -131,7 +131,7 @@ void ParagraphProps::setProp( switch (hash) { RAW_SET_PROP_SWITCH_CASE_BASIC(isSelectable); RAW_SET_PROP_SWITCH_CASE_BASIC(onTextLayout); - RAW_SET_PROP_SWITCH_CASE_BASIC(textLayoutConfig); + RAW_SET_PROP_SWITCH_CASE_BASIC(textLayoutRegions); } /* diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h index 69543594e837c5..1d1123fd723a0c 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h @@ -55,7 +55,7 @@ class ParagraphProps : public ViewProps, public BaseTextProps { /* * Defines text layout tracking strategy. */ - std::vector textLayoutConfig{}; + std::vector textLayoutRegions{}; bool onTextLayout{}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp index 7a7db87886aa70..3bc010d5d7bc6c 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -164,7 +164,7 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { content.attributedString, content.paragraphAttributes, measurement.size, - getConcreteProps().textLayoutConfig); + getConcreteProps().textLayoutRegions); getConcreteEventEmitter().onTextLayout(segmentedMeasurements.linesMeasurements, segmentedMeasurements.regionsMeasurements); } diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h index b04a1b6e208087..ccbdf7f6a495d0 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h @@ -48,7 +48,7 @@ using RCTTextLayoutFragmentEnumerationBlock = - (facebook::react::SegmentedMeasurements)getLinesForAttributedString:(facebook::react::AttributedString)attributedString paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes size:(CGSize)size - textLayoutConfig:(NSArray*)textLayoutConfig; + textLayoutRegions:(NSArray*)textLayoutRegions; - (facebook::react::SharedEventEmitter) getEventEmitterWithAttributeString:(facebook::react::AttributedString)attributedString diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm index 4d663056900cdf..f51ca666106566 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm @@ -123,7 +123,7 @@ - (void)drawAttributedString:(AttributedString)attributedString - (SegmentedMeasurements)getLinesForAttributedString:(AttributedString)attributedString paragraphAttributes:(ParagraphAttributes)paragraphAttributes size:(CGSize)size - textLayoutConfig:(NSArray*)textLayoutConfig + textLayoutRegions:(NSArray*)textLayoutRegions { NSTextStorage *textStorage = [self textStorageForAttributesString:attributedString paragraphAttributes:paragraphAttributes @@ -157,8 +157,8 @@ - (SegmentedMeasurements)getLinesForAttributedString:(AttributedString)attribute facebook::react::Size{usedRect.size.width, usedRect.size.height}}; NSRange lineRange = NSIntersectionRange(lineGlyphRange, NSMakeRange( - [[textLayoutConfig firstObject] integerValue], - [[textLayoutConfig firstObject] integerValue])); + [[textLayoutRegions firstObject] integerValue], + [[textLayoutRegions firstObject] integerValue])); [layoutManager enumerateEnclosingRectsForGlyphRange:lineRange withinSelectedGlyphRange:lineRange diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h index 8bd1a05d8a70cd..1e9728c89f652f 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -44,7 +44,7 @@ class TextLayoutManager { AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size, - std::vector textLayoutConfig) const; + std::vector textLayoutRegions) const; std::shared_ptr getHostTextStorage( AttributedString attributedString, diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm index ac4f954df820b4..674964eab7e0f7 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm @@ -109,10 +109,10 @@ AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size, - std::vector textLayoutConfig) const + std::vector textLayoutRegions) const { id chunks = [NSMutableArray new]; - std::for_each(textLayoutConfig.begin(), textLayoutConfig.end(), ^(int chunk) { + std::for_each(textLayoutRegions.begin(), textLayoutRegions.end(), ^(int chunk) { id region = [NSNumber numberWithInteger:chunk]; [chunks addObject:region]; }); @@ -121,7 +121,7 @@ return [textLayoutManager getLinesForAttributedString:attributedString paragraphAttributes:paragraphAttributes size:{size.width, size.height} - textLayoutConfig:chunks]; + textLayoutRegions:chunks]; } } // namespace react From 1c09f3d872918c7e6dd40d0126cd51e58d18768f Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 12 May 2023 13:07:34 +0500 Subject: [PATCH 03/19] rename segmentedmeasurements to textlayoutmeasurements --- .../renderer/components/text/ParagraphLayoutManager.cpp | 2 +- .../react/renderer/components/text/ParagraphLayoutManager.h | 2 +- .../react/renderer/components/text/ParagraphShadowNode.cpp | 4 ++-- .../react/renderer/textlayoutmanager/TextMeasureCache.h | 2 +- .../react/renderer/textlayoutmanager/RCTTextLayoutManager.h | 2 +- .../renderer/textlayoutmanager/RCTTextLayoutManager.mm | 6 +++--- .../react/renderer/textlayoutmanager/TextLayoutManager.h | 2 +- .../react/renderer/textlayoutmanager/TextLayoutManager.mm | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp index b40f41e39f7918..6ee090e54a35c6 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp @@ -60,7 +60,7 @@ TextMeasurement ParagraphLayoutManager::measure( } } -SegmentedMeasurements ParagraphLayoutManager::measureLines( +TextLayoutMeasurements ParagraphLayoutManager::measureLines( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, Size size, diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h index d2f6b731b46aa0..65ca9d66101d24 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h @@ -28,7 +28,7 @@ class ParagraphLayoutManager { ParagraphAttributes const ¶graphAttributes, LayoutConstraints layoutConstraints) const; - SegmentedMeasurements measureLines( + TextLayoutMeasurements measureLines( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, Size size, diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp index 3bc010d5d7bc6c..42d4e344f499b1 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -160,13 +160,13 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { content.attributedString, content.paragraphAttributes, layoutConstraints); if (getConcreteProps().onTextLayout) { - auto segmentedMeasurements = getStateData().paragraphLayoutManager.measureLines( + auto textLayoutMeasurements = getStateData().paragraphLayoutManager.measureLines( content.attributedString, content.paragraphAttributes, measurement.size, getConcreteProps().textLayoutRegions); - getConcreteEventEmitter().onTextLayout(segmentedMeasurements.linesMeasurements, segmentedMeasurements.regionsMeasurements); + getConcreteEventEmitter().onTextLayout(textLayoutMeasurements.linesMeasurements, textLayoutMeasurements.regionsMeasurements); } if (content.attachments.empty()) { diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index 8839b3e06bde2b..c19babac681dd0 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -41,7 +41,7 @@ struct LineMeasurement { using LinesMeasurements = std::vector; using RegionsMeasurements = std::vector; -struct SegmentedMeasurements { +struct TextLayoutMeasurements { LinesMeasurements linesMeasurements; RegionsMeasurements regionsMeasurements; }; diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h index ccbdf7f6a495d0..dbef4d4815d084 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.h @@ -45,7 +45,7 @@ using RCTTextLayoutFragmentEnumerationBlock = frame:(CGRect)frame textStorage:(NSTextStorage *_Nullable)textStorage; -- (facebook::react::SegmentedMeasurements)getLinesForAttributedString:(facebook::react::AttributedString)attributedString +- (facebook::react::TextLayoutMeasurements)getLinesForAttributedString:(facebook::react::AttributedString)attributedString paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes size:(CGSize)size textLayoutRegions:(NSArray*)textLayoutRegions; diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm index f51ca666106566..fc58d6797cb8f3 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm @@ -120,7 +120,7 @@ - (void)drawAttributedString:(AttributedString)attributedString #endif } -- (SegmentedMeasurements)getLinesForAttributedString:(AttributedString)attributedString +- (TextLayoutMeasurements)getLinesForAttributedString:(AttributedString)attributedString paragraphAttributes:(ParagraphAttributes)paragraphAttributes size:(CGSize)size textLayoutRegions:(NSArray*)textLayoutRegions @@ -178,9 +178,9 @@ - (SegmentedMeasurements)getLinesForAttributedString:(AttributedString)attribute blockParagraphLines->push_back(line); }]; - SegmentedMeasurements segmentedMeasurements = {paragraphLines, regionsMeasurements}; + TextLayoutMeasurements textLayoutMeasurements = {paragraphLines, regionsMeasurements}; - return segmentedMeasurements; + return textLayoutMeasurements; } - (NSTextStorage *)textStorageForAttributesString:(AttributedString)attributedString diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h index 1e9728c89f652f..2bd3cdfbf5705e 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -40,7 +40,7 @@ class TextLayoutManager { * Measures lines of `attributedString` using native text rendering * infrastructure. */ - SegmentedMeasurements measureLines( + TextLayoutMeasurements measureLines( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size, diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm index 674964eab7e0f7..a96b9b210411b2 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm @@ -105,7 +105,7 @@ return measurement; } -SegmentedMeasurements TextLayoutManager::measureLines( +TextLayoutMeasurements TextLayoutManager::measureLines( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size, From c0fee8b0c6c9b4b50e7f79222fa62711e997d4a9 Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 12 May 2023 14:24:34 +0500 Subject: [PATCH 04/19] ontextlayout regions payload modification --- .../components/text/ParagraphEventEmitter.cpp | 13 +++++++++---- .../textlayoutmanager/TextMeasureCache.cpp | 14 ++++++++++++++ .../textlayoutmanager/TextMeasureCache.h | 14 +++++++++++++- .../textlayoutmanager/RCTTextLayoutManager.mm | 18 ++++++++++-------- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp index 829f1cb22b00c0..22bcebbec72e46 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp @@ -32,11 +32,16 @@ static jsi::Value linesMeasurementsPayload( lines.setValueAtIndex(runtime, i, jsiLine); } - if (regionsMeasurements.size() == 2) { - regions.setValueAtIndex(runtime, 0, regionsMeasurements[0]); - regions.setValueAtIndex(runtime, 1, regionsMeasurements[1]); + for (size_t i = 0; i < regionsMeasurements.size(); ++i) { + auto const ®ionMeasurement = regionsMeasurements[i]; + auto jsiRegion = jsi::Object(runtime); + jsiRegion.setProperty(runtime, "x", regionMeasurement.frame.origin.x); + jsiRegion.setProperty(runtime, "y", regionMeasurement.frame.origin.y); + jsiRegion.setProperty(runtime, "width", regionMeasurement.frame.size.width); + jsiRegion.setProperty(runtime, "height", regionMeasurement.frame.size.height); + regions.setValueAtIndex(runtime, i, jsiRegion); } - + payload.setProperty(runtime, "lines", lines); payload.setProperty(runtime, "regions", regions); diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp index 03558aad542270..96e6cb070ef08e 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp @@ -65,4 +65,18 @@ bool LineMeasurement::operator==(LineMeasurement const &rhs) const { rhs.xHeight); } +RegionMeasurement::RegionMeasurement( + Rect frame) + : frame(frame) {} + +RegionMeasurement::RegionMeasurement(folly::dynamic const &data) + : frame(rectFromDynamic(data)) {} + +bool RegionMeasurement::operator==(RegionMeasurement const &rhs) const { + return std::tie( + this->frame) == + std::tie( + rhs.frame); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index c19babac681dd0..ecc188367d95a0 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -38,8 +38,20 @@ struct LineMeasurement { bool operator==(LineMeasurement const &rhs) const; }; +struct RegionMeasurement { + Rect frame; + + RegionMeasurement( + Rect frame + ); + + RegionMeasurement(folly::dynamic const &data); + + bool operator==(RegionMeasurement const &rhs) const; +}; + using LinesMeasurements = std::vector; -using RegionsMeasurements = std::vector; +using RegionsMeasurements = std::vector; struct TextLayoutMeasurements { LinesMeasurements linesMeasurements; diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm index fc58d6797cb8f3..ebe43941eab520 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm @@ -134,7 +134,7 @@ - (TextLayoutMeasurements)getLinesForAttributedString:(AttributedString)attribut NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; std::vector paragraphLines{}; - std::vector regionsMeasurements{}; + std::vector regionsMeasurements{}; auto blockParagraphLines = ¶graphLines; auto blockRegionsMeasurements = ®ionsMeasurements; @@ -152,7 +152,7 @@ - (TextLayoutMeasurements)getLinesForAttributedString:(AttributedString)attribut attribute:NSFontAttributeName atIndex:0 effectiveRange:nil]; - auto rect = facebook::react::Rect{ + auto lineRect = facebook::react::Rect{ facebook::react::Point{usedRect.origin.x, usedRect.origin.y}, facebook::react::Size{usedRect.size.width, usedRect.size.height}}; @@ -161,16 +161,18 @@ - (TextLayoutMeasurements)getLinesForAttributedString:(AttributedString)attribut [[textLayoutRegions firstObject] integerValue])); [layoutManager enumerateEnclosingRectsForGlyphRange:lineRange - withinSelectedGlyphRange:lineRange - inTextContainer:usedTextContainer - usingBlock:^(CGRect enclosingRect, __unused BOOL *anotherStop) { - blockRegionsMeasurements->push_back(enclosingRect.size.width); - blockRegionsMeasurements->push_back(enclosingRect.size.height); + withinSelectedGlyphRange:lineRange + inTextContainer:usedTextContainer + usingBlock:^(CGRect enclosingRect, __unused BOOL *anotherStop) { + auto regionRect = facebook::react::Rect{ + facebook::react::Point{enclosingRect.origin.x, enclosingRect.origin.y}, + facebook::react::Size{enclosingRect.size.width, enclosingRect.size.height}}; + blockRegionsMeasurements->push_back(regionRect); }]; auto line = LineMeasurement{ std::string([renderedString UTF8String]), - rect, + lineRect, -font.descender, font.capHeight, font.ascender, From adf1b0314ab82a0c2c22ccf93e43cb13255c3bc5 Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 12 May 2023 16:09:23 +0500 Subject: [PATCH 05/19] textlayoutregions prop type unification --- .../react/renderer/components/text/ParagraphLayoutManager.cpp | 2 +- .../react/renderer/components/text/ParagraphLayoutManager.h | 2 +- .../react/renderer/components/text/ParagraphProps.h | 3 ++- .../react/renderer/textlayoutmanager/TextMeasureCache.h | 2 ++ .../ios/react/renderer/textlayoutmanager/TextLayoutManager.h | 2 +- .../ios/react/renderer/textlayoutmanager/TextLayoutManager.mm | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp index 6ee090e54a35c6..5625731d9d618a 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.cpp @@ -64,7 +64,7 @@ TextLayoutMeasurements ParagraphLayoutManager::measureLines( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, Size size, - std::vector textLayoutRegions) const { + TextLayoutRegions textLayoutRegions) const { return textLayoutManager_->measureLines( attributedString, paragraphAttributes, size, textLayoutRegions); } diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h index 65ca9d66101d24..ce72c9c1372bfc 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphLayoutManager.h @@ -32,7 +32,7 @@ class ParagraphLayoutManager { AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, Size size, - std::vector textLayoutRegions) const; + TextLayoutRegions textLayoutRegions) const; void setTextLayoutManager( std::shared_ptr textLayoutManager) const; diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h index 1d1123fd723a0c..267df2b509cdad 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace facebook { namespace react { @@ -55,7 +56,7 @@ class ParagraphProps : public ViewProps, public BaseTextProps { /* * Defines text layout tracking strategy. */ - std::vector textLayoutRegions{}; + TextLayoutRegions textLayoutRegions{}; bool onTextLayout{}; diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index ecc188367d95a0..49aeb9ac0b0058 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -53,6 +53,8 @@ struct RegionMeasurement { using LinesMeasurements = std::vector; using RegionsMeasurements = std::vector; +using TextLayoutRegions = std::vector; + struct TextLayoutMeasurements { LinesMeasurements linesMeasurements; RegionsMeasurements regionsMeasurements; diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h index 2bd3cdfbf5705e..16a011175bc64c 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -44,7 +44,7 @@ class TextLayoutManager { AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size, - std::vector textLayoutRegions) const; + TextLayoutRegions textLayoutRegions) const; std::shared_ptr getHostTextStorage( AttributedString attributedString, diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm index a96b9b210411b2..540c7a4f6b0cbc 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm @@ -109,7 +109,7 @@ AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size, - std::vector textLayoutRegions) const + TextLayoutRegions textLayoutRegions) const { id chunks = [NSMutableArray new]; std::for_each(textLayoutRegions.begin(), textLayoutRegions.end(), ^(int chunk) { From a19802954fcda8940c56f846608a2ae13eede209 Mon Sep 17 00:00:00 2001 From: azimgd Date: Fri, 12 May 2023 20:03:47 +0500 Subject: [PATCH 06/19] implementing jsi props conversion and multiple regions calculation --- .../renderer/attributedstring/conversions.h | 27 +++++++++++++++++ .../textlayoutmanager/TextMeasureCache.h | 3 +- .../textlayoutmanager/RCTTextLayoutManager.mm | 29 ++++++++++--------- .../textlayoutmanager/TextLayoutManager.mm | 18 +++++++----- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h index 26379f41e98b9b..bfc10dfc6534dc 100644 --- a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h @@ -21,7 +21,9 @@ #include #include #include +#include #include +#include #ifdef ANDROID #include @@ -828,6 +830,31 @@ inline void fromRawValue( result = HyphenationFrequency::None; } +inline std::string toString(const TextLayoutRegions &textLayoutRegions) { + LOG(ERROR) << "Unsupported HyphenationFrequency value"; + react_native_expect(false); + return "none"; +} + +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + TextLayoutRegions &result) { + react_native_expect(value.hasType>>()); + if (value.hasType>>()) { + auto regions = std::vector>{value}; + for (const auto ®ion : regions) { + std::vector chunks; + for (const auto &chunk : region) { + chunks.push_back(chunk); + } + result.push_back(chunks); + } + } else { + LOG(ERROR) << "Unsupported FontVariant type"; + } +} + inline ParagraphAttributes convertRawProp( const PropsParserContext &context, RawProps const &rawProps, diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index 49aeb9ac0b0058..a94edf3d258c83 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -53,7 +53,8 @@ struct RegionMeasurement { using LinesMeasurements = std::vector; using RegionsMeasurements = std::vector; -using TextLayoutRegions = std::vector; +using TextLayoutRegion = std::vector; +using TextLayoutRegions = std::vector; struct TextLayoutMeasurements { LinesMeasurements linesMeasurements; diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm index ebe43941eab520..8bd8215cca1121 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm @@ -155,20 +155,23 @@ - (TextLayoutMeasurements)getLinesForAttributedString:(AttributedString)attribut auto lineRect = facebook::react::Rect{ facebook::react::Point{usedRect.origin.x, usedRect.origin.y}, facebook::react::Size{usedRect.size.width, usedRect.size.height}}; - - NSRange lineRange = NSIntersectionRange(lineGlyphRange, NSMakeRange( - [[textLayoutRegions firstObject] integerValue], - [[textLayoutRegions firstObject] integerValue])); - - [layoutManager enumerateEnclosingRectsForGlyphRange:lineRange - withinSelectedGlyphRange:lineRange - inTextContainer:usedTextContainer - usingBlock:^(CGRect enclosingRect, __unused BOOL *anotherStop) { - auto regionRect = facebook::react::Rect{ - facebook::react::Point{enclosingRect.origin.x, enclosingRect.origin.y}, - facebook::react::Size{enclosingRect.size.width, enclosingRect.size.height}}; - blockRegionsMeasurements->push_back(regionRect); + + [textLayoutRegions enumerateObjectsUsingBlock:^(id _Nonnull textLayoutRegion, NSUInteger idx, BOOL * _Nonnull stop) { + auto from = [[textLayoutRegion firstObject] integerValue]; + auto to = [[textLayoutRegion lastObject] integerValue]; + NSRange lineRange = NSIntersectionRange(lineGlyphRange, NSMakeRange(from, to)); + + [layoutManager enumerateEnclosingRectsForGlyphRange:lineRange + withinSelectedGlyphRange:lineRange + inTextContainer:usedTextContainer + usingBlock:^(CGRect enclosingRect, __unused BOOL *anotherStop) { + auto regionRect = facebook::react::Rect{ + facebook::react::Point{enclosingRect.origin.x, enclosingRect.origin.y}, + facebook::react::Size{enclosingRect.size.width, enclosingRect.size.height}}; + blockRegionsMeasurements->push_back(regionRect); + }]; }]; + auto line = LineMeasurement{ std::string([renderedString UTF8String]), diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm index 540c7a4f6b0cbc..d3b335316bf3a8 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm @@ -111,17 +111,21 @@ Size size, TextLayoutRegions textLayoutRegions) const { - id chunks = [NSMutableArray new]; - std::for_each(textLayoutRegions.begin(), textLayoutRegions.end(), ^(int chunk) { - id region = [NSNumber numberWithInteger:chunk]; - [chunks addObject:region]; - }); - + id regions = [NSMutableArray new]; + + for (auto const &textLayoutRegion : textLayoutRegions) { + id region = [NSMutableArray new]; + for (auto const &chunk : textLayoutRegion) { + [region addObject:[NSNumber numberWithInteger:chunk]]; + } + [regions addObject:region]; + } + RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_); return [textLayoutManager getLinesForAttributedString:attributedString paragraphAttributes:paragraphAttributes size:{size.width, size.height} - textLayoutRegions:chunks]; + textLayoutRegions:regions]; } } // namespace react From 550ce91f8dbda22c8dd4d1e2b5bafc822227527a Mon Sep 17 00:00:00 2001 From: azimgd Date: Sat, 13 May 2023 10:32:42 +0500 Subject: [PATCH 07/19] add split regions support with per line region calculation and region identifier --- .../react-native/Libraries/Text/TextProps.js | 2 +- .../Libraries/Types/CoreEventTypes.d.ts | 11 ++++++++ .../components/text/ParagraphEventEmitter.cpp | 3 +++ .../textlayoutmanager/TextMeasureCache.cpp | 25 +++++++++++++++---- .../textlayoutmanager/TextMeasureCache.h | 8 +++++- .../textlayoutmanager/RCTTextLayoutManager.mm | 24 +++++++++++++----- 6 files changed, 60 insertions(+), 13 deletions(-) diff --git a/packages/react-native/Libraries/Text/TextProps.js b/packages/react-native/Libraries/Text/TextProps.js index c3eb6351dee031..563c9488314fd0 100644 --- a/packages/react-native/Libraries/Text/TextProps.js +++ b/packages/react-native/Libraries/Text/TextProps.js @@ -171,7 +171,7 @@ export type TextProps = $ReadOnly<{| /** * Regions for text layout tracking */ - textLayoutRegions?: ?$ReadOnlyArray, + textLayoutRegions?: ?$ReadOnlyArray<$ReadOnlyArray>, /** * Defines how far your touch may move off of the button, before diff --git a/packages/react-native/Libraries/Types/CoreEventTypes.d.ts b/packages/react-native/Libraries/Types/CoreEventTypes.d.ts index 7aff083b07a2bf..e70c467cec4e90 100644 --- a/packages/react-native/Libraries/Types/CoreEventTypes.d.ts +++ b/packages/react-native/Libraries/Types/CoreEventTypes.d.ts @@ -33,11 +33,22 @@ interface TextLayoutLine { y: number; } +interface TextLayoutRegion { + text: string; + height: number; + width: number; + x: number; + y: number; + line: number; + region: number; +} + /** * @see TextProps.onTextLayout */ export interface TextLayoutEventData extends TargetedEvent { lines: TextLayoutLine[]; + regions: TextLayoutRegion[]; } // Similar to React.SyntheticEvent except for nativeEvent diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp index 22bcebbec72e46..d25f43c8043b8f 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp @@ -35,10 +35,13 @@ static jsi::Value linesMeasurementsPayload( for (size_t i = 0; i < regionsMeasurements.size(); ++i) { auto const ®ionMeasurement = regionsMeasurements[i]; auto jsiRegion = jsi::Object(runtime); + jsiRegion.setProperty(runtime, "text", regionMeasurement.text); jsiRegion.setProperty(runtime, "x", regionMeasurement.frame.origin.x); jsiRegion.setProperty(runtime, "y", regionMeasurement.frame.origin.y); jsiRegion.setProperty(runtime, "width", regionMeasurement.frame.size.width); jsiRegion.setProperty(runtime, "height", regionMeasurement.frame.size.height); + jsiRegion.setProperty(runtime, "region", regionMeasurement.region); + jsiRegion.setProperty(runtime, "line", regionMeasurement.line); regions.setValueAtIndex(runtime, i, jsiRegion); } diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp index 96e6cb070ef08e..f62f692c0375cc 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp @@ -66,17 +66,32 @@ bool LineMeasurement::operator==(LineMeasurement const &rhs) const { } RegionMeasurement::RegionMeasurement( - Rect frame) - : frame(frame) {} + std::string text, + Rect frame, + int region, + int line) + : text(std::move(text)), + frame(frame), + region(region), + line(line) {} RegionMeasurement::RegionMeasurement(folly::dynamic const &data) - : frame(rectFromDynamic(data)) {} + : text(data.getDefault("text", "").getString()), + frame(rectFromDynamic(data)), + region(data.getDefault("region", 0).getInt()), + line(data.getDefault("line", 0).getInt()){} bool RegionMeasurement::operator==(RegionMeasurement const &rhs) const { return std::tie( - this->frame) == + this->text, + this->frame, + this->region, + this->line) == std::tie( - rhs.frame); + rhs.text, + rhs.frame, + rhs.region, + rhs.line); } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index a94edf3d258c83..3a2be00be2caec 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -39,10 +39,16 @@ struct LineMeasurement { }; struct RegionMeasurement { + std::string text; Rect frame; + int region; + int line; RegionMeasurement( - Rect frame + std::string text, + Rect frame, + int region, + int line ); RegionMeasurement(folly::dynamic const &data); diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm index 8bd8215cca1121..5e392bfc9ef978 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm @@ -133,6 +133,7 @@ - (TextLayoutMeasurements)getLinesForAttributedString:(AttributedString)attribut NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; + __block int currentLine = 0; std::vector paragraphLines{}; std::vector regionsMeasurements{}; auto blockParagraphLines = ¶graphLines; @@ -147,7 +148,7 @@ - (TextLayoutMeasurements)getLinesForAttributedString:(AttributedString)attribut BOOL *_Nonnull stop) { NSRange range = [layoutManager characterRangeForGlyphRange:lineGlyphRange actualGlyphRange:nil]; - NSString *renderedString = [textStorage.string substringWithRange:range]; + NSString *renderedLineString = [textStorage.string substringWithRange:range]; UIFont *font = [[textStorage attributedSubstringFromRange:range] attribute:NSFontAttributeName atIndex:0 @@ -156,31 +157,42 @@ - (TextLayoutMeasurements)getLinesForAttributedString:(AttributedString)attribut facebook::react::Point{usedRect.origin.x, usedRect.origin.y}, facebook::react::Size{usedRect.size.width, usedRect.size.height}}; - [textLayoutRegions enumerateObjectsUsingBlock:^(id _Nonnull textLayoutRegion, NSUInteger idx, BOOL * _Nonnull stop) { + [textLayoutRegions enumerateObjectsUsingBlock:^(id _Nonnull textLayoutRegion, NSUInteger regionIdx, BOOL * _Nonnull stop) { auto from = [[textLayoutRegion firstObject] integerValue]; - auto to = [[textLayoutRegion lastObject] integerValue]; + auto to = [[textLayoutRegion lastObject] integerValue] - from; NSRange lineRange = NSIntersectionRange(lineGlyphRange, NSMakeRange(from, to)); + if (lineRange.location == 0 && lineRange.length == 0) { + return; + } + [layoutManager enumerateEnclosingRectsForGlyphRange:lineRange withinSelectedGlyphRange:lineRange inTextContainer:usedTextContainer usingBlock:^(CGRect enclosingRect, __unused BOOL *anotherStop) { + NSString *renderedRegionString = [textStorage.string substringWithRange:lineRange]; auto regionRect = facebook::react::Rect{ facebook::react::Point{enclosingRect.origin.x, enclosingRect.origin.y}, facebook::react::Size{enclosingRect.size.width, enclosingRect.size.height}}; - blockRegionsMeasurements->push_back(regionRect); + auto region = RegionMeasurement{ + std::string([renderedRegionString UTF8String]), + regionRect, + (int)regionIdx, + currentLine + }; + blockRegionsMeasurements->push_back(region); }]; }]; - auto line = LineMeasurement{ - std::string([renderedString UTF8String]), + std::string([renderedLineString UTF8String]), lineRect, -font.descender, font.capHeight, font.ascender, font.xHeight}; blockParagraphLines->push_back(line); + currentLine += 1; }]; TextLayoutMeasurements textLayoutMeasurements = {paragraphLines, regionsMeasurements}; From 09a4947ea88b5ab7d50567db2f4d39adb7bfe571 Mon Sep 17 00:00:00 2001 From: azimgd Date: Sat, 13 May 2023 10:36:11 +0500 Subject: [PATCH 08/19] remove obsolete imports --- .../java/com/facebook/react/views/text/ReactTextShadowNode.java | 1 - .../ReactCommon/react/renderer/components/text/ParagraphProps.h | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java index fc24261040269d..6bf00ba754a11b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java @@ -41,7 +41,6 @@ import com.facebook.yoga.YogaMeasureOutput; import com.facebook.yoga.YogaNode; import java.util.ArrayList; -import com.facebook.common.logging.FLog; /** * {@link ReactBaseTextShadowNode} concrete class for anchor {@code Text} node. diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h index 267df2b509cdad..b32ad28f61a169 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.h @@ -9,7 +9,6 @@ #include #include -#include #include #include From 605c0a5af5efb2dde3408a1a7c7f869c456f89d2 Mon Sep 17 00:00:00 2001 From: azimgd Date: Sat, 13 May 2023 11:25:52 +0500 Subject: [PATCH 09/19] textlayoutregions prop type checking --- .../react/renderer/attributedstring/conversions.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h index bfc10dfc6534dc..fb331548707cb7 100644 --- a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h @@ -844,14 +844,19 @@ inline void fromRawValue( if (value.hasType>>()) { auto regions = std::vector>{value}; for (const auto ®ion : regions) { - std::vector chunks; - for (const auto &chunk : region) { - chunks.push_back(chunk); + if (region.size() == 2) { + std::vector chunks; + for (const auto &chunk : region) { + chunks.push_back(chunk); + } + result.push_back(chunks); + } else { + LOG(ERROR) << "Unsupported TextLayoutRegion value"; + react_native_expect(false); } - result.push_back(chunks); } } else { - LOG(ERROR) << "Unsupported FontVariant type"; + LOG(ERROR) << "Unsupported TextLayoutRegions type"; } } From 74554801ebce7fc0ee549fe3cbb5c0d70bc9b4f9 Mon Sep 17 00:00:00 2001 From: azimgd Date: Mon, 15 May 2023 12:34:09 +0500 Subject: [PATCH 10/19] measureLines android part partial implementation --- .../react/fabric/FabricUIManager.java | 8 ++--- .../react/views/text/FontMetricsUtil.java | 30 +++++++++++++++++-- .../react/views/text/ReactTextShadowNode.java | 7 ++--- .../react/views/text/TextLayoutManager.java | 4 +-- .../text/TextLayoutManagerMapBuffer.java | 5 ++-- .../textlayoutmanager/TextLayoutManager.cpp | 28 ++++++++++++----- .../textlayoutmanager/TextLayoutManager.h | 10 ++++--- 7 files changed, 66 insertions(+), 26 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index c785ad9b3075c6..0915fe71b8dafd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -443,9 +443,9 @@ public void onCatalystInstanceDestroy() { } @SuppressWarnings("unused") - private NativeArray measureLines( + private NativeMap measureLines( ReadableMap attributedString, ReadableMap paragraphAttributes, float width, float height) { - return (NativeArray) + return (NativeMap) TextLayoutManager.measureLines( mReactApplicationContext, attributedString, @@ -454,12 +454,12 @@ private NativeArray measureLines( } @SuppressWarnings("unused") - private NativeArray measureLinesMapBuffer( + private NativeMap measureLinesMapBuffer( ReadableMapBuffer attributedString, ReadableMapBuffer paragraphAttributes, float width, float height) { - return (NativeArray) + return (NativeMap) TextLayoutManagerMapBuffer.measureLines( mReactApplicationContext, attributedString, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java index f19a1b0425888d..f12ef50221d827 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java @@ -22,10 +22,11 @@ public class FontMetricsUtil { private static final String X_HEIGHT_MEASUREMENT_TEXT = "x"; private static final float AMPLIFICATION_FACTOR = 100; - public static WritableArray getFontMetrics( + public static WritableMap getFontMetrics( CharSequence text, Layout layout, TextPaint paint, Context context) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); WritableArray lines = Arguments.createArray(); + WritableArray regions = Arguments.createArray(); // To calculate xHeight and capHeight we have to render an "x" and "T" and manually measure // their height. // In order to get more precision than Android offers, we blow up the text size by 100 and @@ -41,6 +42,7 @@ public static WritableArray getFontMetrics( paintCopy.getTextBounds( X_HEIGHT_MEASUREMENT_TEXT, 0, X_HEIGHT_MEASUREMENT_TEXT.length(), xHeightBounds); double xHeight = xHeightBounds.height() / AMPLIFICATION_FACTOR / dm.density; + for (int i = 0; i < layout.getLineCount(); i++) { Rect bounds = new Rect(); layout.getLineBounds(i, bounds); @@ -56,8 +58,32 @@ public static WritableArray getFontMetrics( line.putDouble("xHeight", xHeight); line.putString( "text", text.subSequence(layout.getLineStart(i), layout.getLineEnd(i)).toString()); + + int start = 10; + int end = 40; + int lineStartIndex = layout.getLineStart(i); + int lineEndIndex = layout.getLineEnd(i); + int startIndex = Math.max(lineStartIndex, start); + int endIndex = Math.min(lineEndIndex, end); + layout.getLineBounds(i, bounds); + int xStart = (int) layout.getPrimaryHorizontal(startIndex); + int xEnd = (int) layout.getPrimaryHorizontal(endIndex); + int yStart = bounds.top; + int yEnd = bounds.bottom; + Rect charBounds = new Rect(xStart, yStart, xEnd, yEnd); + + WritableMap region = Arguments.createMap(); + region.putDouble("width", charBounds.width()); + region.putDouble("height", charBounds.height()); + lines.pushMap(line); + regions.pushMap(region); } - return lines; + + WritableMap textLayoutMetrics = Arguments.createMap(); + textLayoutMetrics.putArray("lines", lines); + textLayoutMetrics.putArray("regions", regions); + + return textLayoutMetrics; } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java index 6bf00ba754a11b..ea51a6b418d715 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java @@ -109,15 +109,14 @@ public long measure( if (mShouldNotifyOnTextLayout) { ThemedReactContext themedReactContext = getThemedContext(); - WritableArray lines = + WritableMap textLayoutMetrics = FontMetricsUtil.getFontMetrics( text, layout, sTextPaintInstance, themedReactContext); - WritableMap event = Arguments.createMap(); - event.putArray("lines", lines); + if (themedReactContext.hasActiveReactInstance()) { themedReactContext .getJSModule(RCTEventEmitter.class) - .receiveEvent(getReactTag(), "topTextLayout", event); + .receiveEvent(getReactTag(), "topTextLayout", textLayoutMetrics); } else { ReactSoftExceptionLogger.logSoftException( "ReactTextShadowNode", diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java index e5dc5fb074ce9a..88e42b0b3934b7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java @@ -30,7 +30,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableNativeMap; -import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactStylesDiffMap; @@ -564,7 +564,7 @@ public static long measureText( return YogaMeasureOutput.make(widthInSP, heightInSP); } - public static WritableArray measureLines( + public static WritableMap measureLines( @NonNull Context context, ReadableMap attributedString, ReadableMap paragraphAttributes, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java index ecb7f030f2b046..4ff9784808eb6b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java @@ -27,7 +27,7 @@ import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReactNoCrashSoftException; import com.facebook.react.bridge.ReactSoftExceptionLogger; -import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.uimanager.PixelUtil; @@ -581,7 +581,7 @@ public static long measureText( return YogaMeasureOutput.make(widthInSP, heightInSP); } - public static WritableArray measureLines( + public static WritableMap measureLines( @NonNull Context context, MapBuffer attributedString, MapBuffer paragraphAttributes, @@ -610,6 +610,7 @@ public static WritableArray measureLines( includeFontPadding, textBreakStrategy, hyphenationFrequency); + return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context); } diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp index e39f6712784f26..55fe463dd1e738 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp @@ -226,15 +226,16 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById( return TextMeasurement{size, attachments}; } -LinesMeasurements TextLayoutManager::measureLines( +TextLayoutMeasurements TextLayoutManager::measureLines( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, - Size size) const { + Size size, + TextLayoutRegions textLayoutRegions) const { const jni::global_ref &fabricUIManager = contextContainer_->at>("FabricUIManager"); static auto measureLines = jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") - ->getMethodgetMethodconsume(); + auto dynamicObject = cthis(object)->consume(); LinesMeasurements lineMeasurements; - lineMeasurements.reserve(dynamicArray.size()); + lineMeasurements.reserve(dynamicObject.getDefault("lines").size()); + RegionsMeasurements regionsMeasurements; + regionsMeasurements.reserve(dynamicObject.getDefault("regions").size()); - for (auto const &data : dynamicArray) { + for (auto const &data : dynamicObject.getDefault("lines")) { lineMeasurements.push_back(LineMeasurement(data)); } + for (auto const &data : dynamicObject.getDefault("regions")) { + regionsMeasurements.push_back(RegionMeasurement(data)); + } + // Explicitly release smart pointers to free up space faster in JNI tables attributedStringMB.reset(); paragraphAttributesMB.reset(); - return lineMeasurements; + TextLayoutMeasurements textLayoutMeasurements = { + lineMeasurements, + regionsMeasurements, + }; + + return textLayoutMeasurements; } TextMeasurement TextLayoutManager::doMeasure( diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h index 0f5e070b942422..ce0b330f6d7e01 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -67,10 +67,11 @@ class TextLayoutManager { * Measures lines of `attributedString` using native text rendering * infrastructure. */ - LinesMeasurements measureLines( + TextLayoutMeasurements measureLines( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, - Size size) const; + Size size, + TextLayoutRegions textLayoutRegions) const; /* * Returns an opaque pointer to platform-specific TextLayoutManager. @@ -89,10 +90,11 @@ class TextLayoutManager { ParagraphAttributes const ¶graphAttributes, LayoutConstraints layoutConstraints) const; - LinesMeasurements measureLinesMapBuffer( + TextLayoutMeasurements measureLinesMapBuffer( AttributedString const &attributedString, ParagraphAttributes const ¶graphAttributes, - Size size) const; + Size size, + TextLayoutRegions textLayoutRegions) const; void *self_{}; ContextContainer::Shared contextContainer_; From 7050a889539273c9ba74cde8a1c4d925717fdab7 Mon Sep 17 00:00:00 2001 From: azimgd Date: Mon, 15 May 2023 16:36:21 +0500 Subject: [PATCH 11/19] fix android cxx typings and args --- .../com/facebook/react/fabric/FabricUIManager.java | 2 +- .../facebook/react/views/text/FontMetricsUtil.java | 12 ++++++++---- .../react/renderer/attributedstring/conversions.h | 13 +++++++++++++ .../renderer/textlayoutmanager/TextMeasureCache.h | 3 +-- .../platform/cxx/TextLayoutManager.cpp | 5 +++-- .../platform/cxx/TextLayoutManager.h | 5 +++-- 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 0915fe71b8dafd..bf631bb91eede4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -444,7 +444,7 @@ public void onCatalystInstanceDestroy() { @SuppressWarnings("unused") private NativeMap measureLines( - ReadableMap attributedString, ReadableMap paragraphAttributes, float width, float height) { + ReadableMap attributedString, ReadableMap paragraphAttributes, float width, float height, ReadableArray textLayoutRegions) { return (NativeMap) TextLayoutManager.measureLines( mReactApplicationContext, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java index f12ef50221d827..b820e86cef9d58 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java @@ -59,8 +59,8 @@ public static WritableMap getFontMetrics( line.putString( "text", text.subSequence(layout.getLineStart(i), layout.getLineEnd(i)).toString()); - int start = 10; - int end = 40; + int start = 0; + int end = 5; int lineStartIndex = layout.getLineStart(i); int lineEndIndex = layout.getLineEnd(i); int startIndex = Math.max(lineStartIndex, start); @@ -73,8 +73,12 @@ public static WritableMap getFontMetrics( Rect charBounds = new Rect(xStart, yStart, xEnd, yEnd); WritableMap region = Arguments.createMap(); - region.putDouble("width", charBounds.width()); - region.putDouble("height", charBounds.height()); + region.putDouble("x", charBounds.width() / dm.density); + region.putDouble("y", charBounds.height() / dm.density); + region.putDouble("width", 1.2); + region.putDouble("height", 1.2); + region.putString( + "text", text.subSequence(layout.getLineStart(i), layout.getLineEnd(i)).toString()); lines.pushMap(line); regions.pushMap(region); diff --git a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h index fb331548707cb7..a05b244636600b 100644 --- a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h @@ -860,6 +860,19 @@ inline void fromRawValue( } } +inline MapBuffer toMapBuffer(const TextLayoutRegions &textLayoutRegions) { + auto builder = MapBufferBuilder(); + + return builder.build(); +} + +inline folly::dynamic toDynamic( + const TextLayoutRegions &textLayoutRegions) { + auto values = folly::dynamic::object(); + + return values; +} + inline ParagraphAttributes convertRawProp( const PropsParserContext &context, RawProps const &rawProps, diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index 3a2be00be2caec..36e64c51958a30 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -59,8 +59,7 @@ struct RegionMeasurement { using LinesMeasurements = std::vector; using RegionsMeasurements = std::vector; -using TextLayoutRegion = std::vector; -using TextLayoutRegions = std::vector; +using TextLayoutRegions = std::vector>; struct TextLayoutMeasurements { LinesMeasurements linesMeasurements; diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp index 1284f3edfdd91a..5d04944ee056b8 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp @@ -29,10 +29,11 @@ TextMeasurement TextLayoutManager::measure( return TextMeasurement{{0, 0}, attachments}; } -LinesMeasurements TextLayoutManager::measureLines( +TextLayoutMeasurements TextLayoutManager::measureLines( AttributedString attributedString, ParagraphAttributes paragraphAttributes, - Size size) const { + Size size, + TextLayoutRegions textLayoutRegions) const { return {}; }; diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h index cb2b9ebcf0a80c..db44f4cbf40464 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h @@ -43,10 +43,11 @@ class TextLayoutManager { * Measures lines of `attributedString` using native text rendering * infrastructure. */ - LinesMeasurements measureLines( + TextLayoutMeasurements measureLines( AttributedString attributedString, ParagraphAttributes paragraphAttributes, - Size size) const; + Size size, + TextLayoutRegions textLayoutRegions) const; /* * Returns an opaque pointer to platform-specific TextLayoutManager. From 99dd8c7ff0c635dac7f798e26495944f562dc4b5 Mon Sep 17 00:00:00 2001 From: azimgd Date: Tue, 16 May 2023 12:10:47 +0500 Subject: [PATCH 12/19] finalize android implementation for measurelines --- .../react/fabric/FabricUIManager.java | 16 ++++-- .../react/views/text/FontMetricsUtil.java | 49 +++++++++++-------- .../react/views/text/ReactTextShadowNode.java | 12 ++--- .../react/views/text/TextLayoutManager.java | 7 ++- .../text/TextLayoutManagerMapBuffer.java | 6 ++- .../renderer/attributedstring/conversions.h | 16 +++++- .../textlayoutmanager/TextLayoutManager.cpp | 8 ++- 7 files changed, 75 insertions(+), 39 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index bf631bb91eede4..72b95c787a04d4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -51,6 +51,7 @@ import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.common.mapbuffer.ReadableMapBuffer; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.fabric.events.EventBeatManager; @@ -444,13 +445,18 @@ public void onCatalystInstanceDestroy() { @SuppressWarnings("unused") private NativeMap measureLines( - ReadableMap attributedString, ReadableMap paragraphAttributes, float width, float height, ReadableArray textLayoutRegions) { + ReadableMap attributedString, + ReadableMap paragraphAttributes, + float width, + float height, + MapBuffer textLayoutRegions) { return (NativeMap) TextLayoutManager.measureLines( mReactApplicationContext, attributedString, paragraphAttributes, - PixelUtil.toPixelFromDIP(width)); + PixelUtil.toPixelFromDIP(width), + textLayoutRegions); } @SuppressWarnings("unused") @@ -458,13 +464,15 @@ private NativeMap measureLinesMapBuffer( ReadableMapBuffer attributedString, ReadableMapBuffer paragraphAttributes, float width, - float height) { + float height, + ReadableMapBuffer textLayoutRegions) { return (NativeMap) TextLayoutManagerMapBuffer.measureLines( mReactApplicationContext, attributedString, paragraphAttributes, - PixelUtil.toPixelFromDIP(width)); + PixelUtil.toPixelFromDIP(width), + textLayoutRegions); } @SuppressWarnings("unused") diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java index b820e86cef9d58..a09abe44ac4b97 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java @@ -13,8 +13,10 @@ import android.text.TextPaint; import android.util.DisplayMetrics; import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; +import com.facebook.react.common.mapbuffer.MapBuffer; public class FontMetricsUtil { @@ -23,7 +25,7 @@ public class FontMetricsUtil { private static final float AMPLIFICATION_FACTOR = 100; public static WritableMap getFontMetrics( - CharSequence text, Layout layout, TextPaint paint, Context context) { + CharSequence text, Layout layout, TextPaint paint, Context context, MapBuffer textLayoutRegions) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); WritableArray lines = Arguments.createArray(); WritableArray regions = Arguments.createArray(); @@ -59,29 +61,34 @@ public static WritableMap getFontMetrics( line.putString( "text", text.subSequence(layout.getLineStart(i), layout.getLineEnd(i)).toString()); - int start = 0; - int end = 5; - int lineStartIndex = layout.getLineStart(i); - int lineEndIndex = layout.getLineEnd(i); - int startIndex = Math.max(lineStartIndex, start); - int endIndex = Math.min(lineEndIndex, end); - layout.getLineBounds(i, bounds); - int xStart = (int) layout.getPrimaryHorizontal(startIndex); - int xEnd = (int) layout.getPrimaryHorizontal(endIndex); - int yStart = bounds.top; - int yEnd = bounds.bottom; - Rect charBounds = new Rect(xStart, yStart, xEnd, yEnd); + for (int j = 0; j < textLayoutRegions.getCount(); j++) { + MapBuffer textLayoutRegion = textLayoutRegions.getMapBuffer(j); - WritableMap region = Arguments.createMap(); - region.putDouble("x", charBounds.width() / dm.density); - region.putDouble("y", charBounds.height() / dm.density); - region.putDouble("width", 1.2); - region.putDouble("height", 1.2); - region.putString( - "text", text.subSequence(layout.getLineStart(i), layout.getLineEnd(i)).toString()); + int startIndex = Math.max(layout.getLineStart(i), textLayoutRegion.getInt(0)); + int endIndex = Math.min(layout.getLineEnd(i), textLayoutRegion.getInt(1)); + + if (startIndex > endIndex) { + break; + } + + Rect regionBounds = new Rect( + (int)layout.getPrimaryHorizontal(startIndex), + bounds.top, + (int)layout.getPrimaryHorizontal(endIndex - 1), + bounds.bottom); + + WritableMap region = Arguments.createMap(); + region.putDouble("x", regionBounds.left / dm.density); + region.putDouble("y", regionBounds.top / dm.density); + region.putDouble("width", regionBounds.width() / dm.density); + region.putDouble("height", regionBounds.height() / dm.density); + region.putString("text", text.subSequence(startIndex, endIndex).toString()); + region.putInt("region", j); + region.putInt("line", i); + regions.pushMap(region); + } lines.pushMap(line); - regions.pushMap(region); } WritableMap textLayoutMetrics = Arguments.createMap(); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java index ea51a6b418d715..2002b48feaf081 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java @@ -109,14 +109,14 @@ public long measure( if (mShouldNotifyOnTextLayout) { ThemedReactContext themedReactContext = getThemedContext(); - WritableMap textLayoutMetrics = - FontMetricsUtil.getFontMetrics( - text, layout, sTextPaintInstance, themedReactContext); +// WritableMap textLayoutMetrics = +// FontMetricsUtil.getFontMetrics( +// text, layout, sTextPaintInstance, themedReactContext, mTextLayoutRegions); if (themedReactContext.hasActiveReactInstance()) { - themedReactContext - .getJSModule(RCTEventEmitter.class) - .receiveEvent(getReactTag(), "topTextLayout", textLayoutMetrics); +// themedReactContext +// .getJSModule(RCTEventEmitter.class) +// .receiveEvent(getReactTag(), "topTextLayout", textLayoutMetrics); } else { ReactSoftExceptionLogger.logSoftException( "ReactTextShadowNode", diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java index 88e42b0b3934b7..0aa135af2ef4f9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java @@ -32,6 +32,8 @@ import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.common.mapbuffer.MapBuffer; +import com.facebook.react.common.mapbuffer.ReadableMapBuffer; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactStylesDiffMap; import com.facebook.react.uimanager.ViewProps; @@ -568,7 +570,8 @@ public static WritableMap measureLines( @NonNull Context context, ReadableMap attributedString, ReadableMap paragraphAttributes, - float width) { + float width, + MapBuffer textLayoutRegions) { Spannable text = getOrCreateSpannableForText(context, attributedString, null); BoringLayout.Metrics boring = BoringLayout.isBoring(text, sTextPaintInstance); @@ -592,7 +595,7 @@ public static WritableMap measureLines( includeFontPadding, textBreakStrategy, hyphenationFrequency); - return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context); + return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context, textLayoutRegions); } // TODO T31905686: This class should be private diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java index 4ff9784808eb6b..759e9c1a230a70 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java @@ -27,6 +27,7 @@ import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReactNoCrashSoftException; import com.facebook.react.bridge.ReactSoftExceptionLogger; +import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.common.mapbuffer.MapBuffer; @@ -585,7 +586,8 @@ public static WritableMap measureLines( @NonNull Context context, MapBuffer attributedString, MapBuffer paragraphAttributes, - float width) { + float width, + MapBuffer textLayoutRegions) { Spannable text = getOrCreateSpannableForText(context, attributedString, null); BoringLayout.Metrics boring = BoringLayout.isBoring(text, sTextPaintInstance); @@ -611,7 +613,7 @@ public static WritableMap measureLines( textBreakStrategy, hyphenationFrequency); - return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context); + return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context, textLayoutRegions); } // TODO T31905686: This class should be private diff --git a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h index a05b244636600b..edaf23c888849d 100644 --- a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h @@ -861,9 +861,21 @@ inline void fromRawValue( } inline MapBuffer toMapBuffer(const TextLayoutRegions &textLayoutRegions) { - auto builder = MapBufferBuilder(); + auto regionsBuilder = MapBufferBuilder(); + unsigned regionIdx = 0; + + for (const auto ®ion : textLayoutRegions) { + unsigned chunkIdx = 0; + auto chunksBuilder = MapBufferBuilder(); + for (const auto &chunk : region) { + chunksBuilder.putInt(chunkIdx, chunk); + ++chunkIdx; + } + regionsBuilder.putMapBuffer(regionIdx, chunksBuilder.build()); + ++regionIdx; + } - return builder.build(); + return regionsBuilder.build(); } inline folly::dynamic toDynamic( diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp index 55fe463dd1e738..04e31a51a7eb12 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp @@ -239,19 +239,23 @@ TextLayoutMeasurements TextLayoutManager::measureLines( JReadableMapBuffer::javaobject, JReadableMapBuffer::javaobject, jfloat, - jfloat)>("measureLinesMapBuffer"); + jfloat, + JReadableMapBuffer::javaobject)>("measureLinesMapBuffer"); auto attributedStringMB = JReadableMapBuffer::createWithContents(toMapBuffer(attributedString)); auto paragraphAttributesMB = JReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes)); + auto textLayoutRegionsMB = + JReadableMapBuffer::createWithContents(toMapBuffer(textLayoutRegions)); auto object = measureLines( fabricUIManager, attributedStringMB.get(), paragraphAttributesMB.get(), size.width, - size.height); + size.height, + textLayoutRegionsMB.get()); auto dynamicObject = cthis(object)->consume(); LinesMeasurements lineMeasurements; From 014e21afb7ae824f82a24f9efe27998b164c23df Mon Sep 17 00:00:00 2001 From: azimgd Date: Tue, 16 May 2023 12:25:43 +0500 Subject: [PATCH 13/19] fixing ios build for undefined mapbuffer import --- .../renderer/attributedstring/conversions.h | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h index edaf23c888849d..ab5a2c7c951e05 100644 --- a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h @@ -860,24 +860,6 @@ inline void fromRawValue( } } -inline MapBuffer toMapBuffer(const TextLayoutRegions &textLayoutRegions) { - auto regionsBuilder = MapBufferBuilder(); - unsigned regionIdx = 0; - - for (const auto ®ion : textLayoutRegions) { - unsigned chunkIdx = 0; - auto chunksBuilder = MapBufferBuilder(); - for (const auto &chunk : region) { - chunksBuilder.putInt(chunkIdx, chunk); - ++chunkIdx; - } - regionsBuilder.putMapBuffer(regionIdx, chunksBuilder.build()); - ++regionIdx; - } - - return regionsBuilder.build(); -} - inline folly::dynamic toDynamic( const TextLayoutRegions &textLayoutRegions) { auto values = folly::dynamic::object(); @@ -1363,6 +1345,24 @@ inline MapBuffer toMapBuffer(const AttributedString &attributedString) { return builder.build(); } +inline MapBuffer toMapBuffer(const TextLayoutRegions &textLayoutRegions) { + auto regionsBuilder = MapBufferBuilder(); + unsigned regionIdx = 0; + + for (const auto ®ion : textLayoutRegions) { + unsigned chunkIdx = 0; + auto chunksBuilder = MapBufferBuilder(); + for (const auto &chunk : region) { + chunksBuilder.putInt(chunkIdx, chunk); + ++chunkIdx; + } + regionsBuilder.putMapBuffer(regionIdx, chunksBuilder.build()); + ++regionIdx; + } + + return regionsBuilder.build(); +} + #endif } // namespace react From dba8df35240d24b2802c0e04fb051e824ba89b61 Mon Sep 17 00:00:00 2001 From: azimgd Date: Tue, 16 May 2023 21:33:58 +0500 Subject: [PATCH 14/19] fix textshadownode prop setter --- .../react/views/text/ReactTextShadowNode.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java index 2002b48feaf081..d17d6f77d7cf11 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java @@ -25,6 +25,7 @@ import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.uimanager.NativeViewHierarchyOptimizer; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactShadowNode; @@ -59,7 +60,7 @@ public class ReactTextShadowNode extends ReactBaseTextShadowNode { private @Nullable Spannable mPreparedSpannableText; private boolean mShouldNotifyOnTextLayout; - private ReadableArray mTextLayoutRegions; + private MapBuffer mTextLayoutRegions; private final YogaMeasureFunction mTextMeasureFunction = new YogaMeasureFunction() { @@ -109,14 +110,14 @@ public long measure( if (mShouldNotifyOnTextLayout) { ThemedReactContext themedReactContext = getThemedContext(); -// WritableMap textLayoutMetrics = -// FontMetricsUtil.getFontMetrics( -// text, layout, sTextPaintInstance, themedReactContext, mTextLayoutRegions); + WritableMap textLayoutMetrics = + FontMetricsUtil.getFontMetrics( + text, layout, sTextPaintInstance, themedReactContext, mTextLayoutRegions); if (themedReactContext.hasActiveReactInstance()) { -// themedReactContext -// .getJSModule(RCTEventEmitter.class) -// .receiveEvent(getReactTag(), "topTextLayout", textLayoutMetrics); + themedReactContext + .getJSModule(RCTEventEmitter.class) + .receiveEvent(getReactTag(), "topTextLayout", textLayoutMetrics); } else { ReactSoftExceptionLogger.logSoftException( "ReactTextShadowNode", @@ -360,7 +361,7 @@ public void setShouldNotifyOnTextLayout(boolean shouldNotifyOnTextLayout) { @ReactProp(name = "textLayoutRegions") public void setTextLayoutRegions(ReadableArray textLayoutRegions) { - mTextLayoutRegions = textLayoutRegions; + mTextLayoutRegions = (MapBuffer) textLayoutRegions; } @Override From 3e15af7536f9ebf10e3233761f56ff1768485c69 Mon Sep 17 00:00:00 2001 From: azimgd Date: Wed, 17 May 2023 16:20:45 +0500 Subject: [PATCH 15/19] rntester demo implementation --- .../Libraries/Types/CoreEventTypes.js | 11 ++++ .../renderer/attributedstring/conversions.h | 13 ----- .../js/examples/Layout/LayoutEventsExample.js | 54 ++++++++++++++++++- 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/packages/react-native/Libraries/Types/CoreEventTypes.js b/packages/react-native/Libraries/Types/CoreEventTypes.js index a0234e95f26d16..bd9eb5cd8e017d 100644 --- a/packages/react-native/Libraries/Types/CoreEventTypes.js +++ b/packages/react-native/Libraries/Types/CoreEventTypes.js @@ -72,6 +72,16 @@ export type TextLayout = $ReadOnly<{| xHeight: number, |}>; +export type TextRegion = $ReadOnly<{| + text: string, + height: number, + width: number, + x: number, + y: number, + line: number, + region: number, +|}>; + export type LayoutEvent = SyntheticEvent< $ReadOnly<{| layout: Layout, @@ -81,6 +91,7 @@ export type LayoutEvent = SyntheticEvent< export type TextLayoutEvent = SyntheticEvent< $ReadOnly<{| lines: Array, + regions: Array, |}>, >; diff --git a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h index ab5a2c7c951e05..e4c895090d4557 100644 --- a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h @@ -830,12 +830,6 @@ inline void fromRawValue( result = HyphenationFrequency::None; } -inline std::string toString(const TextLayoutRegions &textLayoutRegions) { - LOG(ERROR) << "Unsupported HyphenationFrequency value"; - react_native_expect(false); - return "none"; -} - inline void fromRawValue( const PropsParserContext &context, const RawValue &value, @@ -860,13 +854,6 @@ inline void fromRawValue( } } -inline folly::dynamic toDynamic( - const TextLayoutRegions &textLayoutRegions) { - auto values = folly::dynamic::object(); - - return values; -} - inline ParagraphAttributes convertRawProp( const PropsParserContext &context, RawProps const &rawProps, diff --git a/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js b/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js index 016b0f6193ef9d..f00413388bd1a8 100644 --- a/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js +++ b/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js @@ -18,8 +18,11 @@ const { StyleSheet, Text, View, + ScrollView, } = require('react-native'); +import type {TextLayoutEvent} from 'react-native/Libraries/Types/CoreEventTypes'; + import type { ViewLayout, ViewLayoutEvent, @@ -31,6 +34,7 @@ type State = { extraText?: string, imageLayout?: ViewLayout, textLayout?: ViewLayout, + textLayoutRegions?: TextLayoutEvent["nativeEvent"]["regions"], viewLayout?: ViewLayout, viewStyle: {|margin: number|}, ... @@ -76,6 +80,11 @@ class LayoutEventExample extends React.Component { this.setState({textLayout: e.nativeEvent.layout}); }; + onTextRegionsLayout = (e: TextLayoutEvent) => { + console.log('received text layout regions event\n', e.nativeEvent); + this.setState({textLayoutRegions: e.nativeEvent.regions}); + }; + onImageLayout = (e: ViewLayoutEvent) => { console.log('received image layout event\n', e.nativeEvent); this.setState({imageLayout: e.nativeEvent.layout}); @@ -85,8 +94,10 @@ class LayoutEventExample extends React.Component { const viewStyle = [styles.view, this.state.viewStyle]; const textLayout = this.state.textLayout || {width: '?', height: '?'}; const imageLayout = this.state.imageLayout || {x: '?', y: '?'}; + const textRegions = this.state.textLayoutRegions || []; + return ( - + layout events are called on mount and whenever layout is recalculated. Note that the layout event will typically be received{' '} @@ -124,7 +135,45 @@ class LayoutEventExample extends React.Component { Image x/y: {imageLayout.x}/{imageLayout.y} - + + + + TextLayout Regions:{' '} + { + /* $FlowFixMe[incompatible-type] (>=0.95.0 site=react_native_fb) + * This comment suppresses an error found when Flow v0.95 was + * deployed. To see the error, delete this comment and run Flow. + */ + // $FlowFixMe[unsafe-addition] + JSON.stringify(textRegions, null, ' ') + '\n\n' + } + + + {textRegions.map((region, index) => ( + + ))} + + Lorem Ipsum is simply dummy text of the printing and typesetting + industry. Lorem Ipsum has been the industry's standard dummy text + ever since the 1500s, when an unknown printer took a galley of + type and scrambled it to make a type specimen book + + + + ); } } @@ -140,6 +189,7 @@ const styles = StyleSheet.create({ alignSelf: 'flex-start', borderColor: 'rgba(0, 0, 255, 0.2)', borderWidth: 0.5, + borderRadius: 4, }, image: { width: 50, From aebcf9de327e3f3e9b61a2961bf9fbfd54e706cc Mon Sep 17 00:00:00 2001 From: azimgd Date: Wed, 17 May 2023 16:35:32 +0500 Subject: [PATCH 16/19] fix linting error --- .../js/examples/Layout/LayoutEventsExample.js | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js b/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js index f00413388bd1a8..454ec359c2a13d 100644 --- a/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js +++ b/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js @@ -34,7 +34,7 @@ type State = { extraText?: string, imageLayout?: ViewLayout, textLayout?: ViewLayout, - textLayoutRegions?: TextLayoutEvent["nativeEvent"]["regions"], + textLayoutRegions?: TextLayoutEvent['nativeEvent']['regions'], viewLayout?: ViewLayout, viewStyle: {|margin: number|}, ... @@ -149,19 +149,18 @@ class LayoutEventExample extends React.Component { } - {textRegions.map((region, index) => ( - - ))} + {textRegions.map((region, index) => { + const regionStyle = { + width: region.width, + height: region.height, + top: region.y, + left: region.x, + }; + return ( + // eslint-disable-next-line react-native/no-inline-styles + + ); + })} Date: Wed, 17 May 2023 16:37:43 +0500 Subject: [PATCH 17/19] fix linting error --- packages/rn-tester/js/examples/Layout/LayoutEventsExample.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js b/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js index 454ec359c2a13d..250e65cea96980 100644 --- a/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js +++ b/packages/rn-tester/js/examples/Layout/LayoutEventsExample.js @@ -157,7 +157,6 @@ class LayoutEventExample extends React.Component { left: region.x, }; return ( - // eslint-disable-next-line react-native/no-inline-styles ); })} From c5e8005fa934358261b34b0ed13732c40b790e6d Mon Sep 17 00:00:00 2001 From: azimgd Date: Wed, 24 May 2023 16:28:11 +0500 Subject: [PATCH 18/19] include last char --- .../java/com/facebook/react/views/text/FontMetricsUtil.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java index a09abe44ac4b97..8d25d24b250613 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java @@ -71,10 +71,12 @@ public static WritableMap getFontMetrics( break; } + int offset = layout.getLineEnd(i) >= textLayoutRegion.getInt(1) ? 0 : 1; + Rect regionBounds = new Rect( (int)layout.getPrimaryHorizontal(startIndex), bounds.top, - (int)layout.getPrimaryHorizontal(endIndex - 1), + (int)layout.getPrimaryHorizontal(endIndex - offset), bounds.bottom); WritableMap region = Arguments.createMap(); From d6ef98610e08b44d40c62af54b84cfd317e925eb Mon Sep 17 00:00:00 2001 From: azimgd Date: Wed, 24 May 2023 17:25:32 +0500 Subject: [PATCH 19/19] fix androids multiline region selection --- .../react/views/text/FontMetricsUtil.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java index 8d25d24b250613..019573d4fb4baa 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/FontMetricsUtil.java @@ -61,22 +61,28 @@ public static WritableMap getFontMetrics( line.putString( "text", text.subSequence(layout.getLineStart(i), layout.getLineEnd(i)).toString()); - for (int j = 0; j < textLayoutRegions.getCount(); j++) { + lines.pushMap(line); + } + + for (int j = 0; j < textLayoutRegions.getCount(); j++) { + for (int i = 0; i < layout.getLineCount(); i++) { + Rect bounds = new Rect(); + layout.getLineBounds(i, bounds); + MapBuffer textLayoutRegion = textLayoutRegions.getMapBuffer(j); + int offset = layout.getLineEnd(i) >= textLayoutRegion.getInt(1) ? 0 : 1; int startIndex = Math.max(layout.getLineStart(i), textLayoutRegion.getInt(0)); - int endIndex = Math.min(layout.getLineEnd(i), textLayoutRegion.getInt(1)); + int endIndex = Math.min(layout.getLineEnd(i), textLayoutRegion.getInt(1)) - offset; - if (startIndex > endIndex) { + if (startIndex > endIndex + 1) { break; } - int offset = layout.getLineEnd(i) >= textLayoutRegion.getInt(1) ? 0 : 1; - Rect regionBounds = new Rect( (int)layout.getPrimaryHorizontal(startIndex), bounds.top, - (int)layout.getPrimaryHorizontal(endIndex - offset), + (int)layout.getPrimaryHorizontal(endIndex), bounds.bottom); WritableMap region = Arguments.createMap(); @@ -89,8 +95,6 @@ public static WritableMap getFontMetrics( region.putInt("line", i); regions.pushMap(region); } - - lines.pushMap(line); } WritableMap textLayoutMetrics = Arguments.createMap();