From 26f23fe4af1ead4fa4da3fa25f0ebbb8c2b42f26 Mon Sep 17 00:00:00 2001 From: Vladimir Mazunin Date: Mon, 13 May 2024 01:19:16 +0400 Subject: [PATCH 1/8] fixed initial cursor position in empty textfield when TextAlignment is set --- .../compose/ui/text/SkiaParagraph.skiko.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt index 88fc5bb8d0abd..5a104e7a595cd 100644 --- a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt +++ b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.text.platform.SkiaParagraphIntrinsics import androidx.compose.ui.text.platform.cursorHorizontalPosition import androidx.compose.ui.text.style.LineHeightStyle import androidx.compose.ui.text.style.ResolvedTextDirection +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.isUnspecified @@ -57,6 +58,8 @@ internal class SkiaParagraph( internal val defaultFont get() = layouter.defaultFont + private val textAlign: TextAlign = layouter.textStyle.textAlign + /** * Paragraph isn't always immutable, it could be changed via [paint] method without * rerunning layout @@ -247,7 +250,7 @@ internal class SkiaParagraph( val isRtl = paragraphIntrinsics.textDirection == ResolvedTextDirection.Rtl val isLtr = !isRtl return when { - prevBox == null && nextBox == null -> if (isRtl) width else 0f + prevBox == null && nextBox == null -> getAlignedStartingPosition(isRtl) prevBox == null -> nextBox!!.cursorHorizontalPosition(true) nextBox == null -> prevBox.cursorHorizontalPosition() nextBox.direction == prevBox.direction -> nextBox.cursorHorizontalPosition(true) @@ -260,6 +263,19 @@ internal class SkiaParagraph( } } + private fun getAlignedStartingPosition(isRtl: Boolean) = when { + textAlign == TextAlign.Center -> width / 2 + isRtl && textAlign == TextAlign.Start -> width + isRtl && textAlign == TextAlign.Right -> width + isRtl && textAlign == TextAlign.Left -> 0f + isRtl && textAlign == TextAlign.End -> 0f + !isRtl && textAlign == TextAlign.Start -> 0f + !isRtl && textAlign == TextAlign.Right -> width + !isRtl && textAlign == TextAlign.Left -> 0f + !isRtl && textAlign == TextAlign.End -> width + else -> 0f + } + private var _lineMetrics: Array? = null private val lineMetrics: Array get() { From f93300039af521e110bc061cdd903ddafce6ac24 Mon Sep 17 00:00:00 2001 From: Vladimir Mazunin Date: Mon, 13 May 2024 11:12:55 +0400 Subject: [PATCH 2/8] removed textAlign as separate constant, rewritten `getAlignedStartingPosition` to make it more simple and intuitive --- .../compose/ui/text/SkiaParagraph.skiko.kt | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt index 5a104e7a595cd..535f9886e7722 100644 --- a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt +++ b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt @@ -58,8 +58,6 @@ internal class SkiaParagraph( internal val defaultFont get() = layouter.defaultFont - private val textAlign: TextAlign = layouter.textStyle.textAlign - /** * Paragraph isn't always immutable, it could be changed via [paint] method without * rerunning layout @@ -263,18 +261,15 @@ internal class SkiaParagraph( } } - private fun getAlignedStartingPosition(isRtl: Boolean) = when { - textAlign == TextAlign.Center -> width / 2 - isRtl && textAlign == TextAlign.Start -> width - isRtl && textAlign == TextAlign.Right -> width - isRtl && textAlign == TextAlign.Left -> 0f - isRtl && textAlign == TextAlign.End -> 0f - !isRtl && textAlign == TextAlign.Start -> 0f - !isRtl && textAlign == TextAlign.Right -> width - !isRtl && textAlign == TextAlign.Left -> 0f - !isRtl && textAlign == TextAlign.End -> width - else -> 0f - } + private fun getAlignedStartingPosition(isRtl: Boolean): Float = + when (layouter.textStyle.textAlign) { + TextAlign.Center -> width / 2 + TextAlign.Start -> if (isRtl) width else 0f + TextAlign.Right -> width + TextAlign.Left -> 0f + TextAlign.End -> if (isRtl) 0f else width + else -> 0f + } private var _lineMetrics: Array? = null private val lineMetrics: Array @@ -354,7 +349,7 @@ internal class SkiaParagraph( TextBox(rect, box.direction) } else { // TODO: Use unicode code points (CodePoint.charCount() instead of +1) - val nextBox = paragraph.getRectsForRange( + val nextBox = paragraph.getRectsForRange( offset, offset + 1, RectHeightMode.STRUT, RectWidthMode.TIGHT ).first() @@ -366,6 +361,7 @@ internal class SkiaParagraph( } } } + else -> return box } } @@ -405,7 +401,8 @@ internal class SkiaParagraph( // expectedLine is the line which lays at position.y val expectedLine = getLineMetricsForVerticalPosition(position.y) ?: return glyphPosition - val isNotEmptyLine = expectedLine.startIndex < expectedLine.endIndex // a line with only whitespaces considered to be not empty + val isNotEmptyLine = + expectedLine.startIndex < expectedLine.endIndex // a line with only whitespaces considered to be not empty // No need to apply the workaround if the clicked position is within the line bounds (but doesn't include whitespaces) if (position.x > expectedLine.left && position.x < expectedLine.right) { @@ -434,9 +431,11 @@ internal class SkiaParagraph( var correctedGlyphPosition = glyphPosition if (position.x <= leftX) { // when clicked to the left of a text line - correctedGlyphPosition = paragraph.getGlyphPositionAtCoordinate(leftX + 1f, position.y).position + correctedGlyphPosition = + paragraph.getGlyphPositionAtCoordinate(leftX + 1f, position.y).position } else if (position.x >= rightX) { // when clicked to the right of a text line - correctedGlyphPosition = paragraph.getGlyphPositionAtCoordinate(rightX - 1f, position.y).position + correctedGlyphPosition = + paragraph.getGlyphPositionAtCoordinate(rightX - 1f, position.y).position val isNeutralChar = if (correctedGlyphPosition in text.indices) { text.codePointAt(correctedGlyphPosition).isNeutralDirection() } else false @@ -460,8 +459,10 @@ internal class SkiaParagraph( array: FloatArray, arrayStart: Int ) { - println("Compose Multiplatform doesn't support fillBoundingBoxes` yet. " + - "Follow https://github.com/JetBrains/compose-multiplatform/issues/4236") + println( + "Compose Multiplatform doesn't support fillBoundingBoxes` yet. " + + "Follow https://github.com/JetBrains/compose-multiplatform/issues/4236" + ) // TODO(https://youtrack.jetbrains.com/issue/COMPOSE-720/Implement-Paragraph.fillBoundingBoxes) implement fillBoundingBoxes } @@ -637,8 +638,8 @@ private fun IRange.toTextRange() = TextRange(start, end) * Returns `null` if the array is empty. */ private inline fun Array.binarySearchFirstMatchingOrLast( - crossinline predicate: (T) -> Boolean): T? -{ + crossinline predicate: (T) -> Boolean +): T? { if (this.isEmpty()) { return null } From e09fe98557cde9be99dea114e8e922a9b27701aa Mon Sep 17 00:00:00 2001 From: Vladimir Mazunin Date: Mon, 10 Jun 2024 18:00:37 +0400 Subject: [PATCH 3/8] reverted autoformat changes --- .../compose/ui/text/SkiaParagraph.skiko.kt | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt index 535f9886e7722..ad22631b53081 100644 --- a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt +++ b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt @@ -349,7 +349,7 @@ internal class SkiaParagraph( TextBox(rect, box.direction) } else { // TODO: Use unicode code points (CodePoint.charCount() instead of +1) - val nextBox = paragraph.getRectsForRange( + val nextBox = paragraph.getRectsForRange( offset, offset + 1, RectHeightMode.STRUT, RectWidthMode.TIGHT ).first() @@ -361,7 +361,6 @@ internal class SkiaParagraph( } } } - else -> return box } } @@ -401,8 +400,7 @@ internal class SkiaParagraph( // expectedLine is the line which lays at position.y val expectedLine = getLineMetricsForVerticalPosition(position.y) ?: return glyphPosition - val isNotEmptyLine = - expectedLine.startIndex < expectedLine.endIndex // a line with only whitespaces considered to be not empty + val isNotEmptyLine = expectedLine.startIndex < expectedLine.endIndex // a line with only whitespaces considered to be not empty // No need to apply the workaround if the clicked position is within the line bounds (but doesn't include whitespaces) if (position.x > expectedLine.left && position.x < expectedLine.right) { @@ -431,11 +429,9 @@ internal class SkiaParagraph( var correctedGlyphPosition = glyphPosition if (position.x <= leftX) { // when clicked to the left of a text line - correctedGlyphPosition = - paragraph.getGlyphPositionAtCoordinate(leftX + 1f, position.y).position + correctedGlyphPosition = paragraph.getGlyphPositionAtCoordinate(leftX + 1f, position.y).position } else if (position.x >= rightX) { // when clicked to the right of a text line - correctedGlyphPosition = - paragraph.getGlyphPositionAtCoordinate(rightX - 1f, position.y).position + correctedGlyphPosition = paragraph.getGlyphPositionAtCoordinate(rightX - 1f, position.y).position val isNeutralChar = if (correctedGlyphPosition in text.indices) { text.codePointAt(correctedGlyphPosition).isNeutralDirection() } else false @@ -459,10 +455,8 @@ internal class SkiaParagraph( array: FloatArray, arrayStart: Int ) { - println( - "Compose Multiplatform doesn't support fillBoundingBoxes` yet. " + - "Follow https://github.com/JetBrains/compose-multiplatform/issues/4236" - ) + println("Compose Multiplatform doesn't support fillBoundingBoxes` yet. " + + "Follow https://github.com/JetBrains/compose-multiplatform/issues/4236") // TODO(https://youtrack.jetbrains.com/issue/COMPOSE-720/Implement-Paragraph.fillBoundingBoxes) implement fillBoundingBoxes } @@ -638,8 +632,7 @@ private fun IRange.toTextRange() = TextRange(start, end) * Returns `null` if the array is empty. */ private inline fun Array.binarySearchFirstMatchingOrLast( - crossinline predicate: (T) -> Boolean -): T? { + crossinline predicate: (T) -> Boolean): T? { if (this.isEmpty()) { return null } From 969c609fcc51045238f981d84cb12a3dde067960 Mon Sep 17 00:00:00 2001 From: Vladimir Mazunin Date: Mon, 10 Jun 2024 18:01:22 +0400 Subject: [PATCH 4/8] reverted autoformat changes x2 --- .../kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt index ad22631b53081..cf25e9498fd59 100644 --- a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt +++ b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt @@ -632,7 +632,8 @@ private fun IRange.toTextRange() = TextRange(start, end) * Returns `null` if the array is empty. */ private inline fun Array.binarySearchFirstMatchingOrLast( - crossinline predicate: (T) -> Boolean): T? { + crossinline predicate: (T) -> Boolean): T? +{ if (this.isEmpty()) { return null } From fbac6bfb788b056e8943e555e09015a1599c4745 Mon Sep 17 00:00:00 2001 From: Vladimir Mazunin Date: Mon, 10 Jun 2024 18:01:58 +0400 Subject: [PATCH 5/8] reverted autoformat changes x3 --- .../kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt index cf25e9498fd59..bdea09394c686 100644 --- a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt +++ b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt @@ -456,7 +456,7 @@ internal class SkiaParagraph( arrayStart: Int ) { println("Compose Multiplatform doesn't support fillBoundingBoxes` yet. " + - "Follow https://github.com/JetBrains/compose-multiplatform/issues/4236") + "Follow https://github.com/JetBrains/compose-multiplatform/issues/4236") // TODO(https://youtrack.jetbrains.com/issue/COMPOSE-720/Implement-Paragraph.fillBoundingBoxes) implement fillBoundingBoxes } From 9e5ed5045396988614f3beaffd8df3709233fcca Mon Sep 17 00:00:00 2001 From: Vladimir Mazunin Date: Mon, 10 Jun 2024 18:38:03 +0400 Subject: [PATCH 6/8] added tests for cursor positioning --- .../compose/ui/text/SkikoParagraphTest.kt | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/compose/ui/ui-text/src/skikoTest/kotlin/androidx/compose/ui/text/SkikoParagraphTest.kt b/compose/ui/ui-text/src/skikoTest/kotlin/androidx/compose/ui/text/SkikoParagraphTest.kt index d7750a99b93a4..85e9e762fb959 100644 --- a/compose/ui/ui-text/src/skikoTest/kotlin/androidx/compose/ui/text/SkikoParagraphTest.kt +++ b/compose/ui/ui-text/src/skikoTest/kotlin/androidx/compose/ui/text/SkikoParagraphTest.kt @@ -17,6 +17,8 @@ package androidx.compose.ui.text import androidx.compose.ui.text.font.createFontFamilyResolver +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDirection import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Density import kotlin.test.Ignore @@ -28,6 +30,8 @@ import kotlin.test.assertFailsWith class SkikoParagraphTest { private val fontFamilyResolver = createFontFamilyResolver() private val defaultDensity = Density(density = 1f) + private val minWidthConstraint = 0 + private val maxWidthConstraint = 1000 @Test fun getWordBoundary_out_of_boundary_too_small() { @@ -315,7 +319,8 @@ class SkikoParagraphTest { fun getWordBoundary_multichar() { // "ab 𐐔𐐯𐑅𐐨𐑉𐐯𐐻 cd" - example of multi-char code units // | (offset=3) | (offset=6) - val text = "ab \uD801\uDC14\uD801\uDC2F\uD801\uDC45\uD801\uDC28\uD801\uDC49\uD801\uDC2F\uD801\uDC3B cd" + val text = + "ab \uD801\uDC14\uD801\uDC2F\uD801\uDC45\uD801\uDC28\uD801\uDC49\uD801\uDC2F\uD801\uDC3B cd" val paragraph = simpleParagraph(text) assertEquals( @@ -324,10 +329,60 @@ class SkikoParagraphTest { ) } - private fun simpleParagraph(text: String) = Paragraph( + @Test + fun getHorizontalPosition_cursor_empty_textfield_ltr_start_alignment() { + val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.Start, textDirection = TextDirection.Ltr)) + val cursorHorizontalPosition: Float = paragraph.getHorizontalPosition(0, false) + assertEquals(minWidthConstraint.toFloat(), cursorHorizontalPosition) + } + + @Test + fun getHorizontalPosition_cursor_empty_textfield_ltr_end_alignment() { + val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.End, textDirection = TextDirection.Ltr)) + val cursorHorizontalPosition = paragraph.getHorizontalPosition(0, false) + assertEquals(maxWidthConstraint.toFloat(), cursorHorizontalPosition) + } + + @Test + fun getHorizontalPosition_cursor_empty_textfield_center_alignment() { + val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.Center)) + val cursorHorizontalPosition = paragraph.getHorizontalPosition(0, false) + assertEquals((maxWidthConstraint / 2).toFloat(), cursorHorizontalPosition) + } + + @Test + fun getHorizontalPosition_cursor_empty_textfield_rtl_start_alignment() { + val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.Start, textDirection = TextDirection.Rtl)) + val cursorHorizontalPosition: Float = paragraph.getHorizontalPosition(0, false) + assertEquals(maxWidthConstraint.toFloat(), cursorHorizontalPosition) + } + + @Test + fun getHorizontalPosition_cursor_empty_textfield_rtl_end_alignment() { + val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.End, textDirection = TextDirection.Rtl)) + val cursorHorizontalPosition = paragraph.getHorizontalPosition(0, false) + assertEquals(minWidthConstraint.toFloat(), cursorHorizontalPosition) + } + + @Test + fun getHorizontalPosition_cursor_empty_textfield_left_alignment() { + val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.Start)) + val cursorHorizontalPosition: Float = paragraph.getHorizontalPosition(0, false) + assertEquals(minWidthConstraint.toFloat(), cursorHorizontalPosition) + } + + @Test + fun getHorizontalPosition_cursor_empty_textfield_right_alignment() { + val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.End)) + val cursorHorizontalPosition = paragraph.getHorizontalPosition(0, false) + assertEquals(maxWidthConstraint.toFloat(), cursorHorizontalPosition) + } + + + private fun simpleParagraph(text: String, textStyle: TextStyle = TextStyle()) = Paragraph( text = text, - style = TextStyle(), - constraints = Constraints(maxWidth = 1000), + style = textStyle, + constraints = Constraints(minWidth = minWidthConstraint, maxWidth = maxWidthConstraint), density = defaultDensity, fontFamilyResolver = fontFamilyResolver ) From 475cded60f163a008e1b23ca89e0ddba1aad0f02 Mon Sep 17 00:00:00 2001 From: Vladimir Mazunin Date: Mon, 10 Jun 2024 19:08:40 +0400 Subject: [PATCH 7/8] removed minWidthConstraint, changed it for explicit 0f in related tests --- .../androidx/compose/ui/text/SkikoParagraphTest.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compose/ui/ui-text/src/skikoTest/kotlin/androidx/compose/ui/text/SkikoParagraphTest.kt b/compose/ui/ui-text/src/skikoTest/kotlin/androidx/compose/ui/text/SkikoParagraphTest.kt index 85e9e762fb959..ee0c0e97db561 100644 --- a/compose/ui/ui-text/src/skikoTest/kotlin/androidx/compose/ui/text/SkikoParagraphTest.kt +++ b/compose/ui/ui-text/src/skikoTest/kotlin/androidx/compose/ui/text/SkikoParagraphTest.kt @@ -30,7 +30,6 @@ import kotlin.test.assertFailsWith class SkikoParagraphTest { private val fontFamilyResolver = createFontFamilyResolver() private val defaultDensity = Density(density = 1f) - private val minWidthConstraint = 0 private val maxWidthConstraint = 1000 @Test @@ -333,7 +332,7 @@ class SkikoParagraphTest { fun getHorizontalPosition_cursor_empty_textfield_ltr_start_alignment() { val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.Start, textDirection = TextDirection.Ltr)) val cursorHorizontalPosition: Float = paragraph.getHorizontalPosition(0, false) - assertEquals(minWidthConstraint.toFloat(), cursorHorizontalPosition) + assertEquals(0f, cursorHorizontalPosition) } @Test @@ -361,14 +360,14 @@ class SkikoParagraphTest { fun getHorizontalPosition_cursor_empty_textfield_rtl_end_alignment() { val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.End, textDirection = TextDirection.Rtl)) val cursorHorizontalPosition = paragraph.getHorizontalPosition(0, false) - assertEquals(minWidthConstraint.toFloat(), cursorHorizontalPosition) + assertEquals(0f, cursorHorizontalPosition) } @Test fun getHorizontalPosition_cursor_empty_textfield_left_alignment() { val paragraph = simpleParagraph("", TextStyle(textAlign = TextAlign.Start)) val cursorHorizontalPosition: Float = paragraph.getHorizontalPosition(0, false) - assertEquals(minWidthConstraint.toFloat(), cursorHorizontalPosition) + assertEquals(0f, cursorHorizontalPosition) } @Test @@ -382,7 +381,7 @@ class SkikoParagraphTest { private fun simpleParagraph(text: String, textStyle: TextStyle = TextStyle()) = Paragraph( text = text, style = textStyle, - constraints = Constraints(minWidth = minWidthConstraint, maxWidth = maxWidthConstraint), + constraints = Constraints(maxWidth = maxWidthConstraint), density = defaultDensity, fontFamilyResolver = fontFamilyResolver ) From c0a403c572dfbb1c62941aee6bb0d07f058eb6d5 Mon Sep 17 00:00:00 2001 From: Vladimir Mazunin Date: Tue, 11 Jun 2024 13:01:52 +0400 Subject: [PATCH 8/8] reordering enum values Co-authored-by: Ivan Matkov --- .../kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt index bdea09394c686..3d65a262e3817 100644 --- a/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt +++ b/compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt @@ -263,10 +263,10 @@ internal class SkiaParagraph( private fun getAlignedStartingPosition(isRtl: Boolean): Float = when (layouter.textStyle.textAlign) { + TextAlign.Left -> 0f + TextAlign.Right -> width TextAlign.Center -> width / 2 TextAlign.Start -> if (isRtl) width else 0f - TextAlign.Right -> width - TextAlign.Left -> 0f TextAlign.End -> if (isRtl) 0f else width else -> 0f }