https://kotlinlang.org logo
#compose
Title
# compose
c

Chris Johnson

12/08/2021, 8:48 PM
Hello! Is there a known way to chain detecting touch events? For example I'd like to
detectTransformGestures
and also
detectTapGestures
with double tap.
Well I feel silly. Solved it 2 minutes after asking but... just need to chain different
PointerInput
modifiers. Hopefully this helps someone else.
m

Michael Paus

12/09/2021, 8:14 AM
This works in general but some combinations of gestures seem to be mutually exclusive. E.g., if I remember it correctly you cannot use`detectDragGestures` and `detectTransformGestures`at the same time.
m

matvei

12/09/2021, 11:16 AM
That's a good feedback. I filed https://issuetracker.google.com/issues/209937353 on us to clarify this behaviour in the docs
👍 1
m

Michael Paus

12/09/2021, 3:41 PM
@matvei It also makes a difference if you add the detect*** calls in a single pointerInput or if you add two of them with a single detect*** call each. It was difficult too to detect the end of a drag gesture if you can’t use detectDragGestures together with detectTransformGestures.
m

matvei

12/09/2021, 5:29 PM
If you are able to put two detect*Gestures in the same
Modifier.pointerInput
and it works - it is by accident, we never intended it to. Each detectDragGestures should be used in a separate modifier, as it represents the complete state-machine, working "forever" with
forEachGesture
as a top-level call. I'd be curious to know more about the problems you are facing wiring together drag and transform gestures
m

Michael Paus

12/09/2021, 6:35 PM
1. Putting more than one
detect*Gesture
into the same
Modifier.pointerInput
works technically, meaning there are no warnings or errors. Just the behaviour is not what you might expect. If such a use is not intended, then this should be clearly documented or maybe even technically prevented. I couldn’t find anything like that. I learned it the hard way that you have to use multiple
Modifier.pointerInput
calls. 2. My use case is handling gestures on a Canvas. I’d like to detect all gestures and apply a corresponding action depending on the gesture and the location where it was applied but I learned that you cannot use
detectDragGestures
 and `detectTransformGestures`at the same time. I’d prefer the dragGesture because I then can also get information about the end of the gesture but then I cannot detect a rotation for example at the same time. So I am using detectTransformGestures now and derive the end of the gesture via listening to
detectTabGesture.onPress
. If you try to detect both of them at the same time, one of them is just ignored and will never fire. (If I remember it correctly the last one in the chain will win but I am not sure and I currently can’t try it out again)
a

Albert Chang

01/04/2022, 5:09 PM
@matvei Sorry to ping you but I’m confused because it is documented here that it’s ok to
launch
multiple
awaitPointerEventScope
in one
PointerInputScope
, which seems to disagree with what you said. Do you mean it’s incorrect to
launch
multiple
forEachGesture
in one
PointerInputScope
? If that’s the case, when we are writing custom gesture detector, is it correct to write like this?
Copy code
Modifier.pointerInput(Unit) {
    forEachGesture {
        coroutineScope {
            launch {
                awaitPointerEventScope {
                    // Detect gesture 1
                }
            }
            launch {
                awaitPointerEventScope {
                    // Detect gesture 2
                }
            }
        }
    }
}
m

matvei

01/10/2022, 2:16 PM
What I was stating is that it is impossible to make more than 1
detect***
calls work within one
Modifier.pointerInput
, as they represent the complete state-machine with their own consumption logic, etc. You can have more than one
awaitPointerEventScope
in one modifier, just keep in mind that if they are not parallel, but conditional or one-by-one - you can lose some events when leaving one and entering another one (as there will be no scopes listening for gesture events).
forEachGesture
is just an utility function that ensures that we run forever and wait for "all pointers UP" after we reach the end of each
forEachGesutre
cycle. Other than that, there's no restriction applied and you can do what you want inside - run parallel detection if that makes sense for your logic or any other shenanigans
s

Stylianos Gakis

01/10/2022, 3:47 PM
Hmm reading this makes me question some things. So far in a composable where I had to detect click event and drag events I was implementing it like this:
Copy code
.pointerInput(Unit) {
    coroutineScope { 
        launch { detectTapGestures { "tap handling here" } }
        launch { detectHorizontalDragGestures { _, _ -> "drag handling here" }
    }
}
And it has been working fine afaik. This thread makes it sound like I’d actually need to instead do this:
Copy code
.pointerInput(Unit) {
    detectTapGestures { "tap handling here" }
}
.pointerInput(Unit) {
    detectHorizontalDragGestures { _, _ -> "drag handling here" }   
}
But this is not what I am getting from reading the documentation and definitely not something I would think of doing if I hadn’t seen this thread. Is there some part of documentation that I missed that talks about when to provide several
pointerInput
modifiers (and how they might potentially interfere with each other? probably doesn’t happen?) instead of doing something like this?
Copy code
pointerInput(Unit) {
  coroutineScope {
    launch { //some handling }
    launch { //some more handling }
  }
}
m

matvei

01/10/2022, 6:12 PM
I see, I think I used to work "impossible" where it fact this is possible, but I would proceed with caution because they were not designed to be used or mixed together. It can cause issues with ordering, consumption, or lifetime. For example, I'm pretty sure you want to pass some keys in the
Modifier.pointerInput(keys)
in any of the cases mentioned. If the key changes that is related to "tap" gesutures, does it make sense to interrupt and restart a "drag" gesture (because we can only restart the whole pointerInput modifier scope)? If you are building a one complicated gesture detector (and not just mixing a few to add some bits of functionality) and whose combination works - it makes sense to leave it as they work 🙂
s

Stylianos Gakis

01/10/2022, 7:19 PM
Your point about the keys makes sense yeah. It’s just that in my case all they did was both call the same callback (both on click and on drag) which I am keeping updated with a
rememberUpdatedState
which is why I had no use of the keys which then made me not think of this potential problem. I am guessing in the case that I am working on it’s still “more correct” to do two separate `pointerInput`s but I’m just lucky that it happens to work with the way that I am doing it. But aside from the keys point, if having two separate
detect***
functions, even in separate two
launch{}
within the same
pointerInput
modifier is technically wrong and can lead to unexpected behavior, it should be pointed out in the documentation no?
m

matvei

01/11/2022, 12:06 PM
it should be pointed out in the documentation no?
Agree, worth filing a bug for it
s

Stylianos Gakis

01/11/2022, 12:20 PM
Right, just saw this. Until then we can point people with similar issues to this discussion.
m

matvei

01/11/2022, 12:29 PM
I knew I already filed this bug but didn't manage to find this quickly 🙂 Thanks!
7 Views