Search code examples
android-jetpack-composeskiacompose-desktop

Draw Text within DrawScope - Jetpack Compose Desktop


I want to draw Text inside a canvas to display a label for a chart.

On Android, I can use the library: https://github.com/tehras/charts (For Compose: 1-alpha03) but on Desktop I can't.

Thus I tried to rewrite the broken parts. But I can't get the labels to display.

Original Code, which I try to change to work with Desktop Source:

    private val textPaint = android.graphics.Paint().apply {
        isAntiAlias = true
        color = labelTextColor.toLegacyInt()
    }
    private val textBounds = android.graphics.Rect()

    override fun drawAxisLabels(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect,
        minValue: Float,
        maxValue: Float
    ) = with(drawScope) {
        val labelPaint = textPaint.apply {
            textSize = labelTextSize.toPx()
            textAlign = android.graphics.Paint.Align.RIGHT
        }
        val minLabelHeight = (labelTextSize.toPx() * labelRatio.toFloat())
        val totalHeight = drawableArea.height
        val labelCount = max((drawableArea.height / minLabelHeight).roundToInt(), 2)

        for (i in 0..labelCount) {
            val value = minValue + (i * ((maxValue - minValue) / labelCount))

            val label = labelValueFormatter(value)
            val x = drawableArea.right - axisLineThickness.toPx() - (labelTextSize.toPx() / 2f)

            labelPaint.getTextBounds(label, 0, label.length, textBounds)

            val y = drawableArea.bottom - (i * (totalHeight / labelCount)) + (textBounds.height() / 2f)

            canvas.nativeCanvas.drawText(label, x, y, labelPaint)
        }
    }

For me, the function at the end NativeCanvas::drawText does not exist on Compose Desktop. I tried to replace all the logic with a TextPainter but nothing was painted.

What could I try to make it work ? or
Are there dependencies I could import from Android to just make it work?

COMPOSE_DESKTOP_VERSION: "0.3.0-build138"
KOTLIN_VERSION: "1.4.21"


Solution

  • The Desktop Compose native canvas should have a couple of methods related to drawing text. Instead of canvas.nativeCanvas.drawText, you could try calling canvas.nativeCanvas.drawString(label, x, y, org.jetbrains.skija.Font(), labelPaint).

    I got this using version 0.4.0-build177 of Desktop Compose, so YMMV on different versions.

    This also comes with a few caveats:

    • If you want to style the text (other than color), you'll have to construct your own Font instance. The empty constructor seems to use some system defaults. I'm still learning the API myself, so I would suggest reading through the documentation (which at the moment is just the code, I think).
    • You might also need to change how you create the Paint since (at least on the version I was using) doesn't support textSize on the Paint - I had to specify this as part of the Font creation.
    • It seems to assume left alignment. A way that I found to get it to work was to use TextLine with the canvas.nativeCanvas.drawTextLine() function:
    val textLine = TextLine.make(yourTextString, yourFont)
    val xOffset = when (yourTextAlign) {
        // There are probably better ways to handle Start, End, and Justify...
        TextAlign.Left, TextAlign.Start -> 0f
        TextAlign.Right, TextAlign.End -> textLine.width
        TextAlign.Center, TextAlign.Justify -> textLine.width / 2f
    }
    val actualX = x - xOffset
    
    canvas.nativeCanvas.drawTextLine(textLine, actualX, y, yourPaint)