From eaa1752d4620b4687560d5c211e74ba588115c7d Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Mon, 17 Jun 2024 11:15:01 +0200 Subject: [PATCH 01/32] Setup baseline func --- .../components/text/ParagraphShadowNode.cpp | 4 ++++ .../components/text/ParagraphShadowNode.h | 3 +++ .../AndroidTextInputShadowNode.cpp | 4 ++++ .../AndroidTextInputShadowNode.h | 3 +++ .../iostextinput/TextInputShadowNode.cpp | 4 ++++ .../iostextinput/TextInputShadowNode.h | 3 +++ .../view/YogaLayoutableShadowNode.cpp | 20 +++++++++++++++++++ .../view/YogaLayoutableShadowNode.h | 4 ++++ .../renderer/core/LayoutableShadowNode.cpp | 4 ++++ .../renderer/core/LayoutableShadowNode.h | 6 ++++++ .../react/renderer/core/ShadowNodeTraits.h | 3 +++ 11 files changed, 58 insertions(+) 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 8d4a9b07aec9c8..d7f8278e4d5c83 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -163,6 +163,10 @@ Size ParagraphShadowNode::measureContent( .size; } +float ParagraphShadowNode::baseline(float width, float height) const { + return 0; +} + void ParagraphShadowNode::layout(LayoutContext layoutContext) { ensureUnsealed(); diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h index 0a1cffb621c7eb..2557ef9adf9fe7 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h @@ -43,6 +43,7 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode< auto traits = ConcreteViewShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::LeafYogaNode); traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode); + traits.set(ShadowNodeTraits::Trait::BaselineYogaNode); #ifdef ANDROID // Unsetting `FormsStackingContext` trait is essential on Android where we @@ -69,6 +70,8 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode< const LayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const override; + float baseline(float width, float height) const override; + /* * Internal representation of the nested content of the node in a format * suitable for future processing. diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index d15c7b32bbcf04..e1bf8a155c4820 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -214,6 +214,10 @@ Size AndroidTextInputShadowNode::measureContent( .size; } +float AndroidTextInputShadowNode::baseline(float width, float height) const { + return 0; +} + void AndroidTextInputShadowNode::layout(LayoutContext layoutContext) { updateStateIfNeeded(); ConcreteViewShadowNode::layout(layoutContext); diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h index 43a4df1e9edc1b..326c2f16612a3f 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h @@ -34,6 +34,7 @@ class AndroidTextInputShadowNode final static ShadowNodeTraits BaseTraits() { auto traits = ConcreteViewShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::LeafYogaNode); + traits.set(ShadowNodeTraits::Trait::BaselineYogaNode); return traits; } @@ -65,6 +66,8 @@ class AndroidTextInputShadowNode final const LayoutConstraints& layoutConstraints) const override; void layout(LayoutContext layoutContext) override; + float baseline(float width, float height) const override; + private: ContextContainer* contextContainer_{}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index b1ceb3eaa87931..29e56a40af633b 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -140,6 +140,10 @@ Size TextInputShadowNode::measureContent( .size; } +float TextInputShadowNode::baseline(float width, float height) const { + return 0; +} + void TextInputShadowNode::layout(LayoutContext layoutContext) { updateStateIfNeeded(layoutContext); ConcreteViewShadowNode::layout(layoutContext); diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h index 59e2db0ed56183..0c4df826a5f962 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h @@ -40,6 +40,7 @@ class TextInputShadowNode final : public ConcreteViewShadowNode< auto traits = ConcreteViewShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::LeafYogaNode); traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode); + traits.set(ShadowNodeTraits::Trait::BaselineYogaNode); return traits; } @@ -58,6 +59,8 @@ class TextInputShadowNode final : public ConcreteViewShadowNode< const LayoutConstraints& layoutConstraints) const override; void layout(LayoutContext layoutContext) override; + float baseline(float width, float height) const override; + private: /* * Creates a `State` object if needed. diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp index f7ee626491fa20..fa3110dd0a301b 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp @@ -81,6 +81,11 @@ YogaLayoutableShadowNode::YogaLayoutableShadowNode( YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector); } + if (getTraits().check(ShadowNodeTraits::Trait::BaselineYogaNode)) { + yogaNode_.setBaselineFunc( + YogaLayoutableShadowNode::yogaNodeBaselineCallbackConnector); + } + updateYogaProps(); updateYogaChildren(); @@ -845,6 +850,21 @@ YGSize YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector( yogaFloatFromFloat(size.width), yogaFloatFromFloat(size.height)}; } +float YogaLayoutableShadowNode::yogaNodeBaselineCallbackConnector( + YGNodeConstRef yogaNode, + float width, + float height) { + SystraceSection s( + "YogaLayoutableShadowNode::yogaNodeBaselineCallbackConnector"); + + auto& shadowNode = shadowNodeFromContext(yogaNode); + auto baseline = shadowNode.baseline( + floatFromYogaFloat(width), + floatFromYogaFloat(height)); + + return yogaFloatFromFloat(baseline); +} + YogaLayoutableShadowNode& YogaLayoutableShadowNode::shadowNodeFromContext( YGNodeConstRef yogaNode) { return dynamic_cast( diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h index 6ae363c987ca2f..4c1c5a02a2b702 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h @@ -164,6 +164,10 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode { YGMeasureMode widthMode, float height, YGMeasureMode heightMode); + static float yogaNodeBaselineCallbackConnector( + YGNodeConstRef yogaNode, + float width, + float height); static YogaLayoutableShadowNode& shadowNodeFromContext( YGNodeConstRef yogaNode); diff --git a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp index cef90f40b6923c..97ba97469f2994 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp @@ -225,6 +225,10 @@ Size LayoutableShadowNode::measureContent( return {}; } +float LayoutableShadowNode::baseline(float width, float height) const { + return 0; +} + Size LayoutableShadowNode::measure( const LayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const { diff --git a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h index d74a93ee686d53..23c0b29d5b67a0 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h @@ -82,6 +82,12 @@ class LayoutableShadowNode : public ShadowNode { virtual Size measureContent( const LayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const; + + /* + * Calculates the baseline of the node. + * Default implementation returns zero. + */ + virtual float baseline(float width, float height) const; /* * Measures the node with given `layoutContext` and `layoutConstraints`. diff --git a/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeTraits.h b/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeTraits.h index 25157f64167184..729207447c51ce 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeTraits.h +++ b/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeTraits.h @@ -75,6 +75,9 @@ class ShadowNodeTraits { // Indicates that direct children of the node should not be collapsed ChildrenFormStackingContext = 1 << 10, + + // Inherits `YogaLayoutableShadowNode` and has a custom baseline function. + BaselineYogaNode = 1 << 11, }; /* From ce3d20b6c8d018eb40bca87fd9f073bf23865668 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Mon, 17 Jun 2024 11:47:45 +0200 Subject: [PATCH 02/32] Add context --- .../react/renderer/components/text/ParagraphShadowNode.cpp | 5 ++++- .../react/renderer/components/text/ParagraphShadowNode.h | 5 ++++- .../renderer/components/iostextinput/TextInputShadowNode.cpp | 5 ++++- .../renderer/components/iostextinput/TextInputShadowNode.h | 5 ++++- .../renderer/components/view/YogaLayoutableShadowNode.cpp | 1 + .../ReactCommon/react/renderer/core/LayoutableShadowNode.cpp | 5 ++++- .../ReactCommon/react/renderer/core/LayoutableShadowNode.h | 5 ++++- 7 files changed, 25 insertions(+), 6 deletions(-) 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 d7f8278e4d5c83..49dd55d45362c5 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -163,7 +163,10 @@ Size ParagraphShadowNode::measureContent( .size; } -float ParagraphShadowNode::baseline(float width, float height) const { +float ParagraphShadowNode::baseline( + const LayoutContext& layoutContext, + float width, + float height) const { return 0; } diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h index 2557ef9adf9fe7..b078bf49a45b77 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h @@ -70,7 +70,10 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode< const LayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const override; - float baseline(float width, float height) const override; + float baseline( + const LayoutContext& layoutContext, + float width, + float height) const override; /* * Internal representation of the nested content of the node in a format diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index 29e56a40af633b..ea41e6036f5ac6 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -140,7 +140,10 @@ Size TextInputShadowNode::measureContent( .size; } -float TextInputShadowNode::baseline(float width, float height) const { +float TextInputShadowNode::baseline( + const LayoutContext& layoutContext, + float width, + float height) const { return 0; } diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h index 0c4df826a5f962..3568520aa07246 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h @@ -59,7 +59,10 @@ class TextInputShadowNode final : public ConcreteViewShadowNode< const LayoutConstraints& layoutConstraints) const override; void layout(LayoutContext layoutContext) override; - float baseline(float width, float height) const override; + float baseline( + const LayoutContext& layoutContext, + float width, + float height) const override; private: /* diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp index fa3110dd0a301b..d02d0d11d88204 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp @@ -859,6 +859,7 @@ float YogaLayoutableShadowNode::yogaNodeBaselineCallbackConnector( auto& shadowNode = shadowNodeFromContext(yogaNode); auto baseline = shadowNode.baseline( + threadLocalLayoutContext, floatFromYogaFloat(width), floatFromYogaFloat(height)); diff --git a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp index 97ba97469f2994..a063c608ec5353 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp @@ -225,7 +225,10 @@ Size LayoutableShadowNode::measureContent( return {}; } -float LayoutableShadowNode::baseline(float width, float height) const { +float LayoutableShadowNode::baseline( + const LayoutContext& /*layoutContext*/, + float /*width*/, + float /*height*/) const { return 0; } diff --git a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h index 23c0b29d5b67a0..14c73a7934b42d 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h @@ -87,7 +87,10 @@ class LayoutableShadowNode : public ShadowNode { * Calculates the baseline of the node. * Default implementation returns zero. */ - virtual float baseline(float width, float height) const; + virtual float baseline( + const LayoutContext& /*layoutContext*/, + float /*width*/, + float /*height*/) const; /* * Measures the node with given `layoutContext` and `layoutConstraints`. From 0f61afbdcc14187aa2779204e005f98009c8cb47 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Mon, 17 Jun 2024 11:57:30 +0200 Subject: [PATCH 03/32] Implement iOS --- .../components/text/ParagraphShadowNode.cpp | 9 +++++++- .../iostextinput/TextInputShadowNode.cpp | 6 ++++- .../textlayoutmanager/RCTTextLayoutManager.h | 5 ++++ .../textlayoutmanager/RCTTextLayoutManager.mm | 23 +++++++++++++++++++ .../textlayoutmanager/TextLayoutManager.h | 9 ++++++++ .../textlayoutmanager/TextLayoutManager.mm | 11 +++++++++ 6 files changed, 61 insertions(+), 2 deletions(-) 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 49dd55d45362c5..a156e978033ba1 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -167,7 +167,14 @@ float ParagraphShadowNode::baseline( const LayoutContext& layoutContext, float width, float height) const { - return 0; + auto content = getContent(layoutContext); + auto attributedString = content.attributedString; + + return textLayoutManager_ + ->baseline( + attributedString, + content.paragraphAttributes, + {.width = width, .height = height}); } void ParagraphShadowNode::layout(LayoutContext layoutContext) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index ea41e6036f5ac6..7e8c71c513d58a 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -144,7 +144,11 @@ float TextInputShadowNode::baseline( const LayoutContext& layoutContext, float width, float height) const { - return 0; + return textLayoutManager_ + ->baseline( + getAttributedString(layoutContext), + getConcreteProps().getEffectiveParagraphAttributes(), + {.width = width, .height = height}); } void TextInputShadowNode::layout(LayoutContext layoutContext) { 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 7ef23cc308c365..26b5da86f7565e 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 @@ -44,6 +44,11 @@ using RCTTextLayoutFragmentEnumerationBlock = (facebook::react::ParagraphAttributes)paragraphAttributes size:(CGSize)size; +- (float)getBaselineForAttributedString:(facebook::react::AttributedString)attributedString + paragraphAttributes: + (facebook::react::ParagraphAttributes)paragraphAttributes + size:(CGSize)size; + - (facebook::react::SharedEventEmitter) getEventEmitterWithAttributeString:(facebook::react::AttributedString)attributedString paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes 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 a50065a56cf416..33e24734957fa3 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 @@ -167,6 +167,29 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr return paragraphLines; } +- (float)getBaselineForAttributedString:(facebook::react::AttributedString)attributedString + paragraphAttributes: + (facebook::react::ParagraphAttributes)paragraphAttributes + size:(CGSize)size +{ + NSTextStorage *attributedText = [self + _textStorageAndLayoutManagerWithAttributesString:[self _nsAttributedStringFromAttributedString:attributedString] + paragraphAttributes:paragraphAttributes + size:size]; + __block CGFloat maximumDescender = 0.0; + + [attributedText enumerateAttribute:NSFontAttributeName + inRange:NSMakeRange(0, attributedText.length) + options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(UIFont *font, NSRange range, __unused BOOL *stop) { + if (maximumDescender > font.descender) { + maximumDescender = font.descender; + } + }]; + + return size.height + maximumDescender; +} + - (NSTextStorage *)_textStorageAndLayoutManagerWithAttributesString:(NSAttributedString *)attributedString paragraphAttributes:(ParagraphAttributes)paragraphAttributes size:(CGSize)size 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 30fd816eb59ebe..294f55e20ec736 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 @@ -45,6 +45,15 @@ class TextLayoutManager { ParagraphAttributes paragraphAttributes, Size size) const; + /* + * Calculates baseline of `attributedString` using native text rendering + * infrastructure. + */ + float baseline( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const; + /* * Returns an opaque pointer to platform-specific TextLayoutManager. * Is used on a native views layer to delegate text rendering to the manager. 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 4d9b0f3f89f8b4..a68ce18c6c1260 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 @@ -95,4 +95,15 @@ size:{size.width, size.height}]; } +float TextLayoutManager::baseline( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const +{ + RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_); + return [textLayoutManager getBaselineForAttributedString:attributedString + paragraphAttributes:paragraphAttributes + size:{size.width, size.height}]; +} + } // namespace facebook::react From 346b0e166712864e59059ee4cc6b100966f5eba4 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Mon, 17 Jun 2024 12:33:31 +0200 Subject: [PATCH 04/32] Implement Android --- .../react/fabric/FabricUIManager.java | 14 ++++ .../react/views/text/TextLayoutManager.java | 72 +++++++++++++++++++ .../AndroidTextInputShadowNode.cpp | 11 ++- .../AndroidTextInputShadowNode.h | 5 +- .../textlayoutmanager/TextLayoutManager.cpp | 29 ++++++++ .../textlayoutmanager/TextLayoutManager.h | 9 +++ .../platform/cxx/TextLayoutManager.cpp | 7 ++ .../platform/cxx/TextLayoutManager.h | 9 +++ 8 files changed, 153 insertions(+), 3 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 6c28d95ffa6744..ab36ebd01d6b6d 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 @@ -504,6 +504,20 @@ private NativeArray measureLines( PixelUtil.toPixelFromDIP(height)); } + @SuppressWarnings("unused") + private float baseline( + ReadableMapBuffer attributedString, + ReadableMapBuffer paragraphAttributes, + float width, + float height) { + return TextLayoutManager.getLastBaseline( + mReactApplicationContext, + attributedString, + paragraphAttributes, + PixelUtil.toPixelFromDIP(width), + PixelUtil.toPixelFromDIP(height)); + } + @SuppressWarnings("unused") private long measure( int rootTag, 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 4857780a3a212f..b49d31331b3583 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 @@ -744,4 +744,76 @@ public static WritableArray measureLines( createLayout(context, attributedString, paragraphAttributes, width, height, null); return FontMetricsUtil.getFontMetrics(layout.getText(), layout, sTextPaintInstance, context); } + + public static float getLastBaseline( + @NonNull Context context, + MapBuffer attributedString, + MapBuffer paragraphAttributes, + float width, + float height) { + + Spannable text = getOrCreateSpannableForText(context, attributedString, null); + BoringLayout.Metrics boring = BoringLayout.isBoring(text, sTextPaintInstance); + + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY)); + boolean includeFontPadding = + paragraphAttributes.contains(PA_KEY_INCLUDE_FONT_PADDING) + ? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING) + : DEFAULT_INCLUDE_FONT_PADDING; + int hyphenationFrequency = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(PA_KEY_HYPHENATION_FREQUENCY)); + boolean adjustFontSizeToFit = + paragraphAttributes.contains(PA_KEY_ADJUST_FONT_SIZE_TO_FIT) + ? paragraphAttributes.getBoolean(PA_KEY_ADJUST_FONT_SIZE_TO_FIT) + : DEFAULT_ADJUST_FONT_SIZE_TO_FIT; + int maximumNumberOfLines = + paragraphAttributes.contains(PA_KEY_MAX_NUMBER_OF_LINES) + ? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES) + : ReactConstants.UNSET; + + Layout.Alignment alignment = getTextAlignment(attributedString, text); + + if (adjustFontSizeToFit) { + double minimumFontSize = + paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SIZE) + ? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SIZE) + : Double.NaN; + + adjustSpannableFontToFit( + text, + width, + YogaMeasureMode.EXACTLY, + height, + YogaMeasureMode.UNDEFINED, + minimumFontSize, + maximumNumberOfLines, + includeFontPadding, + textBreakStrategy, + hyphenationFrequency, + alignment); + } + + Layout layout = + createLayout( + text, + boring, + width, + YogaMeasureMode.EXACTLY, + includeFontPadding, + textBreakStrategy, + hyphenationFrequency, + alignment); + + float maxDescent = 0; + for (int i = 0; i < layout.getLineCount(); i++) { + if (maxDescent < layout.getLineDescent(i)) { + maxDescent = layout.getLineDescent(i); + } + } + + return PixelUtil.toDIPFromPixel(height - maxDescent); + } } diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index e1bf8a155c4820..a20fa230c4e3a5 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -214,8 +214,15 @@ Size AndroidTextInputShadowNode::measureContent( .size; } -float AndroidTextInputShadowNode::baseline(float width, float height) const { - return 0; +float AndroidTextInputShadowNode::baseline( + const LayoutContext& layoutContext, + float width, + float height) const { + return textLayoutManager_ + ->baseline( + getMostRecentAttributedString(), + getConcreteProps().paragraphAttributes, + {.width = width, .height = height}); } void AndroidTextInputShadowNode::layout(LayoutContext layoutContext) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h index 326c2f16612a3f..56a2e12f70cb56 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h @@ -66,7 +66,10 @@ class AndroidTextInputShadowNode final const LayoutConstraints& layoutConstraints) const override; void layout(LayoutContext layoutContext) override; - float baseline(float width, float height) const override; + float baseline( + const LayoutContext& layoutContext, + float width, + float height) const override; private: ContextContainer* contextContainer_{}; 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 e3f4f08d384df2..44c3c343353da1 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 @@ -201,6 +201,35 @@ LinesMeasurements TextLayoutManager::measureLines( return lineMeasurements; } +float TextLayoutManager::baseline( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const { + const jni::global_ref& fabricUIManager = + contextContainer_->at>("FabricUIManager"); + static auto baseline = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("baseline"); + + auto attributedStringMB = + JReadableMapBuffer::createWithContents(toMapBuffer(attributedString)); + auto paragraphAttributesMB = + JReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes)); + + auto calculatedBaseline = baseline( + fabricUIManager, + attributedStringMB.get(), + paragraphAttributesMB.get(), + size.width, + size.height); + + return calculatedBaseline; +} + TextMeasurement TextLayoutManager::doMeasure( AttributedString attributedString, const ParagraphAttributes& paragraphAttributes, 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 ac5206985d3792..31a35d860bdf60 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,6 +67,15 @@ class TextLayoutManager { const ParagraphAttributes& paragraphAttributes, Size size) const; + /* + * Calculates baseline of `attributedString` using native text rendering + * infrastructure. + */ + float baseline( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const; + /* * Returns an opaque pointer to platform-specific TextLayoutManager. * Is used on a native views layer to delegate text rendering to the manager. 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 294e886d3c9cab..4ff2bd4bf7cf2d 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 @@ -42,4 +42,11 @@ LinesMeasurements TextLayoutManager::measureLines( return {}; }; +float TextLayoutManager::baseline( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const { + return 0; +} + } // namespace facebook::react 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 4b5eb7d1ff4692..33642fd2f93178 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 @@ -59,6 +59,15 @@ class TextLayoutManager { ParagraphAttributes paragraphAttributes, Size size) const; + /* + * Calculates baseline of `attributedString` using native text rendering + * infrastructure. + */ + virtual float baseline( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const; + /* * Returns an opaque pointer to platform-specific TextLayoutManager. * Is used on a native views layer to delegate text rendering to the manager. From b29ab4a5d1e2bde18a203987248584cc07575722 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 20 Jun 2024 16:03:55 +0200 Subject: [PATCH 05/32] Extract the common code --- .../react/views/text/TextLayoutManager.java | 117 ++++++++++-------- 1 file changed, 63 insertions(+), 54 deletions(-) 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 b49d31331b3583..df54d079d5d04a 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 @@ -556,6 +556,68 @@ public static void adjustSpannableFontToFit( } } + public static Layout createLayout( + @NonNull Context context, + MapBuffer attributedString, + MapBuffer paragraphAttributes, + float width, + float height, + ReactTextViewManagerCallback reactTextViewManagerCallback) { + Spannable text = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback); + BoringLayout.Metrics boring = BoringLayout.isBoring(text, sTextPaintInstance); + + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY)); + boolean includeFontPadding = + paragraphAttributes.contains(PA_KEY_INCLUDE_FONT_PADDING) + ? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING) + : DEFAULT_INCLUDE_FONT_PADDING; + int hyphenationFrequency = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(PA_KEY_HYPHENATION_FREQUENCY)); + boolean adjustFontSizeToFit = + paragraphAttributes.contains(PA_KEY_ADJUST_FONT_SIZE_TO_FIT) + ? paragraphAttributes.getBoolean(PA_KEY_ADJUST_FONT_SIZE_TO_FIT) + : DEFAULT_ADJUST_FONT_SIZE_TO_FIT; + int maximumNumberOfLines = + paragraphAttributes.contains(PA_KEY_MAX_NUMBER_OF_LINES) + ? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES) + : ReactConstants.UNSET; + + Layout.Alignment alignment = getTextAlignment(attributedString, text); + + if (adjustFontSizeToFit) { + double minimumFontSize = + paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SIZE) + ? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SIZE) + : Double.NaN; + + adjustSpannableFontToFit( + text, + width, + YogaMeasureMode.EXACTLY, + height, + YogaMeasureMode.UNDEFINED, + minimumFontSize, + maximumNumberOfLines, + includeFontPadding, + textBreakStrategy, + hyphenationFrequency, + alignment); + } + + return createLayout( + text, + boring, + width, + YogaMeasureMode.EXACTLY, + includeFontPadding, + textBreakStrategy, + hyphenationFrequency, + alignment); + } + public static long measureText( Context context, MapBuffer attributedString, @@ -752,60 +814,7 @@ public static float getLastBaseline( float width, float height) { - Spannable text = getOrCreateSpannableForText(context, attributedString, null); - BoringLayout.Metrics boring = BoringLayout.isBoring(text, sTextPaintInstance); - - int textBreakStrategy = - TextAttributeProps.getTextBreakStrategy( - paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY)); - boolean includeFontPadding = - paragraphAttributes.contains(PA_KEY_INCLUDE_FONT_PADDING) - ? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING) - : DEFAULT_INCLUDE_FONT_PADDING; - int hyphenationFrequency = - TextAttributeProps.getTextBreakStrategy( - paragraphAttributes.getString(PA_KEY_HYPHENATION_FREQUENCY)); - boolean adjustFontSizeToFit = - paragraphAttributes.contains(PA_KEY_ADJUST_FONT_SIZE_TO_FIT) - ? paragraphAttributes.getBoolean(PA_KEY_ADJUST_FONT_SIZE_TO_FIT) - : DEFAULT_ADJUST_FONT_SIZE_TO_FIT; - int maximumNumberOfLines = - paragraphAttributes.contains(PA_KEY_MAX_NUMBER_OF_LINES) - ? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES) - : ReactConstants.UNSET; - - Layout.Alignment alignment = getTextAlignment(attributedString, text); - - if (adjustFontSizeToFit) { - double minimumFontSize = - paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SIZE) - ? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SIZE) - : Double.NaN; - - adjustSpannableFontToFit( - text, - width, - YogaMeasureMode.EXACTLY, - height, - YogaMeasureMode.UNDEFINED, - minimumFontSize, - maximumNumberOfLines, - includeFontPadding, - textBreakStrategy, - hyphenationFrequency, - alignment); - } - - Layout layout = - createLayout( - text, - boring, - width, - YogaMeasureMode.EXACTLY, - includeFontPadding, - textBreakStrategy, - hyphenationFrequency, - alignment); + Layout layout = doTextStuff(context, attributedString, paragraphAttributes, width, height, null); float maxDescent = 0; for (int i = 0; i < layout.getLineCount(); i++) { From 9d7f2699b138cfcc17982e451ba6649065523096 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 21 Jun 2024 10:20:41 +0200 Subject: [PATCH 06/32] Use correct method name --- .../java/com/facebook/react/views/text/TextLayoutManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 df54d079d5d04a..460f938a291651 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 @@ -814,7 +814,7 @@ public static float getLastBaseline( float width, float height) { - Layout layout = doTextStuff(context, attributedString, paragraphAttributes, width, height, null); + Layout layout = createLayout(context, attributedString, paragraphAttributes, width, height, null); float maxDescent = 0; for (int i = 0; i < layout.getLineCount(); i++) { From c8c08c6d16e7a2503730f705763bc18471a73631 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 21 Jun 2024 10:21:00 +0200 Subject: [PATCH 07/32] Use `lastBaseline` method --- .../components/text/ParagraphShadowNode.cpp | 7 +++---- .../renderer/components/text/ParagraphShadowNode.h | 5 ++--- .../androidtextinput/AndroidTextInputShadowNode.cpp | 7 +++---- .../androidtextinput/AndroidTextInputShadowNode.h | 5 ++--- .../components/iostextinput/TextInputShadowNode.cpp | 7 +++---- .../components/iostextinput/TextInputShadowNode.h | 5 ++--- .../components/view/YogaLayoutableShadowNode.cpp | 6 +++--- .../react/renderer/core/LayoutableShadowNode.cpp | 11 ++--------- .../react/renderer/core/LayoutableShadowNode.h | 13 ++----------- 9 files changed, 22 insertions(+), 44 deletions(-) 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 a156e978033ba1..4b4ed6c08d88f3 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -163,10 +163,9 @@ Size ParagraphShadowNode::measureContent( .size; } -float ParagraphShadowNode::baseline( +Float ParagraphShadowNode::lastBaseline( const LayoutContext& layoutContext, - float width, - float height) const { + Size size) const { auto content = getContent(layoutContext); auto attributedString = content.attributedString; @@ -174,7 +173,7 @@ float ParagraphShadowNode::baseline( ->baseline( attributedString, content.paragraphAttributes, - {.width = width, .height = height}); + size); } void ParagraphShadowNode::layout(LayoutContext layoutContext) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h index b078bf49a45b77..a83b693caa1a17 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h @@ -70,10 +70,9 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode< const LayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const override; - float baseline( + Float lastBaseline( const LayoutContext& layoutContext, - float width, - float height) const override; + Size size) const override; /* * Internal representation of the nested content of the node in a format diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index a20fa230c4e3a5..9e54dc2b583174 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -214,15 +214,14 @@ Size AndroidTextInputShadowNode::measureContent( .size; } -float AndroidTextInputShadowNode::baseline( +Float AndroidTextInputShadowNode::lastBaseline( const LayoutContext& layoutContext, - float width, - float height) const { + Size size) const { return textLayoutManager_ ->baseline( getMostRecentAttributedString(), getConcreteProps().paragraphAttributes, - {.width = width, .height = height}); + size); } void AndroidTextInputShadowNode::layout(LayoutContext layoutContext) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h index 56a2e12f70cb56..9d62a8def9af5e 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h @@ -66,10 +66,9 @@ class AndroidTextInputShadowNode final const LayoutConstraints& layoutConstraints) const override; void layout(LayoutContext layoutContext) override; - float baseline( + Float lastBaseline( const LayoutContext& layoutContext, - float width, - float height) const override; + Size size) const override; private: ContextContainer* contextContainer_{}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index 7e8c71c513d58a..0f6cfa3305e228 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -140,15 +140,14 @@ Size TextInputShadowNode::measureContent( .size; } -float TextInputShadowNode::baseline( +Float TextInputShadowNode::lastBaseline( const LayoutContext& layoutContext, - float width, - float height) const { + Size size) const { return textLayoutManager_ ->baseline( getAttributedString(layoutContext), getConcreteProps().getEffectiveParagraphAttributes(), - {.width = width, .height = height}); + size); } void TextInputShadowNode::layout(LayoutContext layoutContext) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h index 3568520aa07246..0f22c2dd0e9a3b 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h @@ -59,10 +59,9 @@ class TextInputShadowNode final : public ConcreteViewShadowNode< const LayoutConstraints& layoutConstraints) const override; void layout(LayoutContext layoutContext) override; - float baseline( + Float lastBaseline( const LayoutContext& layoutContext, - float width, - float height) const override; + Size size) const override; private: /* diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp index d02d0d11d88204..d32dfe131b7e47 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp @@ -858,10 +858,10 @@ float YogaLayoutableShadowNode::yogaNodeBaselineCallbackConnector( "YogaLayoutableShadowNode::yogaNodeBaselineCallbackConnector"); auto& shadowNode = shadowNodeFromContext(yogaNode); - auto baseline = shadowNode.baseline( + auto baseline = shadowNode.lastBaseline( threadLocalLayoutContext, - floatFromYogaFloat(width), - floatFromYogaFloat(height)); + {.width = floatFromYogaFloat(width), + .height = floatFromYogaFloat(height)}); return yogaFloatFromFloat(baseline); } diff --git a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp index a063c608ec5353..eecd5b357628cf 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp @@ -225,13 +225,6 @@ Size LayoutableShadowNode::measureContent( return {}; } -float LayoutableShadowNode::baseline( - const LayoutContext& /*layoutContext*/, - float /*width*/, - float /*height*/) const { - return 0; -} - Size LayoutableShadowNode::measure( const LayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const { @@ -247,11 +240,11 @@ Size LayoutableShadowNode::measure( return layoutableShadowNode.getLayoutMetrics().frame.size; } -Float LayoutableShadowNode::firstBaseline(Size /*size*/) const { +Float LayoutableShadowNode::firstBaseline(const LayoutContext& /*layoutContext*/, Size /*size*/) const { return 0; } -Float LayoutableShadowNode::lastBaseline(Size /*size*/) const { +Float LayoutableShadowNode::lastBaseline(const LayoutContext& /*layoutContext*/, Size /*size*/) const { return 0; } diff --git a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h index 14c73a7934b42d..c761f28ebf8b06 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h @@ -82,15 +82,6 @@ class LayoutableShadowNode : public ShadowNode { virtual Size measureContent( const LayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const; - - /* - * Calculates the baseline of the node. - * Default implementation returns zero. - */ - virtual float baseline( - const LayoutContext& /*layoutContext*/, - float /*width*/, - float /*height*/) const; /* * Measures the node with given `layoutContext` and `layoutConstraints`. @@ -162,8 +153,8 @@ class LayoutableShadowNode : public ShadowNode { /* * Unifed methods to access text layout metrics. */ - virtual Float firstBaseline(Size size) const; - virtual Float lastBaseline(Size size) const; + virtual Float firstBaseline(const LayoutContext& layoutContext, Size size) const; + virtual Float lastBaseline(const LayoutContext& layoutContext, Size size) const; virtual bool canBeTouchTarget() const; virtual bool canChildrenBeTouchTarget() const; From 07f5ffeda98967eb99ff7d554e002ad657ad30b3 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 21 Jun 2024 10:35:43 +0200 Subject: [PATCH 08/32] Restore format --- .../java/com/facebook/react/views/text/TextLayoutManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 460f938a291651..90aba41c131992 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 @@ -814,7 +814,8 @@ public static float getLastBaseline( float width, float height) { - Layout layout = createLayout(context, attributedString, paragraphAttributes, width, height, null); + Layout layout = + createLayout(context, attributedString, paragraphAttributes, width, height, null); float maxDescent = 0; for (int i = 0; i < layout.getLineCount(); i++) { From 62931c91393c85ab2b51b8ec33888c4f89e8d9bd Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 21 Jun 2024 11:10:47 +0200 Subject: [PATCH 09/32] Rename baseline getter --- .../com/facebook/react/fabric/FabricUIManager.java | 2 +- .../renderer/components/text/ParagraphShadowNode.cpp | 2 +- .../androidtextinput/AndroidTextInputShadowNode.cpp | 2 +- .../components/iostextinput/TextInputShadowNode.cpp | 2 +- .../renderer/textlayoutmanager/TextLayoutManager.cpp | 10 +++++----- .../renderer/textlayoutmanager/TextLayoutManager.h | 2 +- .../platform/cxx/TextLayoutManager.cpp | 2 +- .../textlayoutmanager/platform/cxx/TextLayoutManager.h | 2 +- .../renderer/textlayoutmanager/TextLayoutManager.h | 2 +- .../renderer/textlayoutmanager/TextLayoutManager.mm | 2 +- 10 files changed, 14 insertions(+), 14 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 ab36ebd01d6b6d..988e02e3cdfa86 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 @@ -505,7 +505,7 @@ private NativeArray measureLines( } @SuppressWarnings("unused") - private float baseline( + private float getLastBaseline( ReadableMapBuffer attributedString, ReadableMapBuffer paragraphAttributes, float width, 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 4b4ed6c08d88f3..c73c05ca1f4c23 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -170,7 +170,7 @@ Float ParagraphShadowNode::lastBaseline( auto attributedString = content.attributedString; return textLayoutManager_ - ->baseline( + ->getLastBaseline( attributedString, content.paragraphAttributes, size); diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index 9e54dc2b583174..43ce4d7bf7268c 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -218,7 +218,7 @@ Float AndroidTextInputShadowNode::lastBaseline( const LayoutContext& layoutContext, Size size) const { return textLayoutManager_ - ->baseline( + ->getLastBaseline( getMostRecentAttributedString(), getConcreteProps().paragraphAttributes, size); diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index 0f6cfa3305e228..a2b8d64c3c1734 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -144,7 +144,7 @@ Float TextInputShadowNode::lastBaseline( const LayoutContext& layoutContext, Size size) const { return textLayoutManager_ - ->baseline( + ->getLastBaseline( getAttributedString(layoutContext), getConcreteProps().getEffectiveParagraphAttributes(), size); 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 44c3c343353da1..9a5c377d3402f8 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 @@ -201,33 +201,33 @@ LinesMeasurements TextLayoutManager::measureLines( return lineMeasurements; } -float TextLayoutManager::baseline( +float TextLayoutManager::getLastBaseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const { const jni::global_ref& fabricUIManager = contextContainer_->at>("FabricUIManager"); - static auto baseline = + static auto getLastBaseline = jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") ->getMethod("baseline"); + jfloat)>("getLastBaseline"); auto attributedStringMB = JReadableMapBuffer::createWithContents(toMapBuffer(attributedString)); auto paragraphAttributesMB = JReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes)); - auto calculatedBaseline = baseline( + auto lastBaseline = getLastBaseline( fabricUIManager, attributedStringMB.get(), paragraphAttributesMB.get(), size.width, size.height); - return calculatedBaseline; + return lastBaseline; } 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 31a35d860bdf60..d710ae63845fb8 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 @@ -71,7 +71,7 @@ class TextLayoutManager { * Calculates baseline of `attributedString` using native text rendering * infrastructure. */ - float baseline( + float getLastBaseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const; 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 4ff2bd4bf7cf2d..63a69cb822b261 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 @@ -42,7 +42,7 @@ LinesMeasurements TextLayoutManager::measureLines( return {}; }; -float TextLayoutManager::baseline( +float TextLayoutManager::getLastBaseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const { 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 33642fd2f93178..5762e0ef289ad4 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 @@ -63,7 +63,7 @@ class TextLayoutManager { * Calculates baseline of `attributedString` using native text rendering * infrastructure. */ - virtual float baseline( + virtual float getLastBaseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const; 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 294f55e20ec736..dc44304ef12b6b 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 @@ -49,7 +49,7 @@ class TextLayoutManager { * Calculates baseline of `attributedString` using native text rendering * infrastructure. */ - float baseline( + float getLastBaseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const; 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 a68ce18c6c1260..fcb9e176806a7d 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 @@ -95,7 +95,7 @@ size:{size.width, size.height}]; } -float TextLayoutManager::baseline( +float TextLayoutManager::getLastBaseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const From 4aa7b467b4f7815398bf68691849b5f4dedfa9f6 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 21 Jun 2024 11:12:00 +0200 Subject: [PATCH 10/32] Adjust indent --- .../facebook/react/fabric/FabricUIManager.java | 18 +++++++++--------- .../react/views/text/TextLayoutManager.java | 10 +++++----- 2 files changed, 14 insertions(+), 14 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 988e02e3cdfa86..41716e0c050877 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 @@ -506,16 +506,16 @@ private NativeArray measureLines( @SuppressWarnings("unused") private float getLastBaseline( - ReadableMapBuffer attributedString, - ReadableMapBuffer paragraphAttributes, - float width, - float height) { + ReadableMapBuffer attributedString, + ReadableMapBuffer paragraphAttributes, + float width, + float height) { return TextLayoutManager.getLastBaseline( - mReactApplicationContext, - attributedString, - paragraphAttributes, - PixelUtil.toPixelFromDIP(width), - PixelUtil.toPixelFromDIP(height)); + mReactApplicationContext, + attributedString, + paragraphAttributes, + PixelUtil.toPixelFromDIP(width), + PixelUtil.toPixelFromDIP(height)); } @SuppressWarnings("unused") 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 90aba41c131992..04680e2bfc8d65 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 @@ -808,11 +808,11 @@ public static WritableArray measureLines( } public static float getLastBaseline( - @NonNull Context context, - MapBuffer attributedString, - MapBuffer paragraphAttributes, - float width, - float height) { + @NonNull Context context, + MapBuffer attributedString, + MapBuffer paragraphAttributes, + float width, + float height) { Layout layout = createLayout(context, attributedString, paragraphAttributes, width, height, null); From 00ccf02e74fc5de40942290596cf3200e631f9e0 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 21 Jun 2024 22:16:40 +0200 Subject: [PATCH 11/32] Remove duplicate method --- .../react/views/text/TextLayoutManager.java | 62 ------------------- 1 file changed, 62 deletions(-) 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 04680e2bfc8d65..0b076ccacf9769 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 @@ -556,68 +556,6 @@ public static void adjustSpannableFontToFit( } } - public static Layout createLayout( - @NonNull Context context, - MapBuffer attributedString, - MapBuffer paragraphAttributes, - float width, - float height, - ReactTextViewManagerCallback reactTextViewManagerCallback) { - Spannable text = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback); - BoringLayout.Metrics boring = BoringLayout.isBoring(text, sTextPaintInstance); - - int textBreakStrategy = - TextAttributeProps.getTextBreakStrategy( - paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY)); - boolean includeFontPadding = - paragraphAttributes.contains(PA_KEY_INCLUDE_FONT_PADDING) - ? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING) - : DEFAULT_INCLUDE_FONT_PADDING; - int hyphenationFrequency = - TextAttributeProps.getTextBreakStrategy( - paragraphAttributes.getString(PA_KEY_HYPHENATION_FREQUENCY)); - boolean adjustFontSizeToFit = - paragraphAttributes.contains(PA_KEY_ADJUST_FONT_SIZE_TO_FIT) - ? paragraphAttributes.getBoolean(PA_KEY_ADJUST_FONT_SIZE_TO_FIT) - : DEFAULT_ADJUST_FONT_SIZE_TO_FIT; - int maximumNumberOfLines = - paragraphAttributes.contains(PA_KEY_MAX_NUMBER_OF_LINES) - ? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES) - : ReactConstants.UNSET; - - Layout.Alignment alignment = getTextAlignment(attributedString, text); - - if (adjustFontSizeToFit) { - double minimumFontSize = - paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SIZE) - ? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SIZE) - : Double.NaN; - - adjustSpannableFontToFit( - text, - width, - YogaMeasureMode.EXACTLY, - height, - YogaMeasureMode.UNDEFINED, - minimumFontSize, - maximumNumberOfLines, - includeFontPadding, - textBreakStrategy, - hyphenationFrequency, - alignment); - } - - return createLayout( - text, - boring, - width, - YogaMeasureMode.EXACTLY, - includeFontPadding, - textBreakStrategy, - hyphenationFrequency, - alignment); - } - public static long measureText( Context context, MapBuffer attributedString, From f5764130406d507d3705752956d7dd8410bf0136 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 25 Jun 2024 15:39:47 +0200 Subject: [PATCH 12/32] Unify baseline functions --- .../react/renderer/components/text/ParagraphShadowNode.cpp | 2 +- .../react/renderer/components/text/ParagraphShadowNode.h | 2 +- .../androidtextinput/AndroidTextInputShadowNode.cpp | 2 +- .../androidtextinput/AndroidTextInputShadowNode.h | 2 +- .../components/iostextinput/TextInputShadowNode.cpp | 2 +- .../renderer/components/iostextinput/TextInputShadowNode.h | 2 +- .../renderer/components/view/YogaLayoutableShadowNode.cpp | 2 +- .../react/renderer/core/LayoutableShadowNode.cpp | 6 +----- .../ReactCommon/react/renderer/core/LayoutableShadowNode.h | 3 +-- 9 files changed, 9 insertions(+), 14 deletions(-) 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 c73c05ca1f4c23..da36b061b73388 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -163,7 +163,7 @@ Size ParagraphShadowNode::measureContent( .size; } -Float ParagraphShadowNode::lastBaseline( +Float ParagraphShadowNode::baseline( const LayoutContext& layoutContext, Size size) const { auto content = getContent(layoutContext); diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h index a83b693caa1a17..93b639db23f3f1 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.h @@ -70,7 +70,7 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode< const LayoutContext& layoutContext, const LayoutConstraints& layoutConstraints) const override; - Float lastBaseline( + Float baseline( const LayoutContext& layoutContext, Size size) const override; diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index 43ce4d7bf7268c..6bfd259b70adc1 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -214,7 +214,7 @@ Size AndroidTextInputShadowNode::measureContent( .size; } -Float AndroidTextInputShadowNode::lastBaseline( +Float AndroidTextInputShadowNode::baseline( const LayoutContext& layoutContext, Size size) const { return textLayoutManager_ diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h index 9d62a8def9af5e..bd2fb2e6e39cca 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h @@ -66,7 +66,7 @@ class AndroidTextInputShadowNode final const LayoutConstraints& layoutConstraints) const override; void layout(LayoutContext layoutContext) override; - Float lastBaseline( + Float baseline( const LayoutContext& layoutContext, Size size) const override; diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index a2b8d64c3c1734..fc26ba0b17f81c 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -140,7 +140,7 @@ Size TextInputShadowNode::measureContent( .size; } -Float TextInputShadowNode::lastBaseline( +Float TextInputShadowNode::baseline( const LayoutContext& layoutContext, Size size) const { return textLayoutManager_ diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h index 0f22c2dd0e9a3b..44204cddbf8a49 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.h @@ -59,7 +59,7 @@ class TextInputShadowNode final : public ConcreteViewShadowNode< const LayoutConstraints& layoutConstraints) const override; void layout(LayoutContext layoutContext) override; - Float lastBaseline( + Float baseline( const LayoutContext& layoutContext, Size size) const override; diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp index d32dfe131b7e47..8ffbfc5ba608dc 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp @@ -858,7 +858,7 @@ float YogaLayoutableShadowNode::yogaNodeBaselineCallbackConnector( "YogaLayoutableShadowNode::yogaNodeBaselineCallbackConnector"); auto& shadowNode = shadowNodeFromContext(yogaNode); - auto baseline = shadowNode.lastBaseline( + auto baseline = shadowNode.baseline( threadLocalLayoutContext, {.width = floatFromYogaFloat(width), .height = floatFromYogaFloat(height)}); diff --git a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp index eecd5b357628cf..5e5ccfe6e37697 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp @@ -240,11 +240,7 @@ Size LayoutableShadowNode::measure( return layoutableShadowNode.getLayoutMetrics().frame.size; } -Float LayoutableShadowNode::firstBaseline(const LayoutContext& /*layoutContext*/, Size /*size*/) const { - return 0; -} - -Float LayoutableShadowNode::lastBaseline(const LayoutContext& /*layoutContext*/, Size /*size*/) const { +Float LayoutableShadowNode::baseline(const LayoutContext& /*layoutContext*/, Size /*size*/) const { return 0; } diff --git a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h index c761f28ebf8b06..9c21a144fa027a 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h +++ b/packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h @@ -153,8 +153,7 @@ class LayoutableShadowNode : public ShadowNode { /* * Unifed methods to access text layout metrics. */ - virtual Float firstBaseline(const LayoutContext& layoutContext, Size size) const; - virtual Float lastBaseline(const LayoutContext& layoutContext, Size size) const; + virtual Float baseline(const LayoutContext& layoutContext, Size size) const; virtual bool canBeTouchTarget() const; virtual bool canChildrenBeTouchTarget() const; From 0a0d4b50f8fe048925776ae9072a1dcc2ad4868a Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 25 Jun 2024 16:12:16 +0200 Subject: [PATCH 13/32] Align RNTester examples --- .../js/examples/Text/TextExample.android.js | 35 ++++++++++++++++++- .../js/examples/Text/TextExample.ios.js | 3 -- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/rn-tester/js/examples/Text/TextExample.android.js b/packages/rn-tester/js/examples/Text/TextExample.android.js index c8acafe10e96a8..dade04167d9252 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.android.js +++ b/packages/rn-tester/js/examples/Text/TextExample.android.js @@ -18,7 +18,7 @@ import TextInlineViewsExample from './TextInlineViewsExample'; const TextInlineView = require('../../components/TextInlineView'); const React = require('react'); -const {LayoutAnimation, StyleSheet, Text, View} = require('react-native'); +const {LayoutAnimation, StyleSheet, Text, TextInput, View} = require('react-native'); class Entity extends React.Component<{|children: React.Node|}> { render(): React.Node { @@ -958,6 +958,39 @@ function TextBaseLineLayoutExample(props: {}): React.Node { {marker} + + + {'Multi-line interleaved and :'} + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris + venenatis,{' '} + + mauris eu commodo maximus + {' '} + , ante arcu vestibulum ligula, et scelerisque diam. + + + + {':'} + + {marker} + {texts} + {marker} + + + {':'} + + {marker} + + {texts} + + {marker} + ); } diff --git a/packages/rn-tester/js/examples/Text/TextExample.ios.js b/packages/rn-tester/js/examples/Text/TextExample.ios.js index 59d13fe8959a2a..2f01593d4945e5 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.ios.js +++ b/packages/rn-tester/js/examples/Text/TextExample.ios.js @@ -308,9 +308,6 @@ class TextBaseLineLayoutExample extends React.Component<{}, mixed> { {marker} - {/* iOS-only because it relies on inline views being able to size to content. - * Android's implementation requires that a width and height be specified - * on the inline view. */} {'Interleaving and :'} {marker} From 57b7fbeabc3b5ddec02edaea0d1f2bb3dfab9f30 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 25 Jun 2024 16:24:50 +0200 Subject: [PATCH 14/32] Remove interleaving --- .../js/examples/Text/TextExample.android.js | 36 ------------------- .../js/examples/Text/TextExample.ios.js | 3 ++ 2 files changed, 3 insertions(+), 36 deletions(-) diff --git a/packages/rn-tester/js/examples/Text/TextExample.android.js b/packages/rn-tester/js/examples/Text/TextExample.android.js index dade04167d9252..feea0b23c3024c 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.android.js +++ b/packages/rn-tester/js/examples/Text/TextExample.android.js @@ -940,42 +940,6 @@ function TextBaseLineLayoutExample(props: {}): React.Node { {marker} - {'Interleaving and :'} - - {marker} - - Some text. - - {marker} - Text inside View. - {marker} - - - {marker} - - - - {'Multi-line interleaved and :'} - - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris - venenatis,{' '} - - mauris eu commodo maximus - {' '} - , ante arcu vestibulum ligula, et scelerisque diam. - - - {':'} {marker} diff --git a/packages/rn-tester/js/examples/Text/TextExample.ios.js b/packages/rn-tester/js/examples/Text/TextExample.ios.js index 2f01593d4945e5..59d13fe8959a2a 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.ios.js +++ b/packages/rn-tester/js/examples/Text/TextExample.ios.js @@ -308,6 +308,9 @@ class TextBaseLineLayoutExample extends React.Component<{}, mixed> { {marker} + {/* iOS-only because it relies on inline views being able to size to content. + * Android's implementation requires that a width and height be specified + * on the inline view. */} {'Interleaving and :'} {marker} From bf798be5b5018674efd3c37424ab8c1c6ef41c97 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 25 Jun 2024 16:29:33 +0200 Subject: [PATCH 15/32] I removed too much --- .../js/examples/Text/TextExample.android.js | 36 +++++++++++++++++++ .../js/examples/Text/TextExample.ios.js | 3 -- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/packages/rn-tester/js/examples/Text/TextExample.android.js b/packages/rn-tester/js/examples/Text/TextExample.android.js index feea0b23c3024c..dade04167d9252 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.android.js +++ b/packages/rn-tester/js/examples/Text/TextExample.android.js @@ -940,6 +940,42 @@ function TextBaseLineLayoutExample(props: {}): React.Node { {marker} + {'Interleaving and :'} + + {marker} + + Some text. + + {marker} + Text inside View. + {marker} + + + {marker} + + + + {'Multi-line interleaved and :'} + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris + venenatis,{' '} + + mauris eu commodo maximus + {' '} + , ante arcu vestibulum ligula, et scelerisque diam. + + + {':'} {marker} diff --git a/packages/rn-tester/js/examples/Text/TextExample.ios.js b/packages/rn-tester/js/examples/Text/TextExample.ios.js index 59d13fe8959a2a..2f01593d4945e5 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.ios.js +++ b/packages/rn-tester/js/examples/Text/TextExample.ios.js @@ -308,9 +308,6 @@ class TextBaseLineLayoutExample extends React.Component<{}, mixed> { {marker} - {/* iOS-only because it relies on inline views being able to size to content. - * Android's implementation requires that a width and height be specified - * on the inline view. */} {'Interleaving and :'} {marker} From 0ea302c13a7a0131da39163863a0b56ccc6e1c43 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Wed, 26 Jun 2024 14:33:04 +0200 Subject: [PATCH 16/32] Implement `baseline` on top of `measureLines` --- .../components/text/ParagraphShadowNode.cpp | 17 ++++--- .../textlayoutmanager/TextMeasureCache.h | 44 +++++++++++++++++++ .../textlayoutmanager/TextLayoutManager.cpp | 23 ++++++++-- .../textlayoutmanager/TextLayoutManager.h | 8 +++- .../textlayoutmanager/TextLayoutManager.h | 3 +- .../textlayoutmanager/TextLayoutManager.mm | 15 +++++-- 6 files changed, 96 insertions(+), 14 deletions(-) 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 da36b061b73388..4ea721a8d813a9 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -169,11 +169,18 @@ Float ParagraphShadowNode::baseline( auto content = getContent(layoutContext); auto attributedString = content.attributedString; - return textLayoutManager_ - ->getLastBaseline( - attributedString, - content.paragraphAttributes, - size); + auto lines = textLayoutManager_ + ->measureLines(attributedString, content.paragraphAttributes, size); + + float maximumDescender = 0; + + for (const auto &line : lines) { + if (line.descender > maximumDescender) { + maximumDescender = line.descender; + } + } + + return size.height - maximumDescender; } void ParagraphShadowNode::layout(LayoutContext layoutContext) { diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index 01a981ba0c37f6..4a0fd56be48720 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -66,6 +66,16 @@ class TextMeasureCacheKey final { LayoutConstraints layoutConstraints{}; }; +// The Key type that is used for Line Measure Cache. +// The equivalence and hashing operations of this are defined to respect the +// nature of text measuring. +class LineMeasureCacheKey final { + public: + AttributedString attributedString{}; + ParagraphAttributes paragraphAttributes{}; + Size size{}; +}; + /* * Maximum size of the Cache. * The number was empirically chosen based on approximation of an average amount @@ -82,6 +92,15 @@ using TextMeasureCache = SimpleThreadSafeCache< TextMeasurement, kSimpleThreadSafeCacheSizeCap>; +/* + * Thread-safe, evicting hash table designed to store line measurement + * information. + */ +using LineMeasureCache = SimpleThreadSafeCache< + LineMeasureCacheKey, + LinesMeasurements, + kSimpleThreadSafeCacheSizeCap>; + inline bool areTextAttributesEquivalentLayoutWise( const TextAttributes& lhs, const TextAttributes& rhs) { @@ -199,6 +218,21 @@ inline bool operator!=( return !(lhs == rhs); } +inline bool operator==( + const LineMeasureCacheKey& lhs, + const LineMeasureCacheKey& rhs) { + return areAttributedStringsEquivalentLayoutWise( + lhs.attributedString, rhs.attributedString) && + lhs.paragraphAttributes == rhs.paragraphAttributes && + lhs.size == rhs.size; +} + +inline bool operator!=( + const LineMeasureCacheKey& lhs, + const LineMeasureCacheKey& rhs) { + return !(lhs == rhs); +} + } // namespace facebook::react namespace std { @@ -213,4 +247,14 @@ struct hash { } }; +template <> +struct hash { + size_t operator()(const facebook::react::LineMeasureCacheKey& key) const { + return facebook::react::hash_combine( + attributedStringHashLayoutWise(key.attributedString), + key.paragraphAttributes, + key.size); + } +}; + } // namespace std 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 9a5c377d3402f8..331356a27fcd46 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 @@ -89,7 +89,8 @@ Size measureAndroidComponent( TextLayoutManager::TextLayoutManager( const ContextContainer::Shared& contextContainer) : contextContainer_(contextContainer), - measureCache_(kSimpleThreadSafeCacheSizeCap) {} + textMeasureCache_(kSimpleThreadSafeCacheSizeCap), + lineMeasureCache_(kSimpleThreadSafeCacheSizeCap) {} void* TextLayoutManager::getNativeTextLayoutManager() const { return self_; @@ -102,7 +103,7 @@ TextMeasurement TextLayoutManager::measure( LayoutConstraints layoutConstraints) const { auto& attributedString = attributedStringBox.getValue(); - auto measurement = measureCache_.get( + auto measurement = textMeasureCache_.get( {attributedString, paragraphAttributes, layoutConstraints}, [&](const TextMeasureCacheKey& /*key*/) { auto telemetry = TransactionTelemetry::threadLocalTelemetry(); @@ -160,7 +161,7 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById( return TextMeasurement{size, attachments}; } -LinesMeasurements TextLayoutManager::measureLines( +LinesMeasurements TextLayoutManager::doMeasureLines( const AttributedString& attributedString, const ParagraphAttributes& paragraphAttributes, Size size) const { @@ -201,6 +202,22 @@ LinesMeasurements TextLayoutManager::measureLines( return lineMeasurements; } +LinesMeasurements TextLayoutManager::measureLines( + const AttributedString& attributedString, + const ParagraphAttributes& paragraphAttributes, + Size size) const { + auto lineMeasurements = lineMeasureCache_.get( + {attributedString, paragraphAttributes, size}, + [&](const LineMeasureCacheKey& /*key*/) { + auto measurement = + doMeasureLines(attributedString, paragraphAttributes, size); + + return measurement; + }); + + return lineMeasurements; +} + float TextLayoutManager::getLastBaseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, 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 d710ae63845fb8..4fd248411dba24 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 @@ -88,9 +88,15 @@ class TextLayoutManager { const ParagraphAttributes& paragraphAttributes, LayoutConstraints layoutConstraints) const; + LinesMeasurements doMeasureLines( + const AttributedString& attributedString, + const ParagraphAttributes& paragraphAttributes, + Size size) const; + void* self_{}; ContextContainer::Shared contextContainer_; - TextMeasureCache measureCache_; + TextMeasureCache textMeasureCache_; + LineMeasureCache lineMeasureCache_; }; } // namespace facebook::react 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 dc44304ef12b6b..7736483c764af0 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 @@ -62,7 +62,8 @@ class TextLayoutManager { private: std::shared_ptr self_; - TextMeasureCache measureCache_{}; + TextMeasureCache textMeasureCache_{}; + LineMeasureCache lineMeasureCache_{}; }; } // namespace facebook::react 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 fcb9e176806a7d..2873b2a6a1bb27 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 @@ -38,7 +38,7 @@ case AttributedStringBox::Mode::Value: { auto &attributedString = attributedStringBox.getValue(); - measurement = measureCache_.get( + measurement = textMeasureCache_.get( {attributedString, paragraphAttributes, layoutConstraints}, [&](const TextMeasureCacheKey &key) { auto telemetry = TransactionTelemetry::threadLocalTelemetry(); if (telemetry) { @@ -90,9 +90,16 @@ Size size) const { RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_); - return [textLayoutManager getLinesForAttributedString:attributedString - paragraphAttributes:paragraphAttributes - size:{size.width, size.height}]; + + auto measurement = lineMeasureCache_.get( + {attributedString, paragraphAttributes, size}, [&](const LineMeasureCacheKey &key) { + auto measurement = [textLayoutManager getLinesForAttributedString:attributedString + paragraphAttributes:paragraphAttributes + size:{size.width, size.height}]; + return measurement; + }); + + return measurement; } float TextLayoutManager::getLastBaseline( From c29777c2511e85e696d499cb3c0bbd144e63dd30 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Wed, 26 Jun 2024 14:53:04 +0200 Subject: [PATCH 17/32] Remove `getLastBaseline` fromUnify baseline calculations --- .../react/fabric/FabricUIManager.java | 14 -------- .../react/views/text/TextLayoutManager.java | 20 ----------- .../components/text/ParagraphShadowNode.cpp | 2 +- .../textlayoutmanager/TextLayoutManager.cpp | 35 +++++++------------ .../textlayoutmanager/TextLayoutManager.mm | 16 ++++++--- 5 files changed, 25 insertions(+), 62 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 41716e0c050877..6c28d95ffa6744 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 @@ -504,20 +504,6 @@ private NativeArray measureLines( PixelUtil.toPixelFromDIP(height)); } - @SuppressWarnings("unused") - private float getLastBaseline( - ReadableMapBuffer attributedString, - ReadableMapBuffer paragraphAttributes, - float width, - float height) { - return TextLayoutManager.getLastBaseline( - mReactApplicationContext, - attributedString, - paragraphAttributes, - PixelUtil.toPixelFromDIP(width), - PixelUtil.toPixelFromDIP(height)); - } - @SuppressWarnings("unused") private long measure( int rootTag, 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 0b076ccacf9769..4857780a3a212f 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 @@ -744,24 +744,4 @@ public static WritableArray measureLines( createLayout(context, attributedString, paragraphAttributes, width, height, null); return FontMetricsUtil.getFontMetrics(layout.getText(), layout, sTextPaintInstance, context); } - - public static float getLastBaseline( - @NonNull Context context, - MapBuffer attributedString, - MapBuffer paragraphAttributes, - float width, - float height) { - - Layout layout = - createLayout(context, attributedString, paragraphAttributes, width, height, null); - - float maxDescent = 0; - for (int i = 0; i < layout.getLineCount(); i++) { - if (maxDescent < layout.getLineDescent(i)) { - maxDescent = layout.getLineDescent(i); - } - } - - return PixelUtil.toDIPFromPixel(height - maxDescent); - } } 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 4ea721a8d813a9..0bb8672bc38405 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -179,7 +179,7 @@ Float ParagraphShadowNode::baseline( maximumDescender = line.descender; } } - + return size.height - maximumDescender; } 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 331356a27fcd46..b742e1bea812ea 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 @@ -222,29 +222,18 @@ float TextLayoutManager::getLastBaseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const { - const jni::global_ref& fabricUIManager = - contextContainer_->at>("FabricUIManager"); - static auto getLastBaseline = - jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") - ->getMethod("getLastBaseline"); - - auto attributedStringMB = - JReadableMapBuffer::createWithContents(toMapBuffer(attributedString)); - auto paragraphAttributesMB = - JReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes)); - - auto lastBaseline = getLastBaseline( - fabricUIManager, - attributedStringMB.get(), - paragraphAttributesMB.get(), - size.width, - size.height); - - return lastBaseline; + auto lines = this + ->measureLines(attributedString, paragraphAttributes, size); + + float maximumDescender = 0; + + for (const auto &line : lines) { + if (line.descender > maximumDescender) { + maximumDescender = line.descender; + } + } + + return size.height - maximumDescender; } TextMeasurement TextLayoutManager::doMeasure( 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 2873b2a6a1bb27..4b335158cfceea 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 @@ -107,10 +107,18 @@ ParagraphAttributes paragraphAttributes, Size size) const { - RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_); - return [textLayoutManager getBaselineForAttributedString:attributedString - paragraphAttributes:paragraphAttributes - size:{size.width, size.height}]; + auto lines = this + ->measureLines(attributedString, paragraphAttributes, size); + + float maximumDescender = 0; + + for (const auto &line : lines) { + if (line.descender > maximumDescender) { + maximumDescender = line.descender; + } + } + + return size.height - maximumDescender; } } // namespace facebook::react From 02b109d52e72386ec20e1093b3b03d780c1ce83f Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 27 Jun 2024 15:36:25 +0200 Subject: [PATCH 18/32] Use first line as baseline --- .../components/text/ParagraphShadowNode.cpp | 27 +++++++++++-------- .../AndroidTextInputShadowNode.cpp | 19 +++++++++++-- .../iostextinput/TextInputShadowNode.cpp | 20 ++++++++++++-- .../textlayoutmanager/TextLayoutManager.cpp | 11 ++------ .../textlayoutmanager/TextLayoutManager.mm | 11 ++------ 5 files changed, 55 insertions(+), 33 deletions(-) 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 0bb8672bc38405..9d810e7d61c51d 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -168,19 +168,24 @@ Float ParagraphShadowNode::baseline( Size size) const { auto content = getContent(layoutContext); auto attributedString = content.attributedString; - - auto lines = textLayoutManager_ - ->measureLines(attributedString, content.paragraphAttributes, size); - - float maximumDescender = 0; - - for (const auto &line : lines) { - if (line.descender > maximumDescender) { - maximumDescender = line.descender; - } + + if (attributedString.isEmpty()) { + // Note: `zero-width space` is insufficient in some cases (e.g. when we need + // to measure the "height" of the font). + // TODO T67606511: We will redefine the measurement of empty strings as part + // of T67606511 + auto string = BaseTextShadowNode::getEmptyPlaceholder(); + auto textAttributes = TextAttributes::defaultTextAttributes(); + textAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier; + textAttributes.apply(getConcreteProps().textAttributes); + attributedString.appendFragment({string, textAttributes, {}}); } - return size.height - maximumDescender; + return textLayoutManager_ + ->getLastBaseline( + attributedString, + getConcreteProps().paragraphAttributes, + size); } void ParagraphShadowNode::layout(LayoutContext layoutContext) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index 6bfd259b70adc1..6da7ee397f165f 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -217,11 +217,26 @@ Size AndroidTextInputShadowNode::measureContent( Float AndroidTextInputShadowNode::baseline( const LayoutContext& layoutContext, Size size) const { + AttributedString attributedString = getMostRecentAttributedString(); + + if (attributedString.isEmpty()) { + attributedString = getPlaceholderAttributedString(); + + if (attributedString.isEmpty()) { + // TODO: Is this correct? + return 0; + } + } + + // I don't think I should be reading directly from yogaNode, but layout metrics + // aren't yet set at this point + auto paddingTop = yogaNode_.getLayout().padding(yoga::PhysicalEdge::Top); + return textLayoutManager_ ->getLastBaseline( - getMostRecentAttributedString(), + attributedString, getConcreteProps().paragraphAttributes, - size); + size) + paddingTop; } void AndroidTextInputShadowNode::layout(LayoutContext layoutContext) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index fc26ba0b17f81c..10583461375dae 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -143,11 +143,27 @@ Size TextInputShadowNode::measureContent( Float TextInputShadowNode::baseline( const LayoutContext& layoutContext, Size size) const { + auto attributedString = getAttributedString(layoutContext); + + if (attributedString.isEmpty()) { + auto placeholder = getConcreteProps().placeholder; + auto string = !placeholder.empty() + ? placeholder + : BaseTextShadowNode::getEmptyPlaceholder(); + auto textAttributes = getConcreteProps().getEffectiveTextAttributes( + layoutContext.fontSizeMultiplier); + attributedString.appendFragment({string, textAttributes, {}}); + } + + // I don't think I should be reading directly from yogaNode, but leyout metrics + // aren't yet set at this point + auto paddingTop = yogaNode_.getLayout().padding(yoga::PhysicalEdge::Top); + return textLayoutManager_ ->getLastBaseline( - getAttributedString(layoutContext), + attributedString, getConcreteProps().getEffectiveParagraphAttributes(), - size); + size) + paddingTop; } void TextInputShadowNode::layout(LayoutContext layoutContext) { 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 b742e1bea812ea..5b91518c539f16 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 @@ -225,15 +225,8 @@ float TextLayoutManager::getLastBaseline( auto lines = this ->measureLines(attributedString, paragraphAttributes, size); - float maximumDescender = 0; - - for (const auto &line : lines) { - if (line.descender > maximumDescender) { - maximumDescender = line.descender; - } - } - - return size.height - maximumDescender; + // TODO: Should this assume there is at least one line? + return lines[0].ascender; } TextMeasurement TextLayoutManager::doMeasure( 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 4b335158cfceea..9375edc25f9a1b 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 @@ -110,15 +110,8 @@ auto lines = this ->measureLines(attributedString, paragraphAttributes, size); - float maximumDescender = 0; - - for (const auto &line : lines) { - if (line.descender > maximumDescender) { - maximumDescender = line.descender; - } - } - - return size.height - maximumDescender; + // TODO: Should this assume there is at least one line? + return lines[0].ascender; } } // namespace facebook::react From fa62579c1e415e7711f1baab95c1831cd36d1bac Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 28 Jun 2024 16:01:00 +0200 Subject: [PATCH 19/32] Use `getContentWithMeasuredAttachments` --- .../react/renderer/components/text/ParagraphShadowNode.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 9d810e7d61c51d..3b557eba01db08 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -166,7 +166,11 @@ Size ParagraphShadowNode::measureContent( Float ParagraphShadowNode::baseline( const LayoutContext& layoutContext, Size size) const { - auto content = getContent(layoutContext); + auto layoutMetrics = getLayoutMetrics(); + auto layoutConstraints = + LayoutConstraints{size, size, layoutMetrics.layoutDirection}; + auto content = + getContentWithMeasuredAttachments(layoutContext, layoutConstraints); auto attributedString = content.attributedString; if (attributedString.isEmpty()) { From 804eb85e5551c907b332e93ef6aef81f6dd98542 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Mon, 1 Jul 2024 11:23:23 +0200 Subject: [PATCH 20/32] Change how line measurements are done on iOS --- .../renderer/textlayoutmanager/RCTTextLayoutManager.mm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 33e24734957fa3..5c9766bcded811 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 @@ -152,15 +152,16 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr attribute:NSFontAttributeName atIndex:0 effectiveRange:nil]; + CGFloat baseline = [layoutManager locationForGlyphAtIndex:range.location].y; auto rect = facebook::react::Rect{ facebook::react::Point{usedRect.origin.x, usedRect.origin.y}, facebook::react::Size{usedRect.size.width, usedRect.size.height}}; auto line = LineMeasurement{ std::string([renderedString UTF8String]), rect, - -font.descender, + usedRect.size.height - baseline, font.capHeight, - font.ascender, + baseline, font.xHeight}; blockParagraphLines->push_back(line); }]; @@ -326,12 +327,12 @@ - (TextMeasurement)_measureTextStorage:(NSTextStorage *)textStorage CGSize attachmentSize = attachment.bounds.size; CGRect glyphRect = [layoutManager boundingRectForGlyphRange:range inTextContainer:textContainer]; - + CGFloat baseline = [layoutManager locationForGlyphAtIndex:range.location].y; UIFont *font = [textStorage attribute:NSFontAttributeName atIndex:range.location effectiveRange:nil]; CGRect frame = { {glyphRect.origin.x, - glyphRect.origin.y + glyphRect.size.height - attachmentSize.height + font.descender}, + baseline - attachmentSize.height}, attachmentSize}; auto rect = facebook::react::Rect{ From c67f15cc10d6587fbf95a88e8edae44ea1774be6 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Mon, 1 Jul 2024 15:15:41 +0200 Subject: [PATCH 21/32] Use lineRect --- .../react/renderer/textlayoutmanager/RCTTextLayoutManager.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 5c9766bcded811..fdd1da64271c5f 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 @@ -152,6 +152,7 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr attribute:NSFontAttributeName atIndex:0 effectiveRange:nil]; + CGRect lineRect = [layoutManager lineFragmentRectForGlyphAtIndex:range.location effectiveRange:nil]; CGFloat baseline = [layoutManager locationForGlyphAtIndex:range.location].y; auto rect = facebook::react::Rect{ facebook::react::Point{usedRect.origin.x, usedRect.origin.y}, @@ -159,7 +160,7 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr auto line = LineMeasurement{ std::string([renderedString UTF8String]), rect, - usedRect.size.height - baseline, + lineRect.size.height - baseline, font.capHeight, baseline, font.xHeight}; From 1e8a5be77b2c9bcd501ea9e846095b56f76decf2 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 2 Jul 2024 16:24:36 +0200 Subject: [PATCH 22/32] Remove todos --- .../androidtextinput/AndroidTextInputShadowNode.cpp | 5 ----- .../react/renderer/textlayoutmanager/TextLayoutManager.cpp | 7 +++++-- .../react/renderer/textlayoutmanager/TextLayoutManager.mm | 7 +++++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index 6da7ee397f165f..b2ce94acdf2b46 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -221,11 +221,6 @@ Float AndroidTextInputShadowNode::baseline( if (attributedString.isEmpty()) { attributedString = getPlaceholderAttributedString(); - - if (attributedString.isEmpty()) { - // TODO: Is this correct? - return 0; - } } // I don't think I should be reading directly from yogaNode, but layout metrics 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 5b91518c539f16..d306938f50eb66 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 @@ -225,8 +225,11 @@ float TextLayoutManager::getLastBaseline( auto lines = this ->measureLines(attributedString, paragraphAttributes, size); - // TODO: Should this assume there is at least one line? - return lines[0].ascender; + if (!lines.empty()) { + return lines[0].ascender; + } else { + return 0; + } } TextMeasurement TextLayoutManager::doMeasure( 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 9375edc25f9a1b..998940ef30b8db 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 @@ -110,8 +110,11 @@ auto lines = this ->measureLines(attributedString, paragraphAttributes, size); - // TODO: Should this assume there is at least one line? - return lines[0].ascender; + if (!lines.empty()) { + return lines[0].ascender; + } else { + return 0; + } } } // namespace facebook::react From bfd6008afffb9e97668d3fa5f5d346a265ce6211 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 2 Jul 2024 16:30:12 +0200 Subject: [PATCH 23/32] Add multiline text example --- .../js/examples/Text/TextExample.android.js | 17 +++++++++++++++++ .../js/examples/Text/TextExample.ios.js | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/rn-tester/js/examples/Text/TextExample.android.js b/packages/rn-tester/js/examples/Text/TextExample.android.js index dade04167d9252..963013ad88fd09 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.android.js +++ b/packages/rn-tester/js/examples/Text/TextExample.android.js @@ -976,6 +976,23 @@ function TextBaseLineLayoutExample(props: {}): React.Node { + + {'Multi-line alignment'} + + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + + {':'} {marker} diff --git a/packages/rn-tester/js/examples/Text/TextExample.ios.js b/packages/rn-tester/js/examples/Text/TextExample.ios.js index 2f01593d4945e5..b52bdd93efc40d 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.ios.js +++ b/packages/rn-tester/js/examples/Text/TextExample.ios.js @@ -344,6 +344,23 @@ class TextBaseLineLayoutExample extends React.Component<{}, mixed> { + + {'Multi-line alignment'} + + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + + {':'} {marker} From e7f695c3142a9c98da89f1202d5ab62a6d27e727 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Wed, 3 Jul 2024 10:36:33 +0200 Subject: [PATCH 24/32] Fix formatting --- .../js/examples/Text/TextExample.android.js | 18 ++++++++++++------ .../js/examples/Text/TextExample.ios.js | 10 +++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/rn-tester/js/examples/Text/TextExample.android.js b/packages/rn-tester/js/examples/Text/TextExample.android.js index 963013ad88fd09..308a0c45af5bb5 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.android.js +++ b/packages/rn-tester/js/examples/Text/TextExample.android.js @@ -18,7 +18,13 @@ import TextInlineViewsExample from './TextInlineViewsExample'; const TextInlineView = require('../../components/TextInlineView'); const React = require('react'); -const {LayoutAnimation, StyleSheet, Text, TextInput, View} = require('react-native'); +const { + LayoutAnimation, + StyleSheet, + Text, + TextInput, + View, +} = require('react-native'); class Entity extends React.Component<{|children: React.Node|}> { render(): React.Node { @@ -976,19 +982,19 @@ function TextBaseLineLayoutExample(props: {}): React.Node { - - {'Multi-line alignment'} - + {'Multi-line alignment'} - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. diff --git a/packages/rn-tester/js/examples/Text/TextExample.ios.js b/packages/rn-tester/js/examples/Text/TextExample.ios.js index b52bdd93efc40d..f454c2c02554e4 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.ios.js +++ b/packages/rn-tester/js/examples/Text/TextExample.ios.js @@ -344,19 +344,19 @@ class TextBaseLineLayoutExample extends React.Component<{}, mixed> { - - {'Multi-line alignment'} - + {'Multi-line alignment'} - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. From b7be70090ade7947288312fadcd689b06485b508 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 4 Jul 2024 10:32:53 +0200 Subject: [PATCH 25/32] Use public yoga apis --- .../androidtextinput/AndroidTextInputShadowNode.cpp | 10 ++++++---- .../components/iostextinput/TextInputShadowNode.cpp | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index b2ce94acdf2b46..b367310eadb3ae 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -223,15 +223,17 @@ Float AndroidTextInputShadowNode::baseline( attributedString = getPlaceholderAttributedString(); } - // I don't think I should be reading directly from yogaNode, but layout metrics - // aren't yet set at this point - auto paddingTop = yogaNode_.getLayout().padding(yoga::PhysicalEdge::Top); + // Yoga expects a baseline relative to the Node's border-box edge instead of + // the content, so we need to adjust by the padding and border widths, which + // have already been set by the time of baseline alignment + auto top = YGNodeLayoutGetBorder(&yogaNode_, YGEdgeTop) + + YGNodeLayoutGetPadding(&yogaNode_, YGEdgeTop); return textLayoutManager_ ->getLastBaseline( attributedString, getConcreteProps().paragraphAttributes, - size) + paddingTop; + size) + top; } void AndroidTextInputShadowNode::layout(LayoutContext layoutContext) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index 10583461375dae..b511897549fab4 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -155,15 +155,17 @@ Float TextInputShadowNode::baseline( attributedString.appendFragment({string, textAttributes, {}}); } - // I don't think I should be reading directly from yogaNode, but leyout metrics - // aren't yet set at this point - auto paddingTop = yogaNode_.getLayout().padding(yoga::PhysicalEdge::Top); + // Yoga expects a baseline relative to the Node's border-box edge instead of + // the content, so we need to adjust by the padding and border widths, which + // have already been set by the time of baseline alignment + auto top = YGNodeLayoutGetBorder(&yogaNode_, YGEdgeTop) + + YGNodeLayoutGetPadding(&yogaNode_, YGEdgeTop); return textLayoutManager_ ->getLastBaseline( attributedString, getConcreteProps().getEffectiveParagraphAttributes(), - size) + paddingTop; + size) + top; } void TextInputShadowNode::layout(LayoutContext layoutContext) { From 418f1e0944ebc0547044e6f9abf58702cfd7e32b Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 4 Jul 2024 10:34:34 +0200 Subject: [PATCH 26/32] Remove unused method --- .../textlayoutmanager/RCTTextLayoutManager.h | 5 ---- .../textlayoutmanager/RCTTextLayoutManager.mm | 23 ------------------- 2 files changed, 28 deletions(-) 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 26b5da86f7565e..7ef23cc308c365 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 @@ -44,11 +44,6 @@ using RCTTextLayoutFragmentEnumerationBlock = (facebook::react::ParagraphAttributes)paragraphAttributes size:(CGSize)size; -- (float)getBaselineForAttributedString:(facebook::react::AttributedString)attributedString - paragraphAttributes: - (facebook::react::ParagraphAttributes)paragraphAttributes - size:(CGSize)size; - - (facebook::react::SharedEventEmitter) getEventEmitterWithAttributeString:(facebook::react::AttributedString)attributedString paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes 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 fdd1da64271c5f..07264d2fa150f0 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 @@ -169,29 +169,6 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr return paragraphLines; } -- (float)getBaselineForAttributedString:(facebook::react::AttributedString)attributedString - paragraphAttributes: - (facebook::react::ParagraphAttributes)paragraphAttributes - size:(CGSize)size -{ - NSTextStorage *attributedText = [self - _textStorageAndLayoutManagerWithAttributesString:[self _nsAttributedStringFromAttributedString:attributedString] - paragraphAttributes:paragraphAttributes - size:size]; - __block CGFloat maximumDescender = 0.0; - - [attributedText enumerateAttribute:NSFontAttributeName - inRange:NSMakeRange(0, attributedText.length) - options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired - usingBlock:^(UIFont *font, NSRange range, __unused BOOL *stop) { - if (maximumDescender > font.descender) { - maximumDescender = font.descender; - } - }]; - - return size.height + maximumDescender; -} - - (NSTextStorage *)_textStorageAndLayoutManagerWithAttributesString:(NSAttributedString *)attributedString paragraphAttributes:(ParagraphAttributes)paragraphAttributes size:(CGSize)size From 3fca45065dd60158d0f138bd283c85b1d4e50131 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 4 Jul 2024 10:45:08 +0200 Subject: [PATCH 27/32] `getLastBaseline` -> `baseline` --- .../react/renderer/components/text/ParagraphShadowNode.cpp | 2 +- .../components/androidtextinput/AndroidTextInputShadowNode.cpp | 2 +- .../renderer/components/iostextinput/TextInputShadowNode.cpp | 2 +- .../react/renderer/textlayoutmanager/TextLayoutManager.cpp | 2 +- .../react/renderer/textlayoutmanager/TextLayoutManager.h | 2 +- .../textlayoutmanager/platform/cxx/TextLayoutManager.cpp | 2 +- .../renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h | 2 +- .../ios/react/renderer/textlayoutmanager/TextLayoutManager.h | 2 +- .../ios/react/renderer/textlayoutmanager/TextLayoutManager.mm | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) 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 3b557eba01db08..9c3482b88f8073 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -186,7 +186,7 @@ Float ParagraphShadowNode::baseline( } return textLayoutManager_ - ->getLastBaseline( + ->baseline( attributedString, getConcreteProps().paragraphAttributes, size); diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp index b367310eadb3ae..9fd23d97f2fa1f 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp @@ -230,7 +230,7 @@ Float AndroidTextInputShadowNode::baseline( YGNodeLayoutGetPadding(&yogaNode_, YGEdgeTop); return textLayoutManager_ - ->getLastBaseline( + ->baseline( attributedString, getConcreteProps().paragraphAttributes, size) + top; diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index b511897549fab4..c94c90227111dd 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -162,7 +162,7 @@ Float TextInputShadowNode::baseline( YGNodeLayoutGetPadding(&yogaNode_, YGEdgeTop); return textLayoutManager_ - ->getLastBaseline( + ->baseline( attributedString, getConcreteProps().getEffectiveParagraphAttributes(), size) + top; 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 d306938f50eb66..56318a5fc91f7f 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 @@ -218,7 +218,7 @@ LinesMeasurements TextLayoutManager::measureLines( return lineMeasurements; } -float TextLayoutManager::getLastBaseline( +float TextLayoutManager::baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const { 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 4fd248411dba24..1e098851b0a06a 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 @@ -71,7 +71,7 @@ class TextLayoutManager { * Calculates baseline of `attributedString` using native text rendering * infrastructure. */ - float getLastBaseline( + float baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const; 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 63a69cb822b261..4ff2bd4bf7cf2d 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 @@ -42,7 +42,7 @@ LinesMeasurements TextLayoutManager::measureLines( return {}; }; -float TextLayoutManager::getLastBaseline( +float TextLayoutManager::baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const { 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 5762e0ef289ad4..33642fd2f93178 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 @@ -63,7 +63,7 @@ class TextLayoutManager { * Calculates baseline of `attributedString` using native text rendering * infrastructure. */ - virtual float getLastBaseline( + virtual float baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const; 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 7736483c764af0..746cd708cfc824 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 @@ -49,7 +49,7 @@ class TextLayoutManager { * Calculates baseline of `attributedString` using native text rendering * infrastructure. */ - float getLastBaseline( + float baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const; 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 998940ef30b8db..a812c20247327c 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 @@ -102,7 +102,7 @@ return measurement; } -float TextLayoutManager::getLastBaseline( +float TextLayoutManager::baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const From c6ac596ee9eaf1c3e5a2270645d37a5e33e21bc9 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 4 Jul 2024 10:51:45 +0200 Subject: [PATCH 28/32] Don't copy string multiple times --- .../components/iostextinput/TextInputShadowNode.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp index c94c90227111dd..9e03093d51ca33 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp @@ -146,13 +146,12 @@ Float TextInputShadowNode::baseline( auto attributedString = getAttributedString(layoutContext); if (attributedString.isEmpty()) { - auto placeholder = getConcreteProps().placeholder; - auto string = !placeholder.empty() - ? placeholder - : BaseTextShadowNode::getEmptyPlaceholder(); + auto placeholderString = !getConcreteProps().placeholder.empty() + ? getConcreteProps().placeholder + : BaseTextShadowNode::getEmptyPlaceholder(); auto textAttributes = getConcreteProps().getEffectiveTextAttributes( layoutContext.fontSizeMultiplier); - attributedString.appendFragment({string, textAttributes, {}}); + attributedString.appendFragment({std::move(placeholderString), textAttributes, {}}); } // Yoga expects a baseline relative to the Node's border-box edge instead of From 08c8d8dfffe2240c8a02f07527f03a21cd9c753b Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 4 Jul 2024 11:01:06 +0200 Subject: [PATCH 29/32] Inline `doMeasureLines` --- .../textlayoutmanager/TextLayoutManager.cpp | 78 ++++++++----------- .../textlayoutmanager/TextLayoutManager.h | 5 -- 2 files changed, 34 insertions(+), 49 deletions(-) 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 56318a5fc91f7f..b77ba03e4d7e3a 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 @@ -161,47 +161,6 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById( return TextMeasurement{size, attachments}; } -LinesMeasurements TextLayoutManager::doMeasureLines( - const AttributedString& attributedString, - const ParagraphAttributes& paragraphAttributes, - Size size) const { - const jni::global_ref& fabricUIManager = - contextContainer_->at>("FabricUIManager"); - static auto measureLines = - jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") - ->getMethod("measureLines"); - - auto attributedStringMB = - JReadableMapBuffer::createWithContents(toMapBuffer(attributedString)); - auto paragraphAttributesMB = - JReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes)); - - auto array = measureLines( - fabricUIManager, - attributedStringMB.get(), - paragraphAttributesMB.get(), - size.width, - size.height); - - auto dynamicArray = cthis(array)->consume(); - LinesMeasurements lineMeasurements; - lineMeasurements.reserve(dynamicArray.size()); - - for (const auto& data : dynamicArray) { - lineMeasurements.push_back(LineMeasurement(data)); - } - - // Explicitly release smart pointers to free up space faster in JNI tables - attributedStringMB.reset(); - paragraphAttributesMB.reset(); - - return lineMeasurements; -} - LinesMeasurements TextLayoutManager::measureLines( const AttributedString& attributedString, const ParagraphAttributes& paragraphAttributes, @@ -209,10 +168,41 @@ LinesMeasurements TextLayoutManager::measureLines( auto lineMeasurements = lineMeasureCache_.get( {attributedString, paragraphAttributes, size}, [&](const LineMeasureCacheKey& /*key*/) { - auto measurement = - doMeasureLines(attributedString, paragraphAttributes, size); + const jni::global_ref& fabricUIManager = + contextContainer_->at>("FabricUIManager"); + static auto measureLines = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("measureLines"); + + auto attributedStringMB = + JReadableMapBuffer::createWithContents(toMapBuffer(attributedString)); + auto paragraphAttributesMB = + JReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes)); + + auto array = measureLines( + fabricUIManager, + attributedStringMB.get(), + paragraphAttributesMB.get(), + size.width, + size.height); + + auto dynamicArray = cthis(array)->consume(); + LinesMeasurements lineMeasurements; + lineMeasurements.reserve(dynamicArray.size()); + + for (const auto& data : dynamicArray) { + lineMeasurements.push_back(LineMeasurement(data)); + } - return measurement; + // Explicitly release smart pointers to free up space faster in JNI tables + attributedStringMB.reset(); + paragraphAttributesMB.reset(); + + return lineMeasurements; }); return lineMeasurements; 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 1e098851b0a06a..2ee3e986e02161 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 @@ -88,11 +88,6 @@ class TextLayoutManager { const ParagraphAttributes& paragraphAttributes, LayoutConstraints layoutConstraints) const; - LinesMeasurements doMeasureLines( - const AttributedString& attributedString, - const ParagraphAttributes& paragraphAttributes, - Size size) const; - void* self_{}; ContextContainer::Shared contextContainer_; TextMeasureCache textMeasureCache_; From 1235a964bbf17510fc5389f74ed1e1aee9cdb36e Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 4 Jul 2024 15:45:41 +0200 Subject: [PATCH 30/32] Remove unused font variable --- .../ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm | 1 - 1 file changed, 1 deletion(-) 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 07264d2fa150f0..e899d86742ad4a 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 @@ -306,7 +306,6 @@ - (TextMeasurement)_measureTextStorage:(NSTextStorage *)textStorage CGSize attachmentSize = attachment.bounds.size; CGRect glyphRect = [layoutManager boundingRectForGlyphRange:range inTextContainer:textContainer]; CGFloat baseline = [layoutManager locationForGlyphAtIndex:range.location].y; - UIFont *font = [textStorage attribute:NSFontAttributeName atIndex:range.location effectiveRange:nil]; CGRect frame = { {glyphRect.origin.x, From 3b978201d7bec9bf364ceeb85635fb521c6acaad Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 4 Jul 2024 16:21:35 +0200 Subject: [PATCH 31/32] Use `Float` instead of `float` --- .../react/renderer/textlayoutmanager/TextLayoutManager.cpp | 2 +- .../react/renderer/textlayoutmanager/TextLayoutManager.h | 2 +- .../textlayoutmanager/platform/cxx/TextLayoutManager.cpp | 2 +- .../renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h | 2 +- .../ios/react/renderer/textlayoutmanager/TextLayoutManager.h | 2 +- .../ios/react/renderer/textlayoutmanager/TextLayoutManager.mm | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) 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 b77ba03e4d7e3a..45a2e8c91bd613 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 @@ -208,7 +208,7 @@ LinesMeasurements TextLayoutManager::measureLines( return lineMeasurements; } -float TextLayoutManager::baseline( +Float TextLayoutManager::baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const { 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 2ee3e986e02161..103962aafab045 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 @@ -71,7 +71,7 @@ class TextLayoutManager { * Calculates baseline of `attributedString` using native text rendering * infrastructure. */ - float baseline( + Float baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const; 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 4ff2bd4bf7cf2d..285c384e9f44d1 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 @@ -42,7 +42,7 @@ LinesMeasurements TextLayoutManager::measureLines( return {}; }; -float TextLayoutManager::baseline( +Float TextLayoutManager::baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const { 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 33642fd2f93178..180605602ccb99 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 @@ -63,7 +63,7 @@ class TextLayoutManager { * Calculates baseline of `attributedString` using native text rendering * infrastructure. */ - virtual float baseline( + virtual Float baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const; 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 746cd708cfc824..5c380ab41c7a0a 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 @@ -49,7 +49,7 @@ class TextLayoutManager { * Calculates baseline of `attributedString` using native text rendering * infrastructure. */ - float baseline( + Float baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const; 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 a812c20247327c..b64aca412b6f0c 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 @@ -102,7 +102,7 @@ return measurement; } -float TextLayoutManager::baseline( +Float TextLayoutManager::baseline( AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size) const From 828deaa1973672a5f705d724df859b9dbb19bcdf Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Mon, 8 Jul 2024 10:28:15 +0200 Subject: [PATCH 32/32] Use line fragment rect --- .../react/renderer/textlayoutmanager/RCTTextLayoutManager.mm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 e899d86742ad4a..b361f9adb68261 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 @@ -152,7 +152,6 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr attribute:NSFontAttributeName atIndex:0 effectiveRange:nil]; - CGRect lineRect = [layoutManager lineFragmentRectForGlyphAtIndex:range.location effectiveRange:nil]; CGFloat baseline = [layoutManager locationForGlyphAtIndex:range.location].y; auto rect = facebook::react::Rect{ facebook::react::Point{usedRect.origin.x, usedRect.origin.y}, @@ -160,7 +159,7 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr auto line = LineMeasurement{ std::string([renderedString UTF8String]), rect, - lineRect.size.height - baseline, + overallRect.size.height - baseline, font.capHeight, baseline, font.xHeight};