Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix initial cursor position in empty TextField when TextAlignment is set explicitly #1354

Merged
merged 8 commits into from
Jun 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -247,7 +248,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)
Expand All @@ -260,6 +261,16 @@ internal class SkiaParagraph(
}
}

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
mazunin-v-jb marked this conversation as resolved.
Show resolved Hide resolved
else -> 0f
}

private var _lineMetrics: Array<LineMetrics>? = null
private val lineMetrics: Array<LineMetrics>
get() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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() {
Expand Down Expand Up @@ -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(
Expand All @@ -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)
MatkovIvan marked this conversation as resolved.
Show resolved Hide resolved
}

@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
)
Expand Down
Loading