Minimize EditText Spans 5/9: Strikethrough and Underline (#36544)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/36544

This is part of a series of changes to minimize the number of spans committed to EditText, as a mitigation for platform issues on Samsung devices. See this [GitHub thread]( https://github.com/facebook/react-native/issues/35936#issuecomment-1411437789) for greater context on the platform behavior.

This change makes us apply strikethrough and underline as paint flags to the underlying EditText, instead of just the spans. We then opt ReactUnderlineSpan and ReactStrikethroughSpan into being strippable.

This does actually create visual behavior changes, where child text will inherit any underline or strikethrough of the root EditText (including if the child specifies `textDecorationLine: "none"`. The new behavior is consistent with both iOS and web though, so it seems like more of a bugfix than a regression.

Changelog:
[Android][Fixed] - Minimize Spans 5/N: Strikethrough and Underline

Reviewed By: rshest

Differential Revision: D44240778

fbshipit-source-id: d564dfc0121057a5e3b09bb71b8f5662e28be17e
This commit is contained in:
Nick Gerleman
2023-03-24 12:31:22 -07:00
committed by Lorenzo Sciandra
parent 205623cbe8
commit f1c33b9e15
2 changed files with 46 additions and 0 deletions
@@ -12,6 +12,7 @@ import static com.facebook.react.views.text.TextAttributeProps.UNSET;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
@@ -54,8 +55,10 @@ import com.facebook.react.views.text.ReactAbsoluteSizeSpan;
import com.facebook.react.views.text.ReactBackgroundColorSpan;
import com.facebook.react.views.text.ReactForegroundColorSpan;
import com.facebook.react.views.text.ReactSpan;
import com.facebook.react.views.text.ReactStrikethroughSpan;
import com.facebook.react.views.text.ReactTextUpdate;
import com.facebook.react.views.text.ReactTypefaceUtils;
import com.facebook.react.views.text.ReactUnderlineSpan;
import com.facebook.react.views.text.TextAttributes;
import com.facebook.react.views.text.TextInlineImageSpan;
import com.facebook.react.views.text.TextLayoutManager;
@@ -673,6 +676,26 @@ public class ReactEditText extends AppCompatEditText
return span.getForegroundColor() == getCurrentTextColor();
}
});
stripSpansOfKind(
sb,
ReactStrikethroughSpan.class,
new SpanPredicate<ReactStrikethroughSpan>() {
@Override
public boolean test(ReactStrikethroughSpan span) {
return (getPaintFlags() & Paint.STRIKE_THRU_TEXT_FLAG) != 0;
}
});
stripSpansOfKind(
sb,
ReactUnderlineSpan.class,
new SpanPredicate<ReactUnderlineSpan>() {
@Override
public boolean test(ReactUnderlineSpan span) {
return (getPaintFlags() & Paint.UNDERLINE_TEXT_FLAG) != 0;
}
});
}
private <T> void stripSpansOfKind(
@@ -706,6 +729,14 @@ public class ReactEditText extends AppCompatEditText
spans.add(new ReactBackgroundColorSpan(backgroundColor));
}
if ((getPaintFlags() & Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
spans.add(new ReactStrikethroughSpan());
}
if ((getPaintFlags() & Paint.UNDERLINE_TEXT_FLAG) != 0) {
spans.add(new ReactUnderlineSpan());
}
for (Object span : spans) {
workingText.setSpan(span, 0, workingText.length(), spanFlags);
}
@@ -13,6 +13,7 @@ import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -912,6 +913,20 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
view.setAutoFocus(autoFocus);
}
@ReactProp(name = ViewProps.TEXT_DECORATION_LINE)
public void setTextDecorationLine(ReactEditText view, @Nullable String textDecorationLineString) {
view.setPaintFlags(
view.getPaintFlags() & ~(Paint.STRIKE_THRU_TEXT_FLAG | Paint.UNDERLINE_TEXT_FLAG));
for (String token : textDecorationLineString.split(" ")) {
if (token.equals("underline")) {
view.setPaintFlags(view.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
} else if (token.equals("line-through")) {
view.setPaintFlags(view.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
}
}
@ReactPropGroup(
names = {
ViewProps.BORDER_WIDTH,