77
88package com.facebook.react.views.text
99
10+ import android.annotation.SuppressLint
1011import android.graphics.RectF
1112import android.text.BoringLayout
1213import android.text.Layout
@@ -24,6 +25,7 @@ import org.robolectric.RobolectricTestRunner
2425import org.robolectric.annotation.Config
2526
2627@RunWith(RobolectricTestRunner ::class )
28+ @SuppressLint(" NewApi" )
2729class TextLayoutManagerStartOverhangTest {
2830
2931 @Test
@@ -56,6 +58,64 @@ class TextLayoutManagerStartOverhangTest {
5658 assertThat(TextLayoutManager .getRtlRightOverhang(layout)).isEqualTo(8 )
5759 }
5860
61+ @Test
62+ @Config(sdk = [35 ])
63+ fun `RTL overhang reservation preserves max lines and ellipsis` () {
64+ val initialLayout = mock<Layout >()
65+ whenever(initialLayout.lineCount).thenReturn(2 )
66+ whenever(initialLayout.width).thenReturn(LAYOUT_WIDTH .toInt())
67+ whenever(initialLayout.getParagraphDirection(any())).thenReturn(Layout .DIR_RIGHT_TO_LEFT )
68+ whenever(initialLayout.computeDrawingBoundingBox())
69+ .thenReturn(RectF (10f , 0f , LAYOUT_WIDTH + 7.1f , 40f ))
70+ var rebuiltWidth = 0
71+
72+ val layout =
73+ TextLayoutManager .adjustLayoutForRtlRightOverhang(
74+ initialLayout,
75+ LAYOUT_WIDTH .toInt(),
76+ ) { adjustedWidth ->
77+ rebuiltWidth = adjustedWidth
78+ createLayout(
79+ YogaMeasureMode .EXACTLY ,
80+ text =
81+ SpannableString (
82+ listOf (
83+ " \u200F first paragraph" ,
84+ " \u200F second paragraph" ,
85+ " \u200F third paragraph" ,
86+ )
87+ .joinToString(" \n " ),
88+ ),
89+ layoutWidth = adjustedWidth.toFloat(),
90+ ellipsizeMode = TextUtils .TruncateAt .END ,
91+ maxNumberOfLines = 2 ,
92+ )
93+ }
94+
95+ assertThat(rebuiltWidth).isEqualTo(192 )
96+ assertThat(layout.width).isEqualTo(rebuiltWidth)
97+ assertThat(layout.lineCount).isEqualTo(2 )
98+ assertThat(layout.getEllipsisCount(layout.lineCount - 1 )).isGreaterThan(0 )
99+ }
100+
101+ @Test
102+ @Config(sdk = [35 ])
103+ fun `mixed direction text does not reserve RTL right overhang` () {
104+ val initialLayout = mock<Layout >()
105+ whenever(initialLayout.lineCount).thenReturn(2 )
106+ whenever(initialLayout.width).thenReturn(200 )
107+ whenever(initialLayout.getParagraphDirection(0 )).thenReturn(Layout .DIR_RIGHT_TO_LEFT )
108+ whenever(initialLayout.getParagraphDirection(1 )).thenReturn(Layout .DIR_LEFT_TO_RIGHT )
109+ whenever(initialLayout.computeDrawingBoundingBox()).thenReturn(RectF (10f , 0f , 208f , 40f ))
110+
111+ val layout =
112+ TextLayoutManager .adjustLayoutForRtlRightOverhang(initialLayout, 200 ) {
113+ throw AssertionError (" Mixed-direction text must not be rebuilt" )
114+ }
115+
116+ assertThat(layout).isSameAs(initialLayout)
117+ }
118+
59119 @Test
60120 @Config(sdk = [34 ])
61121 fun `EXACTLY mode remains supported before Android 15` () {
@@ -64,9 +124,15 @@ class TextLayoutManagerStartOverhangTest {
64124 assertThat(layout.width).isEqualTo(LAYOUT_WIDTH .toInt())
65125 }
66126
67- private fun createLayout (widthMode : YogaMeasureMode ): Layout {
68- val text = SpannableString (" \u0622\u064a\u0629 \u0627\u0644\u0643\u0631\u0633\u064a " )
69- val paint = TextPaint (TextPaint .ANTI_ALIAS_FLAG ).apply { textSize = 26f }
127+ private fun createLayout (
128+ widthMode : YogaMeasureMode ,
129+ text : SpannableString =
130+ SpannableString ("\u0622\u064a\u0629 \u0627\u0644\u0643\u0631\u0633\u064a"),
131+ layoutWidth : Float = LAYOUT_WIDTH ,
132+ ellipsizeMode : TextUtils .TruncateAt ? = null,
133+ maxNumberOfLines : Int = 2,
134+ paint : TextPaint = TextPaint (TextPaint .ANTI_ALIAS_FLAG ).apply { textSize = 26f },
135+ ): Layout {
70136 val method =
71137 TextLayoutManager ::class
72138 .java
@@ -91,15 +157,15 @@ class TextLayoutManagerStartOverhangTest {
91157 TextLayoutManager ,
92158 text,
93159 null ,
94- LAYOUT_WIDTH ,
160+ layoutWidth ,
95161 widthMode,
96162 /* includeFontPadding = */ false ,
97163 /* textBreakStrategy = */ Layout .BREAK_STRATEGY_HIGH_QUALITY ,
98164 /* hyphenationFrequency = */ Layout .HYPHENATION_FREQUENCY_NONE ,
99165 Layout .Alignment .ALIGN_NORMAL ,
100166 /* justificationMode = */ 0 ,
101- /* ellipsizeMode = */ null ,
102- /* maxNumberOfLines = */ 2 ,
167+ /* ellipsizeMode = */ ellipsizeMode ,
168+ /* maxNumberOfLines = */ maxNumberOfLines ,
103169 paint,
104170 ) as Layout
105171 }
0 commit comments