TextInput: support text value+child Text nodes

Summary:
Fix and simplify `AndroidTextInputShadowNode::getAttributedString` so that it (1) works, and (2) is aligned with the equivalents in the old Java code.

The issue is that we weren't picking up `text` attributes since we're only traversing children and not the TextInput node itself. If a `text` attribute is present it needs to be treated as its own Text node.

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D18739830

fbshipit-source-id: 4b3bc81dbe8c241c2e06fe5be1f9b50e49132890
This commit is contained in:
Joshua Gross
2019-12-05 13:20:29 -08:00
committed by Facebook Github Bot
parent 7590d9f8f4
commit a09346f129
2 changed files with 40 additions and 33 deletions
@@ -16,8 +16,6 @@
#include <react/core/conversions.h>
#include <react/jni/ReadableNativeMap.h>
#include <Glog/logging.h>
using namespace facebook::jni;
namespace facebook {
@@ -33,44 +31,52 @@ void AndroidTextInputShadowNode::setContextContainer(
AttributedString AndroidTextInputShadowNode::getAttributedString(
bool usePlaceholders) const {
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.apply(getProps()->textAttributes);
// Use BaseTextShadowNode to get attributed string from children
auto const &attributedString =
BaseTextShadowNode::getAttributedString(textAttributes, *this);
auto childTextAttributes = TextAttributes::defaultTextAttributes();
childTextAttributes.apply(getProps()->textAttributes);
auto attributedString =
BaseTextShadowNode::getAttributedString(childTextAttributes, *this);
// BaseTextShadowNode only gets children. We must detect and prepend text
// value attributes manually.
if (!getProps()->text.empty()) {
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.apply(getProps()->textAttributes);
auto fragment = AttributedString::Fragment{};
fragment.string = getProps()->text;
fragment.textAttributes = textAttributes;
fragment.parentShadowView = ShadowView(*this);
attributedString.prependFragment(fragment);
// We know this is not empty, because we at least have the `text` value
return attributedString;
}
// No need to use placeholder if we have text at this point.
if (!attributedString.isEmpty()) {
return attributedString;
}
if (!getProps()->text.empty() || usePlaceholders) {
// If the BaseTextShadowNode didn't detect any child Text nodes, we
// may actually just have a `text` attribute.
// TODO: figure out why BaseTextShadowNode doesn't pick this up, this
// is a bug. A minimal Playground example that triggers this: P122991121
auto textAttributedString = AttributedString{};
auto fragment = AttributedString::Fragment{};
fragment.string = getProps()->text;
if (usePlaceholders) {
// Return placeholder text instead, if text was empty.
if (fragment.string.empty()) {
fragment.string = getProps()->placeholder;
}
// For measurement purposes, we want to make sure that there's at least a
// 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.
if (fragment.string.empty()) {
fragment.string = " ";
}
}
fragment.textAttributes = textAttributes;
fragment.parentShadowView = ShadowView(*this);
textAttributedString.appendFragment(fragment);
return textAttributedString;
// Return placeholder text, since text and children are empty.
auto textAttributedString = AttributedString{};
auto fragment = AttributedString::Fragment{};
fragment.string = getProps()->placeholder;
// For measurement purposes, we want to make sure that there's at least a
// 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.
if (fragment.string.empty() && usePlaceholders) {
fragment.string = " ";
}
return attributedString;
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.apply(getProps()->textAttributes);
fragment.textAttributes = textAttributes;
fragment.parentShadowView = ShadowView(*this);
textAttributedString.appendFragment(fragment);
return textAttributedString;
}
void AndroidTextInputShadowNode::setTextLayoutManager(