kotlinandroid-jetpack-composecompose-desktop

Compose Desktop draw Text out of screen using Canvas


I want to draw a text using Canvas. here is my code:

val firstOffset = Offset(x = 0F, y = 0F)
val secondOffset = Offset(x = 0F, y = 1000F)
val thirdOffset = Offset(x = 0F, y = 1080F)
Canvas(
        modifier = Modifier.width(priceBarItemWidth)
            .fillMaxHeight()
            .pointerHoverIcon(
                PointerIcon(Cursor(N_RESIZE_CURSOR))
            )
    ) {
        drawText(
            textMeasurer = textMeasurer,
            text = "qwerty",
            topLeft = firstOffset,
            style = style
        )
    }

when I use firstOffset text goes to top left of screen when I use secondOffset text goes to bottom left of screen and when I use thirdOffset i get this error:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: 
maxHeight(-30) must be >= than minHeight(0)

I want to draw this text out of screen (I want to develop a scrollable list using Canvas and I know there is better ways to do this) so the question is how can I draw texts out of screen to bring scroll function to my custom list!

and the original code is below:

val containerSize by service.containerSize.collectAsState()
Canvas(
        modifier = Modifier.width(priceBarItemWidth)
            .fillMaxHeight()
            .pointerHoverIcon(
                PointerIcon(Cursor(N_RESIZE_CURSOR))
            )
    ) {
        if (containerSize.height == 0) return@Canvas
        var currentHeight = 25 * (priceBarItemHeightInPx + 
            priceBarSpaceBetweenItemInPx)
        prices.forEachIndexed { index, fl ->
            drawText(
                textMeasurer = textMeasurer,
                text = fl.toString(),
                topLeft = Offset(x = 0F, y = 0F),
                style = style
            )
            currentHeight -= (priceBarItemHeightInPx + priceBarSpaceBetweenItemInPx)
        }
    }

Solution

  • This happens due to drawText function passing Constraints with negative height in calculation here.

    https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt;drc=4d53400eca9f3ac90c3a3f6cffcbc5bf492ec536;l=390

    Passing topLeft with bigger than text height, when you don't set size param of drawText, returns negative maxHeight which is not allowed. Passing smaller maxHeight than minHeight also not allowed for Constraints as well.

    You can set size param of drawText function or you can use translate function of DrawScope as

    @Preview
    @Composable
    private fun CanvasTextSample() {
    
        val list = remember {
            listOf(
                "Hello",
                "World",
                "Text1",
                "Text2",
                "Text3",
                "Text4",
            )
        }
    
        Column(
            modifier = Modifier.padding(16.dp).fillMaxSize().padding(top = 60.dp)
        ) {
    
            val textMeasurer = rememberTextMeasurer()
    
            var offset by remember {
                mutableStateOf(0f)
            }
    
            Canvas(
                modifier = Modifier
                    .pointerInput(Unit) {
                        detectDragGestures { change, dragAmount ->
                            offset += dragAmount.y
                        }
                    }
                    .fillMaxWidth()
                    .aspectRatio(1f)
                    .border(1.dp, Color.Red)
            ) {
    
                list.fastForEachIndexed { index, text ->
                    translate(
                        left = 100f,
                        top = offset + index * 20.dp.toPx()
                    ) {
                        drawText(
                            textMeasurer = textMeasurer,
                            text = text,
                        )
                    }
                }
            }
        }
    }
    

    If you wish to center Text horizontally inside Canvas use textMeasurer.measure() to get TextLayoutResult and center Text using size returned from it and Canvas size.