Skip to content

Commit 99119a2

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Fix TextLayoutManager MeasureMode Regression (#51183)
Summary: Pull Request resolved: #51183 D58818560 tried to deduplicate some code, but introduced an error, where we no longer correctly incorporate the width MeasureMode into the text layout that we create, instead, passing `YogaMeasureMode.EXACTLY`. In effect, this means the Android layout created always takes up the maximum allowable space, even if content is smaller. This is later masked, because our returned measure when `AT_MOST` is based on maximum line length, and the layout is then recreated when drawing a TextView, but means: 1. Attachments may not be positioned correctly, when using a non-left-aligned paragraph alignment 2. Directly drawing the layout shows the wrong thing Changelog: [Android][Fixed] - Fix TextLayoutManager MeasureMode Regression Reviewed By: rshest Differential Revision: D74366936 fbshipit-source-id: 3eda8c716ba9790a61c2da19023e140afbb6971d
1 parent 1c52eec commit 99119a2

4 files changed

Lines changed: 28 additions & 11 deletions

File tree

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2357,14 +2357,12 @@ public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/brid
23572357
public fun initialize ()V
23582358
public fun invalidate ()V
23592359
public fun markActiveTouchForTag (II)V
2360-
public fun measurePreparedLayout (Lcom/facebook/react/views/text/PreparedLayout;FFFF)[F
23612360
public fun onAllAnimationsComplete ()V
23622361
public fun onAnimationStarted ()V
23632362
public fun onHostDestroy ()V
23642363
public fun onHostPause ()V
23652364
public fun onHostResume ()V
23662365
public fun onRequestEventBeat ()V
2367-
public fun prepareLayout (ILcom/facebook/react/common/mapbuffer/ReadableMapBuffer;Lcom/facebook/react/common/mapbuffer/ReadableMapBuffer;FF)Lcom/facebook/react/views/text/PreparedLayout;
23682366
public fun prependUIBlock (Lcom/facebook/react/fabric/interop/UIBlock;)V
23692367
public fun profileNextBatch ()V
23702368
public fun receiveEvent (IILjava/lang/String;Lcom/facebook/react/bridge/WritableMap;)V

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,11 +654,14 @@ private long measureMapBuffer(
654654

655655
@AnyThread
656656
@ThreadConfined(ANY)
657+
@UnstableReactNativeAPI
657658
public PreparedLayout prepareLayout(
658659
int surfaceId,
659660
ReadableMapBuffer attributedString,
660661
ReadableMapBuffer paragraphAttributes,
662+
float minWidth,
661663
float maxWidth,
664+
float minHeight,
662665
float maxHeight) {
663666
SurfaceMountingManager surfaceMountingManager =
664667
mMountingManager.getSurfaceManagerEnforced(surfaceId, "prepareLayout");
@@ -667,8 +670,9 @@ public PreparedLayout prepareLayout(
667670
Preconditions.checkNotNull(surfaceMountingManager.getContext()),
668671
attributedString,
669672
paragraphAttributes,
670-
PixelUtil.toPixelFromDIP(maxWidth),
671-
PixelUtil.toPixelFromDIP(maxHeight),
673+
getYogaSize(minWidth, maxWidth),
674+
getYogaMeasureMode(minWidth, maxWidth),
675+
getYogaSize(minHeight, maxHeight),
672676
null /* T219881133: Migrate away from ReactTextViewManagerCallback */);
673677

674678
int maximumNumberOfLines =
@@ -681,6 +685,7 @@ public PreparedLayout prepareLayout(
681685

682686
@AnyThread
683687
@ThreadConfined(ANY)
688+
@UnstableReactNativeAPI
684689
public float[] measurePreparedLayout(
685690
PreparedLayout preparedLayout,
686691
float minWidth,

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
import com.facebook.react.views.text.internal.span.SetSpanOperation;
5353
import com.facebook.react.views.text.internal.span.ShadowStyleSpan;
5454
import com.facebook.react.views.text.internal.span.TextInlineViewPlaceholderSpan;
55-
import com.facebook.yoga.YogaConstants;
5655
import com.facebook.yoga.YogaMeasureMode;
5756
import com.facebook.yoga.YogaMeasureOutput;
5857
import java.util.ArrayList;
@@ -390,8 +389,7 @@ private static Layout createLayout(
390389
boolean isScriptRTL = TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(text, 0, spanLength);
391390

392391
if (boring == null
393-
&& (unconstrainedWidth
394-
|| (!YogaConstants.isUndefined(desiredWidth) && desiredWidth <= width))) {
392+
&& (unconstrainedWidth || (!Float.isNaN(desiredWidth) && desiredWidth <= width))) {
395393
// Is used when the width is not known and the text is not boring, ie. if it contains
396394
// unicode characters.
397395

@@ -510,6 +508,7 @@ public static Layout createLayout(
510508
MapBuffer attributedString,
511509
MapBuffer paragraphAttributes,
512510
float width,
511+
YogaMeasureMode widthYogaMeasureMode,
513512
float height,
514513
@Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) {
515514
Spannable text =
@@ -582,7 +581,7 @@ public static Layout createLayout(
582581
text,
583582
boring,
584583
width,
585-
YogaMeasureMode.EXACTLY,
584+
widthYogaMeasureMode,
586585
includeFontPadding,
587586
textBreakStrategy,
588587
hyphenationFrequency,
@@ -696,6 +695,7 @@ public static long measureText(
696695
attributedString,
697696
paragraphAttributes,
698697
width,
698+
widthYogaMeasureMode,
699699
height,
700700
reactTextViewManagerCallback);
701701

@@ -961,7 +961,14 @@ public static WritableArray measureLines(
961961
float width,
962962
float height) {
963963
Layout layout =
964-
createLayout(context, attributedString, paragraphAttributes, width, height, null);
964+
createLayout(
965+
context,
966+
attributedString,
967+
paragraphAttributes,
968+
width,
969+
YogaMeasureMode.EXACTLY,
970+
height,
971+
null);
965972
return FontMetricsUtil.getFontMetrics(
966973
layout.getText(), layout, Preconditions.checkNotNull(sTextPaintInstance.get()), context);
967974
}

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,22 +308,29 @@ TextLayoutManager::PreparedLayout TextLayoutManager::prepareLayout(
308308
JReadableMapBuffer::javaobject,
309309
JReadableMapBuffer::javaobject,
310310
jfloat,
311+
jfloat,
312+
jfloat,
311313
jfloat)>("prepareLayout");
312314

313315
auto attributedStringMB =
314316
JReadableMapBuffer::createWithContents(toMapBuffer(attributedString));
315317
auto paragraphAttributesMB =
316318
JReadableMapBuffer::createWithContents(toMapBuffer(paragraphAttributes));
317319

320+
auto minimumSize = layoutConstraints.minimumSize;
321+
auto maximumSize = layoutConstraints.maximumSize;
322+
318323
// T222682416: We don't have any global cache here. We should investigate
319324
// whether that is desirable
320325
return {jni::make_global(prepareLayout(
321326
fabricUIManager,
322327
layoutContext.surfaceId,
323328
attributedStringMB.get(),
324329
paragraphAttributesMB.get(),
325-
layoutConstraints.maximumSize.width,
326-
layoutConstraints.maximumSize.height))};
330+
minimumSize.width,
331+
maximumSize.width,
332+
minimumSize.height,
333+
maximumSize.height))};
327334
}
328335

329336
TextMeasurement TextLayoutManager::measurePreparedLayout(

0 commit comments

Comments
 (0)