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

Shawn Rutledge Shawn.Rutledge at qt.io
Mon Sep 10 18:02:24 CEST 2018


> 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