![]() |
Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
|
In Dragging one DragHandler with one touchpoint we began with a detailed tour through the event delivery logic while one Rectangle is being dragged on a touchscreen with one finger. Let's now delve into parallel delivery: two fingers dragging two Rectangles, each with its own DragHandler, on a touchscreen.
The user presses two fingers on two of the small Rectangles simultaneously.
① A QTouchEvent arrives, it contains two QEventPoints, and we have to decide which items and handlers we're going to visit. QQuickWindow::event() dispatches to ② QQuickDeliveryAgent::event(). ③ QQuickDeliveryAgentPrivate::deliverPointerEvent() calls ④ QQuickDeliveryAgentPrivate::deliverPressOrReleaseEvent(), which iterates QPointerEvent::points() and calls ⑤ QQuickDeliveryAgentPrivate::pointerTargets() to build a list of relevant items and handlers that should have a chance to handle that point. If the candidate is a pointer handler, ⑥⑦⑧⑨ QQuickPointerHandler::wantsEventPoint() is the way of asking the handler whether or not it's interested. In this case, for each of the two touchpoints, the PinchHandler and one DragHandler both return true
, so both handlers' parent items get added to the targets
list. Then it merges the lists, so that we end up with one list of all relevant items for all points in the multi-touch event.
Then for each item in the targets
list, we call ⑩ QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(). Again we need to call mapFromScene() and set QEventPoint::position() to item-local coordinates, then ⑪ QQuickItemPrivate::handlePointerEvent() loops over any handlers that are found in the list QQuickItemPrivate::ExtraData::pointerHandlers and calls QQuickPointerHandler::handlePointerEvent() on each of those. Note that the event has not been split up: each DragHandler
sees both touchpoints. So it calls its own wantsPointerEvent(), which (according to its name) should decide whether it wants any part of the multi-touch event; but the parent class of DragHandler
is QQuickMultiPointHandler. QQuickMultiPointHandler::wantsPointerEvent() iterates the points again, calls wantsEventPoint() again for each of those, and builds a list of candidatePoints
. In this case, each DragHandler
is only interested in one point: the one whose position is inside the Rectangle (DragHandler's parent item). That point goes into candidatePoints
. For each of those, a QQuickHandlerPoint instance in QQuickMultiPointHandlerPrivate::currentPoints is initialized to remember that this DragHandler is handling this point.
Then we get to the virtual ⑫ handlePointerEventImpl() function. QQuickMultiPointHandler::handlePointerEventImpl() updates the QML-facing centroid
property, another QQuickHandlerPoint instance; and QQuickDragHandler::handlePointerEventImpl() calls QQuickPointerHandlerPrivate::dragOverThreshold() to check whether the QEventPoint has been dragged past the dragThreshold. As long as it has not been dragged that far, DragHandler
is only monitoring to see what the user will do, so it retains a ⑬ passive grab.
The PinchHandler also "wants" and passively grabs both points: they are both inside its parent Rectangle.
Handling of subsequent move events proceeds in a similar way as the single-point case : QQuickDeliveryAgentPrivate::deliverPointerEvent() calls QQuickDeliveryAgentPrivate::deliverUpdatedPoints(), which iterates the QEventPoints, and for each of those, iterates the passive grabbers in QPointingDevicePrivate::EventPointData::passiveGrabbers (in the order that they are stored, the same order that they added themselves as passive grabbers) and calls QQuickDeliveryAgentPrivate::deliverToPassiveGrabbers(). After QQuickDeliveryAgentPrivate::localizePointerEvent(), it calls QQuickPointerHandler::handlePointerEvent(). QQuickMultiPointHandler::wantsPointerEvent() returns true
because the point in QQuickMultiPointHandlerPrivate::currentPoints still exists in this QTouchEvent; so QQuickDragHandler::handlePointerEventImpl() is called. For each point, it calculates the movement delta (scenePosition() - scenePressPosition())
; and QQuickPointerHandlerPrivate::dragOverThreshold() checks whether it's moved far enough to activate dragging. When the user begins the drag gesture by dragging a finger far enough, the appropriate DragHandler
will take the exclusive grab by calling QQuickMultiPointHandler::grabPoints(). This is not a take-over scenario, so grabPoints() succeeds; therefore, it's ok to call QQuickPointerHandler::setActive(). Since by default, DragHandler's target
is the same as its parent
, QQuickDragHandler::handlePointerEventImpl() ends with a call to QQuickMultiPointHandler::moveTarget(). That uses QMetaProperty::write() to change the Rectangle's x
and y
properties. And so each Rectangle moves as far as the finger is dragged.
deliverUpdatedPoints() also visits the PinchHandler
. Since at least one of the DragHandlers got a chance to take the exclusive grab before delivery to the PinchHandler, and we have declared
the PinchHandler does not have permission to "take over" the exclusive grab from an existing handler. If the qt.quick.handler.grab logging category is enabled, you will see a message like
However if you drag two fingers on the root Rectangle
, outside the smaller Rectangles that have DragHandlers, pinching works, because the PinchHandler
is the only candidate to handle those points. This is explained in the next chapter: Pinch on a touchscreen