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 }
@@ -1210,7 +1138,7 @@ public FabricViewStateManager getFabricViewStateManager() {
12101138 * TextLayoutManager.java with some very minor modifications. There's some duplication between
12111139 * here and TextLayoutManager, so there might be an opportunity for refactor.
12121140 */
1213- private void updateCachedSpannable (boolean resetStyles ) {
1141+ private void updateCachedSpannable () {
12141142 // Noops in non-Fabric
12151143 if (mFabricViewStateManager == null || !mFabricViewStateManager .hasStateWrapper ()) {
12161144 return ;
@@ -1220,12 +1148,6 @@ private void updateCachedSpannable(boolean resetStyles) {
12201148 return ;
12211149 }
12221150
1223- if (resetStyles ) {
1224- mIsSettingTextFromCacheUpdate = true ;
1225- addSpansForMeasurement (getText ());
1226- mIsSettingTextFromCacheUpdate = false ;
1227- }
1228-
12291151 Editable currentText = getText ();
12301152 boolean haveText = currentText != null && currentText .length () > 0 ;
12311153
@@ -1268,7 +1190,6 @@ private void updateCachedSpannable(boolean resetStyles) {
12681190 // - android.app.Activity.dispatchKeyEvent (Activity.java:3447)
12691191 try {
12701192 sb .append (currentText .subSequence (0 , currentText .length ()));
1271- restoreStyleEquivalentSpans (sb );
12721193 } catch (IndexOutOfBoundsException e ) {
12731194 ReactSoftExceptionLogger .logSoftException (TAG , e );
12741195 }
@@ -1284,11 +1205,9 @@ private void updateCachedSpannable(boolean resetStyles) {
12841205 // Measure something so we have correct height, even if there's no string.
12851206 sb .append ("I" );
12861207 }
1287-
1288- // Make sure that all text styles are applied when we're measurable the hint or "blank" text
1289- addSpansForMeasurement (sb );
12901208 }
12911209
1210+ addSpansFromStyleAttributes (sb );
12921211 TextLayoutManager .setCachedSpannabledForTag (getId (), sb );
12931212 }
12941213
@@ -1303,7 +1222,7 @@ void setEventDispatcher(@Nullable EventDispatcher eventDispatcher) {
13031222 private class TextWatcherDelegator implements TextWatcher {
13041223 @ Override
13051224 public void beforeTextChanged (CharSequence s , int start , int count , int after ) {
1306- if (!mIsSettingTextFromCacheUpdate && ! mIsSettingTextFromJS && mListeners != null ) {
1225+ if (!mIsSettingTextFromJS && mListeners != null ) {
13071226 for (TextWatcher listener : mListeners ) {
13081227 listener .beforeTextChanged (s , start , count , after );
13091228 }
@@ -1317,23 +1236,20 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
13171236 TAG , "onTextChanged[" + getId () + "]: " + s + " " + start + " " + before + " " + count );
13181237 }
13191238
1320- if (!mIsSettingTextFromCacheUpdate ) {
1321- if (!mIsSettingTextFromJS && mListeners != null ) {
1322- for (TextWatcher listener : mListeners ) {
1323- listener .onTextChanged (s , start , before , count );
1324- }
1239+ if (!mIsSettingTextFromJS && mListeners != null ) {
1240+ for (TextWatcher listener : mListeners ) {
1241+ listener .onTextChanged (s , start , before , count );
13251242 }
1326-
1327- updateCachedSpannable (
1328- !mIsSettingTextFromJS && !mIsSettingTextFromState && start == 0 && before == 0 );
13291243 }
13301244
1245+ updateCachedSpannable ();
1246+
13311247 onContentSizeChange ();
13321248 }
13331249
13341250 @ Override
13351251 public void afterTextChanged (Editable s ) {
1336- if (!mIsSettingTextFromCacheUpdate && ! mIsSettingTextFromJS && mListeners != null ) {
1252+ if (!mIsSettingTextFromJS && mListeners != null ) {
13371253 for (TextWatcher listener : mListeners ) {
13381254 listener .afterTextChanged (s );
13391255 }
0 commit comments