JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
15.26k stars 1.11k forks source link

Clicking TextField should consume pointer press event #5029

Open mgroth0 opened 3 days ago

mgroth0 commented 3 days ago

Describe the bug

Clicking a TextField should consume the press event (in the Main pass). But instead, it is not consumed and propagates to the parent element.

Affected platforms

Versions

To Reproduce Steps to reproduce the behavior:

  1. Run this code snippet:
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.material3.TextField
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.awtEventOrNull
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application

@OptIn(ExperimentalComposeUiApi::class)
fun main() {
    val text = mutableStateOf("some text")
    var nextPointerEvent = 0
    application {
        Window(
            onCloseRequest = ::exitApplication
        ) {
            Box(
                Modifier
                    .size(400.dp)
                    .background(Color.Yellow)
                    .onPointerEvent(PointerEventType.Press) {
                        val consumed = it.awtEventOrNull!!.isConsumed
                        text.value = "Got pointer event ${nextPointerEvent++} (consumed=$consumed)"
                    }
            ) {
                TextField(
                    value = text.value,
                    onValueChange = {
                        text.value = it
                    }
                )
            }
        }
    }
}
  1. Click on the text field
  2. See that the pointer event progated to the parent

Expected behavior

The event handler registered to the container should not receive the click event.

If the pass was PointerEventPass.Initial, then the click event should be received. However, the default value for pass is PointerEventPass.Main. Since the TextField behaves as expected, it seems that the TextField does handle the mouse press event. However, it then fails to consume it and prevent it from propagating further up the tree.

Additional context

I have a mouse click handler on the TextField container in my app. When it receives a click, it uses focusRequester.requestFocus() on its own component. But when the click was actually in the TextField (inside the container), this unexpectedly messes with the focus in the TextField.

m-sasha commented 2 days ago

Does Android consume taps on TextField?