6464import com .facebook .react .views .text .TextLayoutManager ;
6565import com .facebook .react .views .view .ReactViewBackgroundManager ;
6666import java .util .ArrayList ;
67- import java .util .List ;
6867import java .util .Objects ;
6968
7069/**
@@ -89,7 +88,6 @@ public class ReactEditText extends AppCompatEditText
8988 // *TextChanged events should be triggered. This is less expensive than removing the text
9089 // listeners and adding them back again after the text change is completed.
9190 protected boolean mIsSettingTextFromJS ;
92- protected boolean mIsSettingTextFromCacheUpdate = false ;
9391 private int mDefaultGravityHorizontal ;
9492 private int mDefaultGravityVertical ;
9593
@@ -369,7 +367,7 @@ protected void onSelectionChanged(int selStart, int selEnd) {
369367 }
370368
371369 super .onSelectionChanged (selStart , selEnd );
372- if (! mIsSettingTextFromCacheUpdate && mSelectionWatcher != null && hasFocus ()) {
370+ if (mSelectionWatcher != null && hasFocus ()) {
373371 mSelectionWatcher .onSelectionChanged (selStart , selEnd );
374372 }
375373 }
@@ -610,7 +608,7 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) {
610608 SpannableStringBuilder spannableStringBuilder =
611609 new SpannableStringBuilder (reactTextUpdate .getText ());
612610
613- manageSpans (spannableStringBuilder , reactTextUpdate . mContainsMultipleFragments );
611+ manageSpans (spannableStringBuilder );
614612 stripStyleEquivalentSpans (spannableStringBuilder );
615613
616614 mContainsImages = reactTextUpdate .containsImages ();
@@ -639,7 +637,7 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) {
639637 }
640638
641639 // Update cached spans (in Fabric only).
642- updateCachedSpannable (false );
640+ updateCachedSpannable ();
643641 }
644642
645643 /**
@@ -648,8 +646,7 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) {
648646 * will adapt to the new text, hence why {@link SpannableStringBuilder#replace} never removes
649647 * them.
650648 */
651- private void manageSpans (
652- SpannableStringBuilder spannableStringBuilder , boolean skipAddSpansForMeasurements ) {
649+ private void manageSpans (SpannableStringBuilder spannableStringBuilder ) {
653650 Object [] spans = getText ().getSpans (0 , length (), Object .class );
654651 for (int spanIdx = 0 ; spanIdx < spans .length ; spanIdx ++) {
655652 Object span = spans [spanIdx ];
@@ -677,13 +674,6 @@ private void manageSpans(
677674 spannableStringBuilder .setSpan (span , spanStart , spanEnd , spanFlags );
678675 }
679676 }
680-
681- // In Fabric only, apply necessary styles to entire span
682- // If the Spannable was constructed from multiple fragments, we don't apply any spans that could
683- // impact the whole Spannable, because that would override "local" styles per-fragment
684- if (!skipAddSpansForMeasurements ) {
685- addSpansForMeasurement (getText ());
686- }
687677 }
688678
689679 // TODO: Replace with Predicate<T> and lambdas once Java 8 builds in OSS
@@ -785,10 +775,10 @@ private <T> void stripSpansOfKind(
785775 }
786776
787777 /**
788- * Copy back styles represented as attributes to the underlying span, for later measurement
789- * outside the ReactEditText.
778+ * Copy styles represented as attributes to the underlying span, for later measurement or other
779+ * usage outside the ReactEditText.
790780 */
791- private void restoreStyleEquivalentSpans (SpannableStringBuilder workingText ) {
781+ private void addSpansFromStyleAttributes (SpannableStringBuilder workingText ) {
792782 int spanFlags = Spannable .SPAN_INCLUSIVE_INCLUSIVE ;
793783
794784 // Set all bits for SPAN_PRIORITY so that this span has the highest possible priority
@@ -844,6 +834,11 @@ private void restoreStyleEquivalentSpans(SpannableStringBuilder workingText) {
844834 workingText .length (),
845835 spanFlags );
846836 }
837+
838+ float lineHeight = mTextAttributes .getEffectiveLineHeight ();
839+ if (!Float .isNaN (lineHeight )) {
840+ workingText .setSpan (new CustomLineHeightSpan (lineHeight ), 0 , workingText .length (), spanFlags );
841+ }
847842 }
848843
849844 private static boolean sameTextForSpan (
@@ -862,73 +857,6 @@ private static boolean sameTextForSpan(
862857 return true ;
863858 }
864859
865- // This is hacked in for Fabric. When we delete non-Fabric code, we might be able to simplify or
866- // clean this up a bit.
867- private void addSpansForMeasurement (Spannable spannable ) {
868- if (!mFabricViewStateManager .hasStateWrapper ()) {
869- return ;
870- }
871-
872- boolean originalDisableTextDiffing = mDisableTextDiffing ;
873- mDisableTextDiffing = true ;
874-
875- int start = 0 ;
876- int end = spannable .length ();
877-
878- // Remove duplicate spans we might add here
879- Object [] spans = spannable .getSpans (0 , length (), Object .class );
880- for (Object span : spans ) {
881- int spanFlags = spannable .getSpanFlags (span );
882- boolean isInclusive =
883- (spanFlags & Spanned .SPAN_INCLUSIVE_INCLUSIVE ) == Spanned .SPAN_INCLUSIVE_INCLUSIVE
884- || (spanFlags & Spanned .SPAN_INCLUSIVE_EXCLUSIVE ) == Spanned .SPAN_INCLUSIVE_EXCLUSIVE ;
885- if (isInclusive
886- && span instanceof ReactSpan
887- && spannable .getSpanStart (span ) == start
888- && spannable .getSpanEnd (span ) == end ) {
889- spannable .removeSpan (span );
890- }
891- }
892-
893- List <TextLayoutManager .SetSpanOperation > ops = new ArrayList <>();
894-
895- if (!Float .isNaN (mTextAttributes .getLetterSpacing ())) {
896- ops .add (
897- new TextLayoutManager .SetSpanOperation (
898- start , end , new CustomLetterSpacingSpan (mTextAttributes .getLetterSpacing ())));
899- }
900- ops .add (
901- new TextLayoutManager .SetSpanOperation (
902- start , end , new ReactAbsoluteSizeSpan ((int ) mTextAttributes .getEffectiveFontSize ())));
903- if (mFontStyle != UNSET || mFontWeight != UNSET || mFontFamily != null ) {
904- ops .add (
905- new TextLayoutManager .SetSpanOperation (
906- start ,
907- end ,
908- new CustomStyleSpan (
909- mFontStyle ,
910- mFontWeight ,
911- null , // TODO: do we need to support FontFeatureSettings / fontVariant?
912- mFontFamily ,
913- getReactContext (ReactEditText .this ).getAssets ())));
914- }
915- if (!Float .isNaN (mTextAttributes .getEffectiveLineHeight ())) {
916- ops .add (
917- new TextLayoutManager .SetSpanOperation (
918- start , end , new CustomLineHeightSpan (mTextAttributes .getEffectiveLineHeight ())));
919- }
920-
921- int priority = 0 ;
922- for (TextLayoutManager .SetSpanOperation op : ops ) {
923- // Actual order of calling {@code execute} does NOT matter,
924- // but the {@code priority} DOES matter.
925- op .execute (spannable , priority );
926- priority ++;
927- }
928-
929- mDisableTextDiffing = originalDisableTextDiffing ;
930- }
931-
932860 protected boolean showSoftKeyboard () {
933861 return mInputMethodManager .showSoftInput (this , 0 );
934862 }
@@ -1215,7 +1143,7 @@ public FabricViewStateManager getFabricViewStateManager() {
12151143 * TextLayoutManager.java with some very minor modifications. There's some duplication between
12161144 * here and TextLayoutManager, so there might be an opportunity for refactor.
12171145 */
1218- private void updateCachedSpannable (boolean resetStyles ) {
1146+ private void updateCachedSpannable () {
12191147 // Noops in non-Fabric
12201148 if (mFabricViewStateManager == null || !mFabricViewStateManager .hasStateWrapper ()) {
12211149 return ;
@@ -1225,12 +1153,6 @@ private void updateCachedSpannable(boolean resetStyles) {
12251153 return ;
12261154 }
12271155
1228- if (resetStyles ) {
1229- mIsSettingTextFromCacheUpdate = true ;
1230- addSpansForMeasurement (getText ());
1231- mIsSettingTextFromCacheUpdate = false ;
1232- }
1233-
12341156 Editable currentText = getText ();
12351157 boolean haveText = currentText != null && currentText .length () > 0 ;
12361158
@@ -1273,7 +1195,6 @@ private void updateCachedSpannable(boolean resetStyles) {
12731195 // - android.app.Activity.dispatchKeyEvent (Activity.java:3447)
12741196 try {
12751197 sb .append (currentText .subSequence (0 , currentText .length ()));
1276- restoreStyleEquivalentSpans (sb );
12771198 } catch (IndexOutOfBoundsException e ) {
12781199 ReactSoftExceptionLogger .logSoftException (TAG , e );
12791200 }
@@ -1289,11 +1210,9 @@ private void updateCachedSpannable(boolean resetStyles) {
12891210 // Measure something so we have correct height, even if there's no string.
12901211 sb .append ("I" );
12911212 }
1292-
1293- // Make sure that all text styles are applied when we're measurable the hint or "blank" text
1294- addSpansForMeasurement (sb );
12951213 }
12961214
1215+ addSpansFromStyleAttributes (sb );
12971216 TextLayoutManager .setCachedSpannabledForTag (getId (), sb );
12981217 }
12991218
@@ -1308,7 +1227,7 @@ void setEventDispatcher(@Nullable EventDispatcher eventDispatcher) {
13081227 private class TextWatcherDelegator implements TextWatcher {
13091228 @ Override
13101229 public void beforeTextChanged (CharSequence s , int start , int count , int after ) {
1311- if (!mIsSettingTextFromCacheUpdate && ! mIsSettingTextFromJS && mListeners != null ) {
1230+ if (!mIsSettingTextFromJS && mListeners != null ) {
13121231 for (TextWatcher listener : mListeners ) {
13131232 listener .beforeTextChanged (s , start , count , after );
13141233 }
@@ -1322,23 +1241,20 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
13221241 TAG , "onTextChanged[" + getId () + "]: " + s + " " + start + " " + before + " " + count );
13231242 }
13241243
1325- if (!mIsSettingTextFromCacheUpdate ) {
1326- if (!mIsSettingTextFromJS && mListeners != null ) {
1327- for (TextWatcher listener : mListeners ) {
1328- listener .onTextChanged (s , start , before , count );
1329- }
1244+ if (!mIsSettingTextFromJS && mListeners != null ) {
1245+ for (TextWatcher listener : mListeners ) {
1246+ listener .onTextChanged (s , start , before , count );
13301247 }
1331-
1332- updateCachedSpannable (
1333- !mIsSettingTextFromJS && !mIsSettingTextFromState && start == 0 && before == 0 );
13341248 }
13351249
1250+ updateCachedSpannable ();
1251+
13361252 onContentSizeChange ();
13371253 }
13381254
13391255 @ Override
13401256 public void afterTextChanged (Editable s ) {
1341- if (!mIsSettingTextFromCacheUpdate && ! mIsSettingTextFromJS && mListeners != null ) {
1257+ if (!mIsSettingTextFromJS && mListeners != null ) {
13421258 for (TextWatcher listener : mListeners ) {
13431259 listener .afterTextChanged (s );
13441260 }
0 commit comments