Skip to content

Commit

Permalink
Fix measurement of uncontrolled TextInput after edit
Browse files Browse the repository at this point in the history
Summary:
D42721684 (be69c8b) left a pretty bad bug when using Fabric for Android. I missed that in Fabric specifically, on edit we will cache the Spannable backing the EditText for use in future measurement.

Because we've stripped the sizing spans, Spannable measurement has incorrect font size, and the TextInput size will change (collapsing) after the first edit. This effectively breaks any uncontrolled TextInput which does not have explicit dimensions set.

Changelog:
[Android][Fixed] - Fix measurement of uncontrolled TextInput after edit

Reviewed By: sammy-SC

Differential Revision: D43158407

fbshipit-source-id: 51602eab06c9a50e2b60ef0ed87bdb4df025e51e
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Feb 9, 2023
1 parent 97d90c6 commit 8a0fe30
Showing 1 changed file with 29 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) {
manageSpans(spannableStringBuilder, reactTextUpdate.mContainsMultipleFragments);

// Mitigation for https://github.com/facebook/react-native/issues/35936 (S318090)
stripAbsoluteSizeSpans(spannableStringBuilder);
stripAtributeEquivalentSpans(spannableStringBuilder);

mContainsImages = reactTextUpdate.containsImages();

Expand Down Expand Up @@ -662,7 +662,7 @@ private void manageSpans(
}
}

private void stripAbsoluteSizeSpans(SpannableStringBuilder sb) {
private void stripAtributeEquivalentSpans(SpannableStringBuilder sb) {
// We have already set a font size on the EditText itself. We can safely remove sizing spans
// which are the same as the set font size, and not otherwise overlapped.
final int effectiveFontSize = mTextAttributes.getEffectiveFontSize();
Expand All @@ -683,6 +683,31 @@ private void stripAbsoluteSizeSpans(SpannableStringBuilder sb) {
}
}

private void unstripAttributeEquivalentSpans(
SpannableStringBuilder workingText, Spannable originalText) {
// We must add spans back for Fabric to be able to measure, at lower precedence than any
// existing spans. Remove all spans, add the attributes, then re-add the spans over
workingText.append(originalText);

for (Object span : workingText.getSpans(0, workingText.length(), Object.class)) {
workingText.removeSpan(span);
}

workingText.setSpan(
new ReactAbsoluteSizeSpan(mTextAttributes.getEffectiveFontSize()),
0,
workingText.length(),
Spanned.SPAN_INCLUSIVE_INCLUSIVE);

for (Object span : originalText.getSpans(0, originalText.length(), Object.class)) {
workingText.setSpan(
span,
originalText.getSpanStart(span),
originalText.getSpanEnd(span),
originalText.getSpanFlags(span));
}
}

private static boolean sameTextForSpan(
final Editable oldText,
final SpannableStringBuilder newText,
Expand Down Expand Up @@ -1102,7 +1127,8 @@ private void updateCachedSpannable(boolean resetStyles) {
// ...
// - android.app.Activity.dispatchKeyEvent (Activity.java:3447)
try {
sb.append(currentText.subSequence(0, currentText.length()));
Spannable text = (Spannable) currentText.subSequence(0, currentText.length());
unstripAttributeEquivalentSpans(sb, text);
} catch (IndexOutOfBoundsException e) {
ReactSoftExceptionLogger.logSoftException(TAG, e);
}
Expand Down

0 comments on commit 8a0fe30

Please sign in to comment.