I'm working on building a piano keyboard UI in Jetpack Compose. I need to detect which piano keys are being pressed, but I can't seem to find a way to implement this logic in Jetpack Compose. The problem is that each piano key needs to register as pressed if any touch inputs are currently within the bounds of the piano key, even if they originated somewhere else on the keyboard but were dragged onto the piano key. Also, if a touch pointer is dragged off of the piano key, the key should be released. However, the built-in tools in Jetpack Compose, like the clickable modifier or detectTapGestures/detectDragGestures scopes don't seem to be suited to handle this problem.
The closest I got to solving the issue was by using the pointerInput modifier and awaitPointerEventScope on the entire piano keyboard, which detects pointer events anywhere within the keyboard composable. Then, I set each key as released, then manually calculated which key each touch pointer is on, and set those keys as pressed. However, the problem is that PointerEvents only give you information about the pointers that changed, but I really need a list of each active touch pointer within the keyboard composable. Also, it seems to me that there should be a better way than manually calculating which key a pointer is on.
Here's the code for the composable I'm using to process touch events. I only added logic for events of type PointerEventType.Move, and I don't think there is a way to handle the other types of PointerEvents without releasing keys that shouldn't be released.
Box(
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent()
pianoState.clear() // sets each key to off
if (event.type == PointerEventType.Move) {
event.changes.forEach {
// calculates which key was pressed
val pressedKey = keyboard.whichKeyPressed(it.position)
// set the key to on
if (pressedKey != null)
pianoState.keys[pressedKey].value = 127
}
}
}
}
}
)