On 2020 Oct 8, at 13:26, Shawn Rutledge
<[email protected]<mailto:[email protected]>> wrote:
If you subclass QQuickItem and start handling events, it becomes clear that
QEvent::accept() has always meant two things in legacy Qt Quick: 1) stop
propagation and 2) grab the mouse.
…etc… and there weren’t a lot of replies to that thread. At the time, bigger
changes were possible; now, Qt 6.0 is released (and then some), and we always
end up making only incremental changes anyway, with old behavior held in place
by autotests and tradition and shortage of time.
The next increment seems to be this: I think all pointer handlers (TapHandler,
DragHandler, HoverHandler, WheelHandler, PointHandler) should share a common
boolean flag to tell whether they accept or reject events. What we have so far
is that TapHandler has gesturePolicy, which was meant to be intuitive for
designers: describe how it behaves in an observable way, despite the fact that
under the covers it affects the accept flag, which everyone with Qt experience
knows about, but designers can’t be expected to know. Based on bug reports
we’ve received, programmers still want to control the state of the accept flag
to control whether further propagation is allowed. And I want that to be
declarative, not requiring you to write a JS if statement to decide.
What should we call the flag then? I think our best candidates so far are
“blocking”, “transparent” and “propagates". “Blocking” I think is pretty clear
(as long as users don’t confuse it with the idea of blocking execution, as
modal dialogs do); “transparent” maybe has a good meaning if you understand
that it concerns only event delivery, not appearance, and it goes with things
like Qt::WindowTransparentForInput; on the other hand, it does not mean that
the handler fails to react at all, but that it lets the event keep propagating.
And speaking of propagating: I’ve never liked the meaning we give to that
word, because it makes me think of propagating plants: i.e. copying. In fact
we try to avoid copying events where possible; and even if we do it, it’s not
the handler’s job to copy the event and send it to another target; so maybe
let’s not imply that… even though traditional Qt programmers already know what
event propagation is.
So what do you think? do you have any preference there?
More background, how this came up again: Richard has been refactoring the
delivery of hover events in Qt Quick. It’s something I hadn’t gotten around to
yet: I’ve been torn for a while on whether we should continue with
frame-synchronous hover events or come up with something else. From one
perspective, the reason we have hover events is to inform all the items and
handlers that care, that the mouse has moved; so the naive implementation in
early versions of Qt 5 was just sending the actual mouse moves in the form of
QHoverEvents, which made me wonder why do we have those: why isn’t a
QMouseEvent good enough? But then multiple users wrote bugs complaining about
the case when the mouse does not move, but items are being animated, and they
happen to go underneath the current cursor position. (Why is it so hard for
items to just observe the mouse? The trouble is the sheer number of them that
could want to do that, in general. So we use events. Could we possibly invent
something better?) Robin fixed this set of bugs by sending an artificial hover
event once per frame, before the scene graph gets updated prior to rendering.
It sounds expensive, but we also have a flag
QQuickItemPrivate::subtreeHoverEnabled, which is false unless the item or one
of its children actually needs hover events, which it expresses by setting
hoverEnabled to true. So if you are worried about the cost of these events,
make sure your UI design allows us to rule out most or all of the item subtrees
while we are delivering the hover events, so that it can stop short, and
doesn’t need to visit every item and handler. MouseArea has hoverEnabled:
false by default. I didn’t want pointer handlers to have restrictions by
default; so the result of an empty HoverHandler { } declaration inside an item
is that the item will have its private hoverEnabled flag true, and thus its
parents all need subtreeHoverEnabled true too. I wanted to solve the
long-standing problem when an item and its parent (or grandparent) need to show
visual hover feedback simultaneously. MouseArea will only allow that in the
case that one MouseArea is a direct child of another (not the usual way you
design components!) and they both have hoverEnabled: true. HoverHandler works
regardless of the hierarchy. So, that implies the hover event needs to be
rejected by default, so that it can keep propagating to all other hover
handlers; and that means more work for the delivery code. Now, Richard wants
to enable you to optimize it again by having some flag that you can set to tell
that when one HoverHandler has reacted, it’s enough: nothing else in the scene
needs to know. A HoverHandler ought to reject events by default, because it
cannot know in general whether other items and handlers are interested; but you
should be able to tell it to leave them accepted so that propagation stops, if
you know that no others will be interested.
I’m working on Qt Quick 3D lately: when you have an interactive 2D Qt Quick
scene mapped onto a 3D object, the pointing devices and the keyboard have to
keep working normally. That’s the first step. The next step might be to allow
declaring pointer handlers directly inside 3D objects, without a 2D subscene:
that’s just an experiment so far. (See
https://github.com/ec1oud/qtquick3d-input-demo for a demo, but it depends on
features that aren’t yet integrated.) Anyway… when you hover a 3D object, we
need to do picking (create a mathematical abstraction of a ray pointing down
into the scene, calculate which faces of which 3D objects it intersects and at
what exact positions, then visit each 2D scene that is mapped onto them to see
what items and handers are impacted), so the expense of doing that at 60FPS (or
more) is higher than it was in Qt Quick itself. (Granted, we can probably do
it less often: every 1/10 of a second might be often enough. So maybe we can
just skip some frames until that much time has elapsed.) I think application
developers need ways to avoid that overhead in case hover is not needed in most
of the scene.
WheelHandler behaves in a way that looks to be “blocking” by default, but I
think we need the flag there too: you should be able to react to the mouse
wheel in more than one place in the z stack, if you need to. (Show feedback in
one layer and actually move something in another layer, for example.). Last
year I was going to call it acceptsEvents, just in WheelHandler, but that patch
sat there in gerrit, unapproved for a year, and I already was thinking we need
to solve this problem in general, not just for WheelHandler. If we are to have
one unified flag, the default setting will be different in different handlers,
and I think that’s OK: we'll just document it. The interaction between the new
flag and TapHandler.gesturePolicy is tricky though. One will have to override
the other. I suppose maybe blocking should override gesturePolicy because
blocking is the geekier flag, you probably know what you’re doing if you use
it? But I didn’t try yet; maybe something would go wrong with that.
_______________________________________________
Development mailing list
[email protected]
https://lists.qt-project.org/listinfo/development