diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java index c09a4a6220d..8bde57f62d7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java @@ -20,6 +20,7 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.util.LayoutDirection; import android.util.LruCache; +import android.view.View; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReadableArray; @@ -86,7 +87,7 @@ public class TextLayoutManager { sb.append(TextTransform.apply(fragment.getString("string"), textAttributes.mTextTransform)); int end = sb.length(); - int reactTag = fragment.getInt("reactTag"); + int reactTag = fragment.hasKey("reactTag") ? fragment.getInt("reactTag") : View.NO_ID; if (fragment.hasKey(ViewProps.IS_ATTACHMENT) && fragment.getBoolean(ViewProps.IS_ATTACHMENT)) { float width = PixelUtil.toPixelFromSP(fragment.getDouble(ViewProps.WIDTH)); diff --git a/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h b/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h index 0e04b3c2b99..be95f4cacf1 100644 --- a/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h +++ b/ReactCommon/fabric/components/text/basetext/BaseTextShadowNode.h @@ -57,6 +57,13 @@ class BaseTextShadowNode { ShadowNode const &parentNode, AttributedString &outAttributedString, Attachments &outAttachments); + + /** + * Returns a character used to measure empty strings in native platforms. + */ + inline static std::string getEmptyPlaceholder() { + return "I"; + } }; } // namespace react diff --git a/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp b/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp index b93503a42ed..3ead9c9a4bf 100644 --- a/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp +++ b/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.cpp @@ -120,13 +120,21 @@ Size ParagraphShadowNode::measure(LayoutConstraints layoutConstraints) const { auto content = getContentWithMeasuredAttachments(LayoutContext{}, layoutConstraints); - if (content.attributedString.isEmpty()) { - return layoutConstraints.clamp({0, 0}); + auto attributedString = content.attributedString; + 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.apply(getConcreteProps().textAttributes); + attributedString.appendFragment({string, textAttributes, {}}); } return textLayoutManager_ ->measure( - AttributedStringBox{content.attributedString}, + AttributedStringBox{attributedString}, content.paragraphAttributes, layoutConstraints) .size; diff --git a/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp b/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp index 0d90987f511..1349f3f4e88 100644 --- a/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp +++ b/ReactCommon/fabric/components/textinput/androidtextinput/AndroidTextInputShadowNode.cpp @@ -62,6 +62,8 @@ AttributedString AndroidTextInputShadowNode::getAttributedString() const { // single character in the string so that the measured height is greater // than zero. Otherwise, empty TextInputs with no placeholder don't // display at all. +// TODO T67606511: We will redefine the measurement of empty strings as part +// of T67606511 AttributedString AndroidTextInputShadowNode::getPlaceholderAttributedString() const { // Return placeholder text, since text and children are empty. @@ -70,7 +72,7 @@ AttributedString AndroidTextInputShadowNode::getPlaceholderAttributedString() fragment.string = getConcreteProps().placeholder; if (fragment.string.empty()) { - fragment.string = " "; + fragment.string = BaseTextShadowNode::getEmptyPlaceholder(); } auto textAttributes = TextAttributes::defaultTextAttributes(); diff --git a/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp b/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp index 5b65e24a325..fd70e306d73 100644 --- a/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp +++ b/ReactCommon/fabric/components/textinput/iostextinput/TextInputShadowNode.cpp @@ -38,7 +38,11 @@ AttributedStringBox TextInputShadowNode::attributedStringBoxToMeasure() const { auto placeholder = getConcreteProps().placeholder; // Note: `zero-width space` is insufficient in some cases (e.g. when we need // to measure the "hight" of the font). - auto string = !placeholder.empty() ? placeholder : "I"; + // TODO T67606511: We will redefine the measurement of empty strings as part + // of T67606511 + auto string = !placeholder.empty() + ? placeholder + : BaseTextShadowNode::getEmptyPlaceholder(); auto textAttributes = getConcreteProps().getEffectiveTextAttributes(); attributedString.appendFragment({string, textAttributes, {}}); }