Fix a pixel difference in width when measured in RTL
This is reproduced when letter spacing is provided.
For more details, please check the bug.
Fixes: 233856978
Test: new tests
Change-Id: I52143ddb41da78ae5e21e259339206a8f93b979c
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextDelegateIntegrationTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextDelegateIntegrationTest.kt
index 1e699be..3120601 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextDelegateIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextDelegateIntegrationTest.kt
@@ -183,6 +183,23 @@
assertThat(layoutResult.lineCount).isEqualTo(2)
assertThat(layoutResult.isLineEllipsized(1)).isTrue()
}
+
+ @Test
+ fun TextLayoutResult_sameWidth_inRtlAndLtr_withLetterSpacing() {
+ val fontSize = 20f
+ val text = AnnotatedString(text = "Hello World")
+ val textDelegate = TextDelegate(
+ text = text,
+ style = TextStyle(fontSize = fontSize.sp, letterSpacing = 0.5.sp),
+ overflow = TextOverflow.Ellipsis,
+ density = density,
+ fontFamilyResolver = fontFamilyResolver
+ )
+ val layoutResultLtr = textDelegate.layout(Constraints(), LayoutDirection.Ltr)
+ val layoutResultRtl = textDelegate.layout(Constraints(), LayoutDirection.Rtl)
+
+ assertThat(layoutResultLtr.size.width).isEqualTo(layoutResultRtl.size.width)
+ }
}
private fun TextLayoutResult.toBitmap() = Bitmap.createBitmap(
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt
index 1d24cbfb..83169be 100644
--- a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt
+++ b/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt
@@ -125,6 +125,28 @@
assertLineCount(defaultText)
}
+ @Test
+ fun intrinsicWidth_sameInLtrAndRtl() {
+ val text = SpannableString("asdf")
+
+ val intrinsicsLtr = LayoutIntrinsics(text, defaultPaint, LayoutCompat.TEXT_DIRECTION_LTR)
+ val intrinsicsRtl = LayoutIntrinsics(text, defaultPaint, LayoutCompat.TEXT_DIRECTION_RTL)
+
+ assertThat(intrinsicsLtr.maxIntrinsicWidth).isEqualTo(intrinsicsRtl.maxIntrinsicWidth)
+ }
+
+ @Test
+ fun intrinsicWidth_sameInLtrAndRtl_withLetterSpacing() {
+ val text = SpannableString("asdf").apply {
+ setSpan(LetterSpacingSpanPx(letterSpacingPx))
+ }
+
+ val intrinsicsLtr = LayoutIntrinsics(text, defaultPaint, LayoutCompat.TEXT_DIRECTION_LTR)
+ val intrinsicsRtl = LayoutIntrinsics(text, defaultPaint, LayoutCompat.TEXT_DIRECTION_RTL)
+
+ assertThat(intrinsicsLtr.maxIntrinsicWidth).isEqualTo(intrinsicsRtl.maxIntrinsicWidth)
+ }
+
private fun assertLineCount(text: CharSequence, paint: TextPaint = defaultPaint) {
val intrinsics = LayoutIntrinsics(text, paint, LayoutCompat.TEXT_DIRECTION_LTR)
assertThat(
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt
index d9b29cd..af9cb35 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt
@@ -24,6 +24,7 @@
import androidx.compose.ui.text.android.style.LetterSpacingSpanPx
import java.text.BreakIterator
import java.util.PriorityQueue
+import kotlin.math.ceil
/**
* Computes and caches the text layout intrinsic values such as min/max width.
@@ -59,8 +60,17 @@
* of text where no soft line breaks are applied.
*/
val maxIntrinsicWidth: Float by lazy(LazyThreadSafetyMode.NONE) {
- var desiredWidth: Float = boringMetrics?.width?.toFloat()
- ?: Layout.getDesiredWidth(charSequence, 0, charSequence.length, textPaint)
+ var desiredWidth = boringMetrics?.width?.toFloat()
+
+ // boring metrics doesn't cover RTL text so we fallback to different calculation when boring
+ // metrics can't be calculated
+ if (desiredWidth == null) {
+ // b/233856978, apply `ceil` function here to be consistent with the boring metrics
+ // width calculation that does it under the hood, too
+ desiredWidth = ceil(
+ Layout.getDesiredWidth(charSequence, 0, charSequence.length, textPaint)
+ )
+ }
if (shouldIncreaseMaxIntrinsic(desiredWidth, charSequence, textPaint)) {
// b/173574230, increase maxIntrinsicWidth, so that StaticLayout won't form 2
// lines for the given maxIntrinsicWidth