JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
16.12k stars 1.17k forks source link

[Web] Text overflow with TextOverflow.Clip becomes blank or shows garbage content #3224

Closed mdigman closed 3 weeks ago

mdigman commented 1 year ago

Describe the bug Text(... overflow = TextOverflow.Clip) becomes blank or shows garbage content when clipped on web

Affected platforms Select one of the platforms below:

Versions

To Reproduce Run the MultipleTextOverflowTester composable in the following code and observe something similar to the screenshots posted below.

@Composable
fun MultipleTextOverflowTester() {
    Row {
        TextOverflowTesterWithBackground(50.dp, showBackground = true)
        TextOverflowTesterWithBackground(100.dp, showBackground = true)
        TextOverflowTesterWithBackground(150.dp, showBackground = true)
    }
}

@Composable
fun TextOverflowTesterWithBackground(width: Dp, showBackground: Boolean) {
    Row {
        TextNoClipTest(width, if(showBackground) Color.Gray else null)
        TextSingleLineClipTest(width, if(showBackground) Color.Blue else null)
        TextSingleLineEllipsisTest(width, if (showBackground) Color.Red else null)
    }
}

@Composable
fun TextSingleLineClipTest(
    width: Dp = 100.dp,
    background: Color? = null
) {
    Text(
        modifier = Modifier.width(width).let { if(background != null) it.background(color = background) else it },
        color = Color.Black,
        text = "This is a very long line of text that should be clipped",
        maxLines = 1,
        overflow = TextOverflow.Clip
    )
}

@Composable
fun TextSingleLineEllipsisTest(
    width: Dp = 100.dp,
    background: Color? = null
) {
    Text(
        modifier = Modifier.width(width).let { if(background != null) it.background(color = background) else it },
        color = Color.Black,
        text = "This is a very long line of text that should end in an ellipsis",
        maxLines = 1,
        overflow = TextOverflow.Ellipsis
    )
}

@Composable
fun TextNoClipTest(
    width: Dp = 100.dp,
    background: Color? = null
) {
    Text(
        modifier = Modifier.width(width).let { if(background != null) it.background(color = background) else it },
        color = Color.Black,
        text = "This is a very long line of text that should not be clipped",
    )
}

Expected behavior Text with overflow should render as expected given the overflow setting as it does on Android and Desktop.

Screenshots Various behaviors side-by-side at various fixed widths using code from the repro section. Gray has no clipping, Blue is TextOverflow.Clip and Red is TextOverflow.Ellipsis both clipped by setting 1 line and fixed width:

Screenshot 2023-05-30 at 11 37 54 AM

Rendering only one the smallest of the three widths, TextOverflip.Clip seems to generate garbage text:

Screenshot 2023-05-30 at 11 37 26 AM

Additional context To temporarily solve this problem, I attempted to wrap Text with a box and use TextOverflow.Visible. This did not work consistently, usually rendering blank. This makes me think the underlying issue is related to text measurement? Code to repro:

@Composable
fun TextSingleLineWrappedClipTest(
    width: Dp = 100.dp,
    background: Color? = null
) {
    Box(Modifier.clipToBounds()) {
        Text(
            modifier = Modifier.width(width).let { if(background != null) it.background(color = background) else it },
            color = Color.Black,
            text = "This is a very long line of text that should be clipped by a wrapping container",
            maxLines = 1,
            overflow = TextOverflow.Visible
        )
    }
}
okushnikov commented 1 month ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.