From f18358dda6ec1bc4417cbc5520208cbe85ed697d Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 9 Mar 2020 16:32:33 -0700 Subject: [PATCH] Fabric: Introducing `BaseTextShadowNode::getAttributedString()` Summary: `BaseTextShadowNode::getAttributedString()` was replaced with simular method `BaseTextShadowNode::buildAttributedString()` which does same work with following differences: * Besides returning `AttributedString`, it retures an array of `Attachment`s elements of which points to `ShadowNode`s that form attachments; * Now we use single `AttributedString` to construct result instead of concatenation objects recurvily walking the tree. We will use the array of attachments later. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D20268048 fbshipit-source-id: 371c548826bdfd5c4f4b18915d68977724885ce6 --- .../text/basetext/BaseTextShadowNode.cpp | 39 ++++++++++--------- .../text/basetext/BaseTextShadowNode.h | 34 ++++++++++++++-- .../text/paragraph/ParagraphShadowNode.cpp | 8 +++- .../AndroidTextInputShadowNode.cpp | 7 +++- .../iostextinput/TextInputShadowNode.cpp | 6 ++- 5 files changed, 66 insertions(+), 28 deletions(-) diff --git a/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.cpp b/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.cpp index 6681acaf08e..e892881c43d 100644 --- a/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.cpp +++ b/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include namespace facebook { @@ -26,51 +25,53 @@ inline ShadowView shadowViewFromShadowNode(ShadowNode const &shadowNode) { return shadowView; } -AttributedString BaseTextShadowNode::getAttributedString( - TextAttributes const &textAttributes, - ShadowNode const &parentNode) { - auto attributedString = AttributedString{}; - +void BaseTextShadowNode::buildAttributedString( + TextAttributes const &baseTextAttributes, + ShadowNode const &parentNode, + AttributedString &outAttributedString, + Attachments &outAttachments) { for (auto const &childNode : parentNode.getChildren()) { // RawShadowNode auto rawTextShadowNode = - std::dynamic_pointer_cast(childNode); + std::dynamic_pointer_cast(childNode); if (rawTextShadowNode) { auto fragment = AttributedString::Fragment{}; fragment.string = rawTextShadowNode->getConcreteProps().text; - fragment.textAttributes = textAttributes; + fragment.textAttributes = baseTextAttributes; // Storing a retaining pointer to `ParagraphShadowNode` inside // `attributedString` causes a retain cycle (besides that fact that we // don't need it at all). Storing a `ShadowView` instance instead of // `ShadowNode` should properly fix this problem. fragment.parentShadowView = shadowViewFromShadowNode(parentNode); - attributedString.appendFragment(fragment); + outAttributedString.appendFragment(fragment); continue; } // TextShadowNode auto textShadowNode = - std::dynamic_pointer_cast(childNode); + std::dynamic_pointer_cast(childNode); if (textShadowNode) { - auto localTextAttributes = textAttributes; + auto localTextAttributes = baseTextAttributes; localTextAttributes.apply( textShadowNode->getConcreteProps().textAttributes); - attributedString.appendAttributedString( - textShadowNode->getAttributedString( - localTextAttributes, *textShadowNode)); + buildAttributedString( + localTextAttributes, + *textShadowNode, + outAttributedString, + outAttachments); continue; } - // Any other kind of ShadowNode + // Any *other* kind of ShadowNode auto fragment = AttributedString::Fragment{}; fragment.string = AttributedString::Fragment::AttachmentCharacter(); fragment.parentShadowView = shadowViewFromShadowNode(*childNode); - fragment.textAttributes = textAttributes; - attributedString.appendFragment(fragment); + fragment.textAttributes = baseTextAttributes; + outAttributedString.appendFragment(fragment); + outAttachments.push_back(Attachment{ + childNode.get(), outAttributedString.getFragments().size() - 1}); } - - return attributedString; } } // namespace react diff --git a/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h b/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h index c29ddb66501..0e04b3c2b99 100644 --- a/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h +++ b/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h @@ -20,15 +20,43 @@ namespace react { class BaseTextShadowNode { public: /* - * Returns a `AttributedString` which represents text content of the node. + * Represents additional information associated with some fragments which + * represent embedded into text component (such as an image or inline view). + */ + class Attachment final { + public: + /* + * Unowning pointer to a `ShadowNode` that represents the attachment. + * Cannot be `null`. + */ + ShadowNode const *shadowNode; + + /* + * Index of the fragment in `AttributedString` that represents the + * the attachment. + */ + size_t fragmentIndex; + }; + + /* + * A list of `Attachment`s. + * Performance-wise, the prevailing case is when there are no attachments + * at all, therefore we don't need an inline buffer (`small_vector`). + */ + using Attachments = std::vector; + + /* + * Builds an `AttributedString` which represents text content of the node. * This is static so that both Paragraph (which subclasses BaseText) and * TextInput (which does not) can use this. * TODO T53299884: decide if this should be moved out and made a static * function, or if TextInput should inherit from BaseTextShadowNode. */ - static AttributedString getAttributedString( + static void buildAttributedString( TextAttributes const &baseTextAttributes, - ShadowNode const &parentNode); + ShadowNode const &parentNode, + AttributedString &outAttributedString, + Attachments &outAttachments); }; } // namespace react diff --git a/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp b/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp index ad7570c97b6..26f7e28e18b 100644 --- a/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp +++ b/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp @@ -20,8 +20,12 @@ AttributedString ParagraphShadowNode::getAttributedString() const { auto textAttributes = TextAttributes::defaultTextAttributes(); textAttributes.apply(getConcreteProps().textAttributes); - cachedAttributedString_ = - BaseTextShadowNode::getAttributedString(textAttributes, *this); + auto attributedString = AttributedString{}; + auto attachments = Attachments{}; + BaseTextShadowNode::buildAttributedString( + textAttributes, *this, attributedString, attachments); + + cachedAttributedString_ = attributedString; } return cachedAttributedString_.value(); diff --git a/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp b/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp index 376c1615ae1..8f2c00cf3a0 100644 --- a/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp @@ -33,8 +33,11 @@ AttributedString AndroidTextInputShadowNode::getAttributedString() const { // Use BaseTextShadowNode to get attributed string from children auto childTextAttributes = TextAttributes::defaultTextAttributes(); childTextAttributes.apply(getConcreteProps().textAttributes); - auto attributedString = - BaseTextShadowNode::getAttributedString(childTextAttributes, *this); + + auto attributedString = AttributedString{}; + auto attachments = BaseTextShadowNode::Attachments{}; + BaseTextShadowNode::buildAttributedString( + childTextAttributes, *this, attributedString, attachments); // BaseTextShadowNode only gets children. We must detect and prepend text // value attributes manually. diff --git a/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp b/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp index 5678cfc9b24..f426f1ed9b1 100644 --- a/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp +++ b/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp @@ -53,8 +53,10 @@ AttributedString TextInputShadowNode::getAttributedString() const { attributedString.appendFragment( AttributedString::Fragment{getConcreteProps().text, textAttributes}); - attributedString.appendAttributedString( - BaseTextShadowNode::getAttributedString(textAttributes, *this)); + auto attachments = Attachments{}; + BaseTextShadowNode::buildAttributedString( + textAttributes, *this, attributedString, attachments); + return attributedString; }