mirror of
https://github.com/react/react-native.git
synced 2026-09-28 05:13:06 +08:00
Guard RTL overhang handling for fixed-line text (#58685)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/58685 Add Android regression coverage for exactly constrained RTL text that reserves start-side ink overhang while enforcing a maximum line count and ellipsizing overflow. Also cover the mixed-direction exclusion, where a shared right-side reservation must not be applied. Changelog: [Internal] ___ Differential Revision: D121670747 fbshipit-source-id: 307461e4ef24d0dad90e609aa8dd7845efc70fa7
This commit is contained in:
committed by
meta-codesync[bot]
parent
447ccd1ddb
commit
5c87ca977a
+72
-6
@@ -7,6 +7,7 @@
|
||||
|
||||
package com.facebook.react.views.text
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.RectF
|
||||
import android.text.BoringLayout
|
||||
import android.text.Layout
|
||||
@@ -24,6 +25,7 @@ import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@SuppressLint("NewApi")
|
||||
class TextLayoutManagerStartOverhangTest {
|
||||
|
||||
@Test
|
||||
@@ -56,6 +58,64 @@ class TextLayoutManagerStartOverhangTest {
|
||||
assertThat(TextLayoutManager.getRtlRightOverhang(layout)).isEqualTo(8)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(sdk = [35])
|
||||
fun `RTL overhang reservation preserves max lines and ellipsis`() {
|
||||
val initialLayout = mock<Layout>()
|
||||
whenever(initialLayout.lineCount).thenReturn(2)
|
||||
whenever(initialLayout.width).thenReturn(LAYOUT_WIDTH.toInt())
|
||||
whenever(initialLayout.getParagraphDirection(any())).thenReturn(Layout.DIR_RIGHT_TO_LEFT)
|
||||
whenever(initialLayout.computeDrawingBoundingBox())
|
||||
.thenReturn(RectF(10f, 0f, LAYOUT_WIDTH + 7.1f, 40f))
|
||||
var rebuiltWidth = 0
|
||||
|
||||
val layout =
|
||||
TextLayoutManager.adjustLayoutForRtlRightOverhang(
|
||||
initialLayout,
|
||||
LAYOUT_WIDTH.toInt(),
|
||||
) { adjustedWidth ->
|
||||
rebuiltWidth = adjustedWidth
|
||||
createLayout(
|
||||
YogaMeasureMode.EXACTLY,
|
||||
text =
|
||||
SpannableString(
|
||||
listOf(
|
||||
"\u200Ffirst paragraph",
|
||||
"\u200Fsecond paragraph",
|
||||
"\u200Fthird paragraph",
|
||||
)
|
||||
.joinToString("\n"),
|
||||
),
|
||||
layoutWidth = adjustedWidth.toFloat(),
|
||||
ellipsizeMode = TextUtils.TruncateAt.END,
|
||||
maxNumberOfLines = 2,
|
||||
)
|
||||
}
|
||||
|
||||
assertThat(rebuiltWidth).isEqualTo(192)
|
||||
assertThat(layout.width).isEqualTo(rebuiltWidth)
|
||||
assertThat(layout.lineCount).isEqualTo(2)
|
||||
assertThat(layout.getEllipsisCount(layout.lineCount - 1)).isGreaterThan(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(sdk = [35])
|
||||
fun `mixed direction text does not reserve RTL right overhang`() {
|
||||
val initialLayout = mock<Layout>()
|
||||
whenever(initialLayout.lineCount).thenReturn(2)
|
||||
whenever(initialLayout.width).thenReturn(200)
|
||||
whenever(initialLayout.getParagraphDirection(0)).thenReturn(Layout.DIR_RIGHT_TO_LEFT)
|
||||
whenever(initialLayout.getParagraphDirection(1)).thenReturn(Layout.DIR_LEFT_TO_RIGHT)
|
||||
whenever(initialLayout.computeDrawingBoundingBox()).thenReturn(RectF(10f, 0f, 208f, 40f))
|
||||
|
||||
val layout =
|
||||
TextLayoutManager.adjustLayoutForRtlRightOverhang(initialLayout, 200) {
|
||||
throw AssertionError("Mixed-direction text must not be rebuilt")
|
||||
}
|
||||
|
||||
assertThat(layout).isSameAs(initialLayout)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(sdk = [34])
|
||||
fun `EXACTLY mode remains supported before Android 15`() {
|
||||
@@ -64,9 +124,15 @@ class TextLayoutManagerStartOverhangTest {
|
||||
assertThat(layout.width).isEqualTo(LAYOUT_WIDTH.toInt())
|
||||
}
|
||||
|
||||
private fun createLayout(widthMode: YogaMeasureMode): Layout {
|
||||
val text = SpannableString("\u0622\u064a\u0629 \u0627\u0644\u0643\u0631\u0633\u064a")
|
||||
val paint = TextPaint(TextPaint.ANTI_ALIAS_FLAG).apply { textSize = 26f }
|
||||
private fun createLayout(
|
||||
widthMode: YogaMeasureMode,
|
||||
text: SpannableString =
|
||||
SpannableString("\u0622\u064a\u0629 \u0627\u0644\u0643\u0631\u0633\u064a"),
|
||||
layoutWidth: Float = LAYOUT_WIDTH,
|
||||
ellipsizeMode: TextUtils.TruncateAt? = null,
|
||||
maxNumberOfLines: Int = 2,
|
||||
paint: TextPaint = TextPaint(TextPaint.ANTI_ALIAS_FLAG).apply { textSize = 26f },
|
||||
): Layout {
|
||||
val method =
|
||||
TextLayoutManager::class
|
||||
.java
|
||||
@@ -91,15 +157,15 @@ class TextLayoutManagerStartOverhangTest {
|
||||
TextLayoutManager,
|
||||
text,
|
||||
null,
|
||||
LAYOUT_WIDTH,
|
||||
layoutWidth,
|
||||
widthMode,
|
||||
/* includeFontPadding = */ false,
|
||||
/* textBreakStrategy = */ Layout.BREAK_STRATEGY_HIGH_QUALITY,
|
||||
/* hyphenationFrequency = */ Layout.HYPHENATION_FREQUENCY_NONE,
|
||||
Layout.Alignment.ALIGN_NORMAL,
|
||||
/* justificationMode = */ 0,
|
||||
/* ellipsizeMode = */ null,
|
||||
/* maxNumberOfLines = */ 2,
|
||||
/* ellipsizeMode = */ ellipsizeMode,
|
||||
/* maxNumberOfLines = */ maxNumberOfLines,
|
||||
paint,
|
||||
) as Layout
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user