Skip to content

Commit 12e066b

Browse files
generatedunixname1608173377072046meta-codesync[bot]
authored andcommitted
Limit Android 15 bounds-based text width to RTL text (#59504)
Summary: Pull Request resolved: #59504 On Android 15+, every exactly sized `StaticLayout` enables `setUseBoundsForWidth` and `setShiftDrawingOffsetForStartOverhang` to keep RTL line-start ink from clipping. The exact width usually comes from an earlier `AT_MOST` measurement, which is advance-based. When a font's glyph bounds extend past their advances (seen with some OEM system fonts), a single-line LTR `Text` with `numberOfLines={1}` no longer fits the width it was measured at, and gets ellipsized well inside its own frame. Only enable the bounds-based width and start-overhang shift when the text contains RTL characters, which is the case the start-overhang handling was added for. LTR-only text goes back to advance-based layout in both passes, so the layout matches the measurement. Changelog: [Android][Fixed] - Fix single-line LTR `Text` being ellipsized inside its measured width on Android 15+ Reviewed By: christophpurrer Differential Revision: D126965612 fbshipit-source-id: 4288a23828c252f999668b8cc0b3d427cdf8dcec
1 parent 7320bf4 commit 12e066b

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,12 @@ internal object TextLayoutManager {
902902
}
903903
}
904904
}
905-
val enableStartOverhang = widthYogaMeasureMode == YogaMeasureMode.EXACTLY
905+
// Bounds-based width is limited to text with RTL characters. An EXACTLY width usually comes
906+
// from an earlier advance-based AT_MOST measurement, and LTR glyph bounds that are wider than
907+
// their advances (some OEM fonts) would make a single line overflow that width and ellipsize.
908+
val enableStartOverhang =
909+
widthYogaMeasureMode == YogaMeasureMode.EXACTLY &&
910+
TextDirectionHeuristics.ANYRTL_LTR.isRtl(text, 0, text.length)
906911
val layout =
907912
buildLayout(
908913
text,

‎packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerStartOverhangTest.kt‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,29 @@ class TextLayoutManagerStartOverhangTest {
4646
assertThat(getBooleanLayoutProperty(layout, "getShiftDrawingOffsetForStartOverhang")).isFalse()
4747
}
4848

49+
@Test
50+
@Config(sdk = [35])
51+
fun `EXACTLY mode keeps advance based width measurement for LTR text`() {
52+
val layout =
53+
createLayout(
54+
YogaMeasureMode.EXACTLY,
55+
text = SpannableString("Just listed"),
56+
ellipsizeMode = TextUtils.TruncateAt.END,
57+
maxNumberOfLines = 1,
58+
)
59+
60+
assertThat(getBooleanLayoutProperty(layout, "getUseBoundsForWidth")).isFalse()
61+
assertThat(getBooleanLayoutProperty(layout, "getShiftDrawingOffsetForStartOverhang")).isFalse()
62+
}
63+
64+
@Test
65+
@Config(sdk = [35])
66+
fun `EXACTLY mode enables start overhang support for mixed LTR and RTL text`() {
67+
val layout = createLayout(YogaMeasureMode.EXACTLY, text = SpannableString("Hello שלום"))
68+
69+
assertThat(getBooleanLayoutProperty(layout, "getUseBoundsForWidth")).isTrue()
70+
}
71+
4972
@Test
5073
@Config(sdk = [35])
5174
fun `RTL right overhang is rounded up to reserve whole pixels`() {

0 commit comments

Comments
 (0)