[Interest] QML Pointer Handlers: how to use 'GrabPermissions'

Alexander Ivash elderorb at gmail.com
Tue Sep 18 14:36:23 CEST 2018


Hi Shawn,

I'm still curious about best practices on implementing use case I
mentioned previously: To refresh memory I need a 'canvas' with some
'items' inside. Simplified version looks like this:

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    ScrollView {
        anchors.fill: parent

        Flickable {
            Rectangle {
                anchors.fill: parent
                color: 'yellow'

                TapHandler {
                    onTapped: {
                        console.debug('item deselected');
                    }
                }
            }

            Rectangle {
                width: 50
                height: 50
                color: 'green'

                TapHandler {
                    onTapped: {
                        console.debug('item selected');
                    }
                }
            }
        }
    }
}

Tapping canvas should result in item de-selection, tapping item should
result in item selection. So I need item's tap handler to do not
propagate tap (although maybe wouldn't mind propagating long presses).
I've played with gesturePolicy / grabPermissions but maybe 5.11 is a
bit buggy or I don't understand something still, but every attempt
resulted in weird side-effects on android (in reality everything is
bit more complicated: zoomable canvas, draggable/zoomable items etc.).
So for now I'm just disabling canvas tap handler inside item's tap
handler and re-enabling it using Qt.callLater - it is ugly but at
least doesn't break other input handlers.

So... you recommendations? Or maybe there is some example showing some
advanced input handlers technics? If yes, please let me know where can
I check it. Taking into account 5.12 is going to finalize input
handlers and provide for mass adoption, I feel like it might result a
lot of questions like mine which could be eliminated by good example.

Regards, Alexander
вт, 11 сент. 2018 г. в 20:08, Alexander Ivash <elderorb at gmail.com>:
>
> Hi Shawn,
>
> First of all big thank you for such a detailed reply (I would never
> write something like this if wouldn't be forced :))!
>
> As for the namespace for enum - yes, I've already realized that was
> using it incorrectly (noticed some PR in gerrit during google-ing),
> just haven't play with flags yet because still feel like there might
> be better way.
>
> What I'm going to achieve - is capture clicks on item OR on background
> in the following sample:
>
> import QtQuick 2.9
> import QtQuick.Controls 2.2
> import Qt.labs.handlers 1.0
>
>
> ApplicationWindow {
>     visible: true
>     width: 640
>     height: 480
>     title: qsTr("Scroll")
>
>     ScrollView {
>         anchors.fill: parent
>
>         Flickable {
>             Rectangle {
>                 anchors.fill: parent
>                 color: 'yellow'
>
>                 TapHandler {
>                     onTapped: {
>                         console.debug('background tapped');
>                     }
>                 }
>             }
>
>             Rectangle {
>                 width: 50
>                 height: 50
>                 color: 'green'
>
>                 TapHandler {
>                     onTapped: {
>                         console.debug('item tapped');
>                     }
>                 }
>             }
>         }
>     }
> }
>
> The issue is that currently I capture both due to events propagation:
>
> qml: item tapped
> qml: background tapped
>
> While I really like flexibility which input handlers provide (again
> big thanks for it!) there might be still some cases (like mine) where
> simple way to enable exclusive behavior might be beneficial.
> Of course one can apply some kind of ugly workaround current behaviour
> by for example disabling backround tap handler from item's one and
> re-enabling it using Qt.callLater() but I'm wondering what it the
> right way to achieve desired behavior?
>
> It is achievable at all without grabPermissions/gestureBehaviour at
> all? If it doesn't then maybe a few words in docs covering 'exclusive'
> scenario could be very helpful.
>
> Also (a bit not related to the current issue) I noticed weird
> behaviour of PinchHandler while was trying to implement 'zoomable
> scrollview' so had to revert back to PinchArea. Hope this is just a
> bug which is going to be fixed in 5.12.
>
> Other than that, I really like new way of handling events!
> пн, 10 сент. 2018 г. в 19:02, Shawn Rutledge <Shawn.Rutledge at qt.io>:
> >
> >
> > > On 2 Sep 2018, at 18:52, Alexander Ivash <elderorb at gmail.com> wrote:
> > >
> > > Is there any example of usage 'GrabPermissions' from QML ?
> >
> > So far we don’t have many examples with pointer handlers in general (we need to work on that for 5.12 and onwards) but the manual tests cover some of the initial obvious use cases:  qtdeclarative/tests/manual/pointer
> >
> > > I've tried to do this:
> > >
> > > TapHandler {
> > > grabPermissions: GrabPermissions.CanTakeOverFromHandlersOfSameType |
> > > GrabPermissions.CanTakeOverFromItems |
> > > GrabPermissions.CanTakeOverFromHandlersOfDifferentType |
> > > GrabPermissions.ApprovesTakeOverByAnything
> > > onTapped: {
> > > console.debug('text tap handler')
> > > }
> > > }
> > >
> > > but it resulted in syntax errors.
> >
> > The enum is nested in the PointerHandler base class, so at the moment you can use whatever scoping is convenient, the base or any subclass:
> >
> > grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | TapHandler.CanTakeOverFromItems | PinchHandler.CanTakeOverFromHandlersOfDifferentType | PointHandler.ApprovesTakeOverByAnything
> >
> > Yeah that’s silly (in practice you’d probably use either PointerHandler or <Leaf>Handler scoping consistently).  Does anybody think we could or should try to lock it down further somehow?  (Omitting the scoping altogether might be nice, wouldn’t it?  But we probably can’t.)
> >
> > > Btw, documentation seems to be broken - it shows 'grabPermissions' as boolean property
> >
> > Yes that’s wrong.  https://codereview.qt-project.org/#/c/239399/
> >
> > > Also, do I understand correctly that grabPermissions is the only way to make one TapHandler to consume events so that other TapHandler will not get any?
> >
> > I was hoping that conflicts between handlers will be rare, because you have more declarative control over the geometry within which a handler handles events, which device type the event can come from, which buttons are allowed, modifiers etc., and therefore the need to use grabPermissions should also be rare.  But of course we can’t imagine all the use cases either.  So how did you get into the situation that two TapHandlers are grabbing from each other?  They are on different Items, I suppose?  A parent and a child?  Do you have a simple testable example to show?
> >
> > There are two kinds of grabs now, and gesturePolicy controls which kind of grab TapHandler will take.  As the docs explain, if the TapHandler that gets the event first (the top one in effective “Z-order” [even though only Items have a z property]) has gesturePolicy set to WithinBounds or ReleaseWithinBounds, then it needs to take an exclusive grab on press (same thing that MouseArea would do) to get the intended behavior.  That means it will get the updates and the release.  However it doesn’t stop delivery to other handlers, so yeah, another handler which receives that same press event can steal from the handler that just took the grab.  You can use grabPermissions to make the topmost handler “more greedy” (refusing to give up its grab) or to make the second one “more polite” (refusing to steal).  I would suggest trying to build your UI as cleanly and tersely as possible and then see how few places you can use grabPermissions to fix the remaining conflicts, not sprinkle them all over.
> >
> > On the other hand if gesturePolicy is DragThreshold, it only takes a passive grab on press.  This doesn’t obstruct the delivery process to Items (or other handlers) at all: it means the handler will get any future events involving the same point (the moves and the release) _in addition to_ any other grabbers that get them.  So it can see when the point is released and emit tapped(); and it can also see if the point moves past the drag threshold (and gives up the passive grab in that case), so that tapped() will not be emitted.  Passive grab works well enough to implement that behavior, so it doesn’t bother with an exclusive grab.
> >
> > The purpose of tech preview in 5.10 and 5.11 was in the hope that there would be some experience with using handlers before the 5.12 LTS release, and some interesting cases would emerge so that they could be solved in time.  But now we already have an API freeze for 5.12.  But maybe there is still a little time for some small tweaks if necessary.
> >
> > I’m not 100% sure that accepting the event _should_ mean “stop delivery to everyone else”.  But it’s traditional.  (Is it common in non-Qt APIs too?)  You have to understand delivery order a little for the idea of accepting individual events to make sense, so I have doubts that it’s a friendly API; for UI designers and QML newbies it would be better if they never had to think about that.  Delivery of events to items and handlers is interleaved according to effective Z order, and we have to keep the traditional behavior when delivering to Items to keep source compatibility.  But when architecting the handlers we figured we could change the rules a little in that context, because it’s new API.  And there is the goal to have mostly declarative API: setting some property that controls the behavior is preferred over the old MouseArea { onPressed: event.accepted = false } because IMO it is 1) unfortunate that you need imperative Javascript for that, and 2) unintuitive that the event is accepted by default.  We changed those things with the event handler classes: we expect more handler instances, to be configured with very-choosey property settings, so that most of the instances will ignore most of the events, so rejecting by default makes more sense to me (and consistent with widget API).  But letting events propagate deeper (especially, ignoring the accepted state and letting more handlers see the event) can result in more grab-conflicts too.
> >
> > Several things are still confusingly intertwined for now: gesturePolicy, what kind of grab should be taken on press, whether or not the event should be accepted, and whether accepting the event should mean that delivery stops.  But we’re reluctant to add more declarative API to micro-manage the geeky details.  Naming such API so that it makes sense to all audiences is hard, and ideally QML is friendlier if you don’t have to think about those things anyway.
> >
> > On the other hand, people who already understand the details might expect more complete control of those details.  Maybe we could do it with an attached-property API later.  At least with C++ API you could do it.  (That isn’t public yet, but you can include private headers and subclass handler classes if you want.)
> >
> > Opinions?  Does anyone else have experience with handlers yet?
> >



More information about the Interest mailing list