7.0 KiB
Getting Started with Compose for Desktop
What is covered
In this tutorial we will see how to install mouse event listeners on components in Compose for Desktop.
Mouse event listeners
Click listeners
Click listeners are available in both Compose on Android and Compose for Desktop, so code like this will work on both platforms:
import androidx.compose.desktop.Window
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.sp
fun main() = Window(title = "Compose for Desktop", size = IntSize(400, 400)) {
var count = remember { mutableStateOf(0) }
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) {
var text = remember { mutableStateOf("Click magenta box!") }
Column {
@OptIn(ExperimentalFoundationApi::class)
Box(
modifier = Modifier
.background(Color.Magenta)
.fillMaxWidth(0.7f)
.fillMaxHeight(0.2f)
.combinedClickable(
onClick = {
text.value = "Click! ${count.value++}"
},
onDoubleClick = {
text.value = "Double click! ${count.value++}"
},
onLongClick = {
text.value = "Long click! ${count.value++}"
}
)
)
Text(text = text.value, fontSize = 40.sp)
}
}
}
Mouse move listeners
As typically mouse and other positional pointers are only available on desktop platforms, the following code will only work with Compose for Desktop. Let's create a window and install a pointer move filter on it that changes the background color according to the mouse pointer position:
import androidx.compose.desktop.Window
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerMoveFilter
import androidx.compose.ui.unit.IntSize
fun main() = Window(title = "Compose for Desktop", size = IntSize(400, 400)) {
var color = remember { mutableStateOf(Color(0, 0, 0)) }
Box(
modifier = Modifier
.wrapContentSize(Alignment.Center)
.fillMaxSize()
.background(color = color.value)
.pointerMoveFilter(
onMove = {
color.value = Color(it.x.toInt() % 256, it.y.toInt() % 256, 0)
false
}
)
)
}
Mouse enter listeners
Compose for Desktop also supports pointer enter and exit handlers, like this:
import androidx.compose.desktop.Window
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerMoveFilter
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
fun main() = Window(title = "Compose for Desktop", size = IntSize(400, 400)) {
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
repeat(10) { index ->
var active = remember { mutableStateOf(false) }
Text(
modifier = Modifier
.fillMaxWidth()
.background(color = if (active.value) Color.Green else Color.White)
.pointerMoveFilter(
onEnter = {
active.value = true
false
},
onExit = {
active.value = false
false
}
),
fontSize = 30.sp,
fontStyle = if (active.value) FontStyle.Italic else FontStyle.Normal,
text = "Item $index"
)
}
}
}
Mouse right/middle clicks and keyboard modifiers
While first-class support for pointer type-specific data, like pressed mouse buttons, is still in development in Compose, there is an available raw AWT mouse event object in Compose for Desktop, that can be used as a workaround when you need advanced functionality.
import androidx.compose.desktop.Window
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Text
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.input.pointer.changedToDown
import androidx.compose.ui.input.pointer.consumeDownChange
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.IntSize
import java.awt.event.MouseEvent
fun main() = Window(title = "Compose for Desktop", size = IntSize(400, 400)) {
var lastEvent by remember { mutableStateOf<MouseEvent?>(null) }
Column {
Text(
text = "Custom button",
modifier = Modifier.pointerInput(Unit) {
forEachGesture {
awaitPointerEventScope {
lastEvent = awaitEventFirstDown().also {
it.changes.forEach { it.consumeDownChange() }
}.mouseEvent
}
}
}
)
Text("Mouse event: ${lastEvent?.paramString()}")
}
}
private suspend fun AwaitPointerEventScope.awaitEventFirstDown(): PointerEvent {
var event: PointerEvent
do {
event = awaitPointerEvent()
} while (
!event.changes.all { it.changedToDown() }
)
return event
}