I'd like to implement a simple drawing app with Compose for Desktop and the basics seem to be easy:
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
const val ACTION_IDLE = 0
const val ACTION_DOWN = 1
const val ACTION_MOVE = 2
const val ACTION_UP = 3
fun main() = application {
Window(
onCloseRequest = ::exitApplication,
title = "Compose for Desktop",
state = rememberWindowState(width = 500.dp, height = 500.dp)
) {
MaterialTheme {
var motionEvent by remember { mutableStateOf(ACTION_IDLE) }
var currentPosition by remember { mutableStateOf(Offset.Unspecified) }
Canvas(
modifier = Modifier.fillMaxSize()
.background(Color.LightGray)
.pointerInput(Unit) {
forEachGesture {
awaitPointerEventScope {
awaitFirstDown().also {
motionEvent = ACTION_DOWN
currentPosition = it.position
}
}
}
}
) {
fun draw() =
drawCircle(
color = Color.Magenta,
center = Offset(x = currentPosition.x, y = currentPosition.y),
radius = size.minDimension / 4,
style = Stroke(width = 4.dp.toPx(), cap = StrokeCap.Round, join = StrokeJoin.Round)
)
when (motionEvent) {
ACTION_DOWN -> draw()
}
}
}
}
}
The app, however, does not behave the way I'd anticipate: it draws a new circle dismissing the previous one every time, e.g. as if the canvas doesn't preserve state and reset on every new input. I'd like to keep all the figures though. Is there anything I'm missing to achieve that?