Skip to content

Commit

Permalink
Android TextInput: Improve application of styles for value prop
Browse files Browse the repository at this point in the history
Prior to this change, when you passed text to `TextInput` via the `value` or `defaultValue` props, React Native didn't apply any of the styles in `buildSpannedFromShadowNode` to the text. This is because `spannedFromShadowNode` appends `value` after calling `buildSpannedFromShadowNode`. Many styles worked because their logic is included in both `buildSpannedFromShadowNode` and `ReactTextInputManager`. However, some only appear in `buildSpannedFromShadowNode` such as `textDecorationLine` (it would be good to understand why we need to duplicate styling logic in `buildSpannedFromShadowNode` & `ReactTextInputManager` and to know whether `ReactTextInputManager` should be handling `textDecorationLine`).

Also, this commit improves consistency between iOS and Android if you specify both `value` and children on a `TextInput`. Prior to this, iOS concatenated the strings such that the `value` prop came before the children whereas Android put the children before the `value` prop. Now Android matches iOS's behavior and puts the `value` prop before the children.

These appear to be regressions. The `value` prop used to be appended before calling `buildSpannedFromShadowNode` (this behavior appears to have been changed by accident in 80027ce#diff-4f5947f2fe0381c4a6373a30e596b8c3).

The fix is done by pulling the logic that applies styling out of `buildSpannedFromShadowNode` and into a method `applyStyles`. The `value` prop is styled via `applyStyles` and then the children are styled and appended to it.

Test Plan:
----------

Used a variety of props to style some `TextInputs` (e.g. `textDecorationLine`, `fontSize`, `letterSpacing`). Verified that the `TextInputs` looked the same regardless of whether the text was provided via the `value` prop, children, or both.

Changelog:
----------

[Android] [Fixed] - TextInput: Improve application of styles for `value` prop

Adam Comella
Microsoft Corp.
  • Loading branch information
Adam Comella committed Nov 30, 2018
1 parent 18f3de9 commit 141bba9
Showing 1 changed file with 81 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,76 +113,85 @@ private static void buildSpannedFromShadowNode(
}
int end = sb.length();
if (end >= start) {
if (textShadowNode.mIsColorSet) {
ops.add(new SetSpanOperation(start, end, new ForegroundColorSpan(textShadowNode.mColor)));
}
if (textShadowNode.mIsBackgroundColorSet) {
ops.add(
new SetSpanOperation(
start, end, new BackgroundColorSpan(textShadowNode.mBackgroundColor)));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (!Float.isNaN(textShadowNode.mLetterSpacing)) {
ops.add(new SetSpanOperation(
start,
end,
new CustomLetterSpacingSpan(textShadowNode.mLetterSpacing)));
}
}
if (textShadowNode.mFontSize != UNSET) {
ops.add(new SetSpanOperation(start, end, new AbsoluteSizeSpan(textShadowNode.mFontSize)));
}
if (textShadowNode.mFontStyle != UNSET
|| textShadowNode.mFontWeight != UNSET
|| textShadowNode.mFontFamily != null) {
ops.add(
new SetSpanOperation(
start,
end,
new CustomStyleSpan(
textShadowNode.mFontStyle,
textShadowNode.mFontWeight,
textShadowNode.mFontFamily,
textShadowNode.getThemedContext().getAssets())));
}
if (textShadowNode.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new UnderlineSpan()));
}
if (textShadowNode.mIsLineThroughTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new StrikethroughSpan()));
}
if (
(
textShadowNode.mTextShadowOffsetDx != 0 ||
textShadowNode.mTextShadowOffsetDy != 0 ||
textShadowNode.mTextShadowRadius != 0
) &&
Color.alpha(textShadowNode.mTextShadowColor) != 0
) {
ops.add(
new SetSpanOperation(
start,
end,
new ShadowStyleSpan(
textShadowNode.mTextShadowOffsetDx,
textShadowNode.mTextShadowOffsetDy,
textShadowNode.mTextShadowRadius,
textShadowNode.mTextShadowColor)));
}
if (!Float.isNaN(textShadowNode.getEffectiveLineHeight())) {
ops.add(
new SetSpanOperation(
start, end, new CustomLineHeightSpan(textShadowNode.getEffectiveLineHeight())));
}
if (textShadowNode.mTextTransform != TextTransform.UNSET) {
ops.add(
applyStyles(textShadowNode, sb, ops, start, end);
}
}

private static void applyStyles(
ReactBaseTextShadowNode textShadowNode,
SpannableStringBuilder sb,
List<SetSpanOperation> ops,
int start,
int end) {
if (textShadowNode.mIsColorSet) {
ops.add(new SetSpanOperation(start, end, new ForegroundColorSpan(textShadowNode.mColor)));
}
if (textShadowNode.mIsBackgroundColorSet) {
ops.add(
new SetSpanOperation(
start,
end,
new CustomTextTransformSpan(textShadowNode.mTextTransform)));
start, end, new BackgroundColorSpan(textShadowNode.mBackgroundColor)));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (!Float.isNaN(textShadowNode.mLetterSpacing)) {
ops.add(new SetSpanOperation(
start,
end,
new CustomLetterSpacingSpan(textShadowNode.mLetterSpacing)));
}
ops.add(new SetSpanOperation(start, end, new ReactTagSpan(textShadowNode.getReactTag())));
}
if (textShadowNode.mFontSize != UNSET) {
ops.add(new SetSpanOperation(start, end, new AbsoluteSizeSpan(textShadowNode.mFontSize)));
}
if (textShadowNode.mFontStyle != UNSET
|| textShadowNode.mFontWeight != UNSET
|| textShadowNode.mFontFamily != null) {
ops.add(
new SetSpanOperation(
start,
end,
new CustomStyleSpan(
textShadowNode.mFontStyle,
textShadowNode.mFontWeight,
textShadowNode.mFontFamily,
textShadowNode.getThemedContext().getAssets())));
}
if (textShadowNode.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new UnderlineSpan()));
}
if (textShadowNode.mIsLineThroughTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new StrikethroughSpan()));
}
if (
(
textShadowNode.mTextShadowOffsetDx != 0 ||
textShadowNode.mTextShadowOffsetDy != 0 ||
textShadowNode.mTextShadowRadius != 0
) &&
Color.alpha(textShadowNode.mTextShadowColor) != 0
) {
ops.add(
new SetSpanOperation(
start,
end,
new ShadowStyleSpan(
textShadowNode.mTextShadowOffsetDx,
textShadowNode.mTextShadowOffsetDy,
textShadowNode.mTextShadowRadius,
textShadowNode.mTextShadowColor)));
}
if (!Float.isNaN(textShadowNode.getEffectiveLineHeight())) {
ops.add(
new SetSpanOperation(
start, end, new CustomLineHeightSpan(textShadowNode.getEffectiveLineHeight())));
}
if (textShadowNode.mTextTransform != TextTransform.UNSET) {
ops.add(
new SetSpanOperation(
start,
end,
new CustomTextTransformSpan(textShadowNode.mTextTransform)));
}
ops.add(new SetSpanOperation(start, end, new ReactTagSpan(textShadowNode.getReactTag())));
}

protected int getDefaultFontSize() {
Expand All @@ -201,12 +210,15 @@ protected static Spannable spannedFromShadowNode(
// a new spannable will be wiped out
List<SetSpanOperation> ops = new ArrayList<>();

buildSpannedFromShadowNode(textShadowNode, sb, ops);

if (text != null) {
// Handle text that is provided via a prop (e.g. the `value` and `defaultValue` props on
// TextInput).
sb.append(text);
applyStyles(textShadowNode, sb, ops, 0, sb.length());
}

buildSpannedFromShadowNode(textShadowNode, sb, ops);

if (textShadowNode.mFontSize == UNSET) {
int defaultFontSize = textShadowNode.getDefaultFontSize();

Expand Down

0 comments on commit 141bba9

Please sign in to comment.