[Development] QInputEvent refactoring
Henry Skoglund
henry at tungware.se
Tue May 26 16:27:35 CEST 2020
On 2020-05-26 16:07, Shawn Rutledge wrote:
> When I started working with Qt Quick in 2011, it wasn't too
> long before I began to notice that our vaunted support for multitouch
> (which
> still felt like an innovative new feature at the time, even though it had
> already been 5 years since the introduction of the iPhone) was quite
> flawed.
> Everyone was using MouseArea and Flickable, and those were not very good
> companions for the only components that actually supported multitouch:
> MultiPointTouchArea and PinchArea. At some point someone had made the
> decision
> that touch is enough like mouse, that it would simplify things if we just
> convert touch events to synthetic mouse events and reuse the same
> logic for
> both. Maybe that was decided before multi-touch screens were invented; it
> would have been ok for the resistive ones from the QTopia days, for
> example.
> But I thought that Qt Quick was newer than that, so it was an immediate
> facepalm moment when I realized that mistake was perpetuated there.
> Finding
> ways to work around it has dominated a lot of my time working on Qt
> Quick ever
> since. But Qt 5 has had such a long lifespan, and it wasn't possible
> to fix it
> in a fundamental way, without increasing complexity in ways that you
> might have
> noticed.
>
> First, I did some patches to handle actual QTouchEvents in Flickable and
> MouseArea. This naturally increases the code size quite dramatically,
> because
> QMouseEvent and QTouchEvent have too little in common, and the delivery
> strategy needs to be different. QTouchEvents are broken up according
> to item
> boundaries, so that you can touch multiple Items at the same time with
> different fingers. This complexity was already there in QQuickWindow,
> but I
> had to add a lot more code to Flickable and MouseArea. It took a
> really long
> time to get all the tests to pass again. Frederik was supportive,
> understood
> the point of what I was doing, and helped with it. Finally this work
> was ready
> to go into 5.5, but then reviewers still had too many doubts. From
> one side it
> doesn't make a lot of sense to take a component called MouseArea, with
> all the
> limitations that name implies, and try to make it do the right things with
> touch events too. (Even though users already expected it to handle
> taps and
> drags on touchscreens, because the touch->mouse synthesis had always been
> there.) It was a lot of code to read and understand. So we left it
> broken.
> And customers keep asking for those patches, so they still exist on a
> personal
> branch somewhere, which I haven't updated for quite a while. To this
> day, if
> you use any touch-handling Item or pointer handler inside a Flickable, the
> results are often not very satisfying. If you turn on pressDelay, it gets
> worse. (Flickable never saw the touch press, only a synth-mouse
> press. So it
> cannot replay a touch press either. So the children will see a
> synthetic mouse
> press followed by a series of touch events, and will be required to
> see through
> the synth-mouse crap and treat the whole series as if the press had been a
> touch event too. But… should filtering events that would otherwise go
> to the
> children really be Flickable’s responsibility? What about replaying
> events
> after a press delay: monolithic Flickable should really do that too?) And
> because of another architectural abomination, making Item Views inherit
> Flickable, that affects even more use cases with ListView and TableView
> delegates. (I have some hope of eventually rewriting Flickable using
> Pointer Handlers (that’s what FakeFlickable.qml demonstrates), but
> keeping it working the same both for subclasses and for end-users is quite
> a high bar.)
>
> That experience taught me that we can only fix touch and mouse and
> Wacom tablet
> event delivery by making it the same for all of them. That means we
> must make
> the events look enough alike that the same delivery code will work for
> all of
> them. It's not possible with the leftover QInputEvent hierarchy from
> Qt 4 and
> earlier. There is not even a consistently-named set of accessors for
> getting
> the coordinates from the various event types.
>
> Continuing with the touch->mouse synthesis approach could maybe have been
> justified if we had support for multiple mice in Qt (so that there
> could be a
> virtual mouse for each touchpoint), and if we could agree that it's ok to
> disassociate touchpoints from each other and deliver them as separate
> events.
> I had a series of patches to deliver touch events that way. It worked
> fine in
> practice, but for that prototype I had done some non-BC modifications in
> qevent.h (which could have been mitigated with differently-designed
> wrapper
> events). But when we discussed it with Lars, he was very much against
> the idea
> of disassociating touchpoints, feeling strongly that points which
> belong to the
> same gesture need to be kept together. As said, touch events get
> broken up
> during delivery in QQuickWindow; but if PinchHandler for example received
> multiple events, one for each finger involved in the pinch, it would
> have to
> update the gesture each time. We could have mitigated that by adding an
> incrementing frame counter so that touchpoints could be re-associated.
>
> But at that time, we concluded that we will go the other way in Qt
> Quick: every
> "pointer event" will have API appropriate for multiple points.
> QMouseEvent can
> have hard-coded accessors for the single point that it carries; but touch
> events carry multiple points. This is how we can eventually refactor the
> delivery code so that mouse and touch events are delivered the same
> way. And
> we agreed to make the Qt Quick events a prototype of how we would
> refactor the
> QEvents in Qt 6. So since Qt 5.8, Qt Quick has been delivering
> wrapper events
> instead: QQuickPointerEvent which contains instances of
> QQuickEventPoint. Some
> of the delivery refactoring has been done: conservatively, because
> although few
> are willing to help, a lot more complain loudly when any existing usecase
> breaks. And there are so many applications already. But the wrapper
> events
> made it possible to develop Pointer Handlers; and the goal has always
> been that
> those would retain QML source compatibility in Qt 6. The delivery
> mechanism
> for those adds a lot of flexibility. After enough use cases have been
> ported
> over to using them, maybe we can eventually deprecate some of the most
> pernicious features that depend on complex delivery code that I'd like
> to get
> rid of in QQuickWindow; but progress has been so slow in other modules
> outside
> of Qt Quick itself, that it still seems too early to consider doing
> that in Qt
> 6, because it would require heroic effort by a number of people over a
> short
> time period.
>
> Anyway, the time has come to at least get the QEvent refactoring done,
> so that
> Qt Quick can go back to delivering them without wrappers, and so that
> Items can
> receive pointer events directly. We have discussed this a few times
> already at
> various events. At one QtCS Qt 6 planning session, I think in 2018
> (or was it
> earlier?), I promised to do the refactoring for Qt 6. The goal is to
> break
> nothing in widgets: that is, QMouseEvent needs to keep its existing
> accessors.
> We will add new ones, and deprecate the ones that are named
> inconsistently and
> the ones that provide integer coordinates. The same for the other
> event types.
> QTouchEvent::TouchPoint is a bit special: it will be replaced by the
> QEventPoint that every QPointerEvent contains at least one of. So far
> it looks
> like I can use "using" to make QTouchEvent::TouchPoint an alias of
> QEventPoint,
> for the sake of source compatibility.
>
> I think the result will look something like this:
>
>
> What I started with a few months ago was adding const QInputDevice
> *device() to
> QInputEvent. We have seen that the MouseEventSource enum does not provide
> enough information. E.g. in Controls we had to assume in some places
> that if a
> mouse event is SynthesizedByQt, then it's synthesized from a
> touchscreen by
> QQuickWindow during delivery of the original QTouchEvent. That's not
> always
> true (there are other places where synthesis occurs), and resulted in some
> bugs. Now that we're trying to fully support Wacom tablets in Qt Quick, a
> synth-mouse event could come from that. So I want to completely replace
> MouseEventSource with the device pointer, so that the event consumer
> can see
> specifically which device the event came from, and thus can adjust
> behavior
> depending on the specific type of device, its capabilities, the screen
> area
> that the device can access (e.g. a touchscreen input device or Wacom
> tablet is
> probably mapped to a specific QScreen or QWindow), etc. This way we
> can also
> begin to support multiple mice and multiple "seats" (in the Wayland
> sense) at
> the same time. But it imposes a new requirement on platform plugins:
> to create
> the QInputDevice instances. The plugins that I know about all do device
> discovery anyway, though; they have just been using internal ad-hoc data
> structures of their own, and not exposing those devices to
> QWindowSystemInterface. I've been working on the xcb plugin so far,
> since I
> understand that one the best, and it already supports multiple seats
> after a
> fashion (there can be multiple core pointers and core keyboards, and
> they can
> be associated with each other; there just isn't a seat name, but I can
> make one
> up).
>
> The fantastic result of that should be that event delivery code can
> finally be
> device-agnostic! QQuickWindow and QQuickFlickable will no longer need
> to treat
> mouse and touch events differently, and Wacom tablet events will go
> through the
> same way too. Flickable should be able to blindly replay a copy of
> whatever event
> it got when the pressDelay timer fires, without caring about every
> piece of data
> inside. Only the final event consumer (QQuickItem or Pointer Handler
> or even
> a QWidget subclass) will need to care about the device-specific
> details, and it
> will have all the information necessary for very finely-tuned
> behavior. Now we
> can finally add virtual functions to QQuickItem to handle pointer
> events, so
> not only Pointer Handlers will be able to do that. And we will open the
> possibility to refactor event delivery code in other parts of Qt later
> on. It
> should become possible to fix most of the open bugs related to
> handling mouse
> and touch events in Qt Quick and Controls during the Qt 6 series.
>
> Because we will make QPointerEvent look as much as possible like
> QQuickPointerEvent, we will maintain QML source compatibility for
> anyone who
> just started using pointer handlers. Of course the goal is for older
> stuff
> like MouseArea and Flickable and Controls that we can choose
> appropriate API
> changes, not be required to make them because of event changes.
> QPointerEvent
> will be a gadget instead of a QObject wrapper, but it will have the same
> properties, so to the extent that it's exposed in QML API (which is
> not much),
> it will look the same. It will also look enough like a QMouseEvent
> and enough
> like a QTouchEvent that we will have source compatibility in virtual
> functions
> that handle those, too. Hopefully.
>
> After proving that we have also maintained source compatibility as much as
> possible (including in the great heap of widget code in the world), we
> still
> end up with a lot of deprecated methods that should be replaced over time
> (QPoint pos() -> QPointF position() and such). For that purpose I
> want to add
> a feature to clazy or develop some other clang-based tooling which can
> fix all
> of our modules, and also be available to customers to make the same
> replacements in their code bases. If we end up with any SC breaks, it's a
> possible fallback position that at least we can deliver a tool to fix
> them.
>
> Beyond that, we probably ought to do something about native gestures.
> Another
> reason Qt Quick is so complex is that it started out assuming it will
> be given
> only raw touch events and needs to do gesture recognition itself; but now
> gestures have gone mainstream on most platforms, so we could be getting
> QNativeGestureEvents from most of them, especially from touchpads. But I
> didn't get as far as I should have over the last couple of years just
> exploring
> how to improve that aspect of Qt, and the platform plugin maintainers
> have not gotten around to adding support for native gestures to all the
> platforms that could have them, either. I wish we could get rid of
> the gesture
> recognizer in the widgets module completely; but customers will not be
> satisfied unless we then have native gesture recognition on all
> platforms where
> it's possible. Some of them want to continue doing custom gesture
> recognition,
> too. But we have QGestureEvent (for the widget gesture recognizer) and
> QNativeGestureEvent (for gesture events that come from the QPA plugin) as
> separate types. We're running out of time to figure out whether we
> can unify
> those two, just to make it less confusing for applications. I guess
> it's not
> so terrible to keep the events separate if we still have to keep the old
> gesture framework intact; but do we?
>
> So that's the status of pointer event handling. And I'm still working
> mostly
> alone on it. It seems with our schedule that the QEvent API, and all
> other
> APIs that need to change as a result of that, need to be stabilized by
> the end
> of June. I still think I can get the broad strokes done if nobody and no
> unexpected bugs get in the way this time, and the +2's come quickly
> (keeping in
> mind that the perfect is the enemy of the good, and every change is
> subject to
> ongoing incremental changes later). (It looks like my time is limited
> to make
> other API changes in Qt Quick, since this is taking most of it.) Is
> anyone
> interested in helping?
>
Hi, I tried some QGestureEvents this winter in my first Qt iOS app, and
got bored having to use 3 fingers on my iPhone for swiping (1 finger
seems to be the norm on Android and iPhones). And I discovered that it
was pretty easy to integrate the vanilla standard iOS 1-finger swiping
with Qt, see https://bugreports.qt.io/browse/QTBUG-81042
If you're thinking about refactoring also for iOS, maybe my suggestion
can help...
Rgrds Henry
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/development/attachments/20200526/12fb6caf/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: event-hierarchy-qt6.png
Type: image/png
Size: 167066 bytes
Desc: not available
URL: <http://lists.qt-project.org/pipermail/development/attachments/20200526/12fb6caf/attachment-0001.png>
More information about the Development
mailing list