diff options
-rw-r--r-- | src/quick/handlers/qquickmultipointerhandler.cpp | 36 | ||||
-rw-r--r-- | src/quick/handlers/qquickmultipointerhandler_p.h | 2 | ||||
-rw-r--r-- | src/quick/handlers/qquickpinchhandler.cpp | 6 | ||||
-rw-r--r-- | src/quick/handlers/qquickpointerhandler.cpp | 1 | ||||
-rw-r--r-- | src/quick/handlers/qquicktaphandler.cpp | 7 | ||||
-rw-r--r-- | src/quick/items/qquickevents.cpp | 36 | ||||
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 9 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 1 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 38 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 4 |
10 files changed, 87 insertions, 53 deletions
diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 016210beff..a605b3f12e 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -66,44 +66,34 @@ bool QQuickMultiPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) return false; - const int pCount = event->pointCount(); - int pointCandidateCount = 0; - - // If one or more points are newly pressed, all points are candidates for this handler. - // In other cases however, do not steal the grab: that is, if a point has a grabber, - // it's not a candidate for this handler. - if (event->isPressEvent()) { - pointCandidateCount = pCount; - } else { - for (int i = 0; i < pCount; ++i) { - QQuickEventPoint *pt = event->point(i); - QObject *exclusiveGrabber = pt->exclusiveGrabber(); - if (!exclusiveGrabber || exclusiveGrabber == this) - ++pointCandidateCount; - } - } - - if (pointCandidateCount < m_requiredPointCount) - return false; if (sameAsCurrentPoints(event)) return true; - const QVector<QQuickEventPoint *> candidatePoints = pointsInsideOrNearTarget(event); + const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event); const bool ret = (candidatePoints.size() == m_requiredPointCount); if (ret) m_currentPoints = candidatePoints; return ret; } -QVector<QQuickEventPoint *> QQuickMultiPointerHandler::pointsInsideOrNearTarget(QQuickPointerEvent *event) +QVector<QQuickEventPoint *> QQuickMultiPointerHandler::eligiblePoints(QQuickPointerEvent *event) { QVector<QQuickEventPoint *> ret; int c = event->pointCount(); QRectF targetBounds = target()->mapRectToScene(target()->boundingRect()) .marginsAdded(QMarginsF(m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold)); + // If one or more points are newly pressed or released, all non-released points are candidates for this handler. + // In other cases however, do not steal the grab: that is, if a point has a grabber, + // it's not a candidate for this handler. + bool stealingAllowed = event->isPressEvent() || event->isReleaseEvent(); for (int i = 0; i < c; ++i) { QQuickEventPoint *p = event->point(i); - if (targetBounds.contains(p->scenePos())) + if (!stealingAllowed) { + QObject *exclusiveGrabber = p->exclusiveGrabber(); + if (exclusiveGrabber && exclusiveGrabber != this) + continue; + } + if (p->state() != QQuickEventPoint::Released && targetBounds.contains(p->scenePos())) ret << p; } return ret; @@ -136,6 +126,8 @@ bool QQuickMultiPointerHandler::sameAsCurrentPoints(QQuickPointerEvent *event) // TODO optimize: either ensure the points are sorted, // or use std::equal with a predicate for (int i = 0; ret && i < c; ++i) { + if (event->point(i)->state() == QQuickEventPoint::Released) + return false; bool found = false; int pointId = event->point(i)->pointId(); for (QQuickEventPoint *o : qAsConst(m_currentPoints)) diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h index ce014d658c..1f2d213e56 100644 --- a/src/quick/handlers/qquickmultipointerhandler_p.h +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -88,7 +88,7 @@ protected: bool wantsPointerEvent(QQuickPointerEvent *event) override; bool sameAsCurrentPoints(QQuickPointerEvent *event); - QVector<QQuickEventPoint *> pointsInsideOrNearTarget(QQuickPointerEvent *event); + QVector<QQuickEventPoint *> eligiblePoints(QQuickPointerEvent *event); QPointF touchPointCentroid(); QVector2D touchPointCentroidVelocity(); qreal averageTouchPointDistance(const QPointF &ref); diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 3af77593e2..465a49a6fd 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -228,7 +228,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPos() << "->" << point->scenePos(); } - if (!active()) { + bool containsReleasedPoints = event->isReleaseEvent(); + if (!active() && !containsReleasedPoints) { // Verify that least one of the points have moved beyond threshold needed to activate the handler for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { if (QQuickWindowPrivate::dragOverThreshold(point)) { @@ -302,7 +303,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) << ", rotation" << rotation; } - acceptPoints(m_currentPoints); + if (!containsReleasedPoints) + acceptPoints(m_currentPoints); emit updated(); } diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 3774c2d24c..1ed80c0a5b 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -216,6 +216,7 @@ void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) pt->cancelExclusiveGrab(); } } + event->device()->eventDeliveryTargets().append(this); } bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index f79dbb266a..d73e9fd100 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -270,7 +270,7 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi else setExclusiveGrab(point, press); } - if (!cancel && !press) { + if (!cancel && !press && parentContains(point)) { if (point->timeHeld() < longPressThreshold()) { // Assuming here that pointerEvent()->timestamp() is in ms. qreal ts = point->pointerEvent()->timestamp() / 1000.0; @@ -301,8 +301,9 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) { QQuickPointerSingleHandler::onGrabChanged(grabber, stateChange, point); - if (grabber == this && (stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive)) - setPressed(false, true, point); + bool isCanceled = stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive; + if (grabber == this && (isCanceled || point->state() == QQuickEventPoint::Released)) + setPressed(false, isCanceled, point); } void QQuickTapHandler::connectPreRenderSignal(bool conn) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 0f613d300b..bde271882c 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -1055,6 +1055,18 @@ bool QQuickPointerMouseEvent::isPressEvent() const (me->buttons() & me->button()) == me->buttons()); } +bool QQuickPointerMouseEvent::isUpdateEvent() const +{ + auto me = static_cast<QMouseEvent*>(m_event); + return me->type() == QEvent::MouseMove; +} + +bool QQuickPointerMouseEvent::isReleaseEvent() const +{ + auto me = static_cast<QMouseEvent*>(m_event); + return me->type() == QEvent::MouseButtonRelease; +} + bool QQuickPointerTouchEvent::allPointsAccepted() const { for (int i = 0; i < m_pointCount; ++i) { if (!m_touchPoints.at(i)->isAccepted()) @@ -1124,6 +1136,16 @@ bool QQuickPointerTouchEvent::isPressEvent() const return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed; } +bool QQuickPointerTouchEvent::isUpdateEvent() const +{ + return static_cast<QTouchEvent*>(m_event)->touchPointStates() & (Qt::TouchPointMoved | Qt::TouchPointStationary); +} + +bool QQuickPointerTouchEvent::isReleaseEvent() const +{ + return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointReleased; +} + QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const { QVector<QPointF> points; @@ -1239,7 +1261,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i // Or else just document that velocity is always scene-relative and is not scaled and rotated with the item // but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity - bool anyPressInside = false; + bool anyPressOrReleaseInside = false; bool anyGrabber = false; QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform()); for (int i = 0; i < m_pointCount; ++i) { @@ -1250,8 +1272,9 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i bool isGrabber = p->exclusiveGrabber() == item; if (isGrabber) anyGrabber = true; - // include points inside the bounds + // include points inside the bounds if no other item is the grabber or if the item is filtering bool isInside = item->contains(item->mapFromScene(p->scenePos())); + bool hasAnotherGrabber = p->exclusiveGrabber() && p->exclusiveGrabber() != item; // filtering: (childMouseEventFilter) include points that are grabbed by children of the target item bool grabberIsChild = false; @@ -1265,11 +1288,10 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i } bool filterRelevant = isFiltering && grabberIsChild; - if (!(isGrabber || isInside || filterRelevant)) + if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant)) continue; - bool isPress = p->state() == QQuickEventPoint::Pressed; - if (isPress && isInside) - anyPressInside = true; + if ((p->state() == QQuickEventPoint::Pressed || p->state() == QQuickEventPoint::Released) && isInside) + anyPressOrReleaseInside = true; const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); if (tp) { eventStates |= tp->state(); @@ -1285,7 +1307,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i // Now touchPoints will have only points which are inside the item. // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway. - if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressInside && !anyGrabber && !isFiltering)) + if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering)) return nullptr; // if all points have the same state, set the event type accordingly diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 72d5be54c9..4b3587f18c 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -272,7 +272,8 @@ public: Released = Qt::TouchPointReleased // Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe }; - Q_ENUM(State) + Q_DECLARE_FLAGS(States, State) + Q_FLAG(States) enum GrabState { GrabPassive = 0, @@ -406,6 +407,8 @@ public: // helpers for C++ only (during event delivery) virtual void localize(QQuickItem *target) = 0; virtual bool isPressEvent() const = 0; + virtual bool isUpdateEvent() const = 0; + virtual bool isReleaseEvent() const = 0; virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; } virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; } @@ -448,6 +451,8 @@ public: QQuickPointerEvent *reset(QEvent *) override; void localize(QQuickItem *target) override; bool isPressEvent() const override; + bool isUpdateEvent() const override; + bool isReleaseEvent() const override; QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } int pointCount() const override { return 1; } @@ -481,6 +486,8 @@ public: QQuickPointerEvent *reset(QEvent *) override; void localize(QQuickItem *target) override; bool isPressEvent() const override; + bool isUpdateEvent() const override; + bool isReleaseEvent() const override; QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; } const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } int pointCount() const override { return m_pointCount; } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 4cb7ef3e22..e959b51bc3 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -5113,7 +5113,6 @@ bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoid if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) { handler->handlePointerEvent(event); delivered = true; - eventDeliveryTargets.append(handler); } } } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 9c889d54f5..e4eed8454c 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1703,18 +1703,17 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven bool delivered = false; if (pointerEvent->isPressEvent()) { // send initial press - delivered = deliverPressEvent(pointerEvent); + delivered = deliverPressOrReleaseEvent(pointerEvent); } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) { // if this is an update or release from an actual mouse, // and the point wasn't grabbed, deliver only to PointerHandlers: // passive grabbers first, then the rest - QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); + const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); for (auto handler : point->passiveGrabbers()) { // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) handler->handlePointerEvent(pointerEvent); - eventDeliveryTargets.append(handler); } } // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order @@ -2266,9 +2265,11 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); if (event->isPressEvent()) - deliverPressEvent(event); + deliverPressOrReleaseEvent(event); if (!event->allUpdatedPointsAccepted()) deliverUpdatedTouchPoints(event); + if (event->isReleaseEvent()) + deliverPressOrReleaseEvent(event, true); // Remove released points from itemForTouchPointId bool allReleased = true; @@ -2322,7 +2323,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve int pointCount = event->pointCount(); // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) - QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets(); + const QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets(); for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); for (auto handler : point->passiveGrabbers()) { @@ -2330,7 +2331,6 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve if (sendFilteredPointerEvent(event, handler->parentItem())) return; handler->handlePointerEvent(event); - eventDeliveryTargets.append(handler); } } } @@ -2363,8 +2363,8 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve } } -// Deliver newly pressed touch points -bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) +// Deliver an event containing newly pressed or released touch points +bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly) { int pointCount = event->pointCount(); QVector<QQuickItem *> targetItems; @@ -2372,6 +2372,8 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) for (int i = 0; i < pointCount; ++i) { auto point = event->point(i); point->setAccepted(false); // because otherwise touchEventForItem will ignore it + if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released) + point->setGrabberPointerHandler(nullptr, true); QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), !isTouchEvent, isTouchEvent); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); @@ -2380,14 +2382,14 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) } } - if (allowChildEventFiltering) { + if (allowChildEventFiltering && !handlersOnly) { updateFilteringParentItems(targetItems); if (sendFilteredPointerEvent(event, nullptr)) return true; } for (QQuickItem *item: targetItems) { - deliverMatchingPointsToItem(item, event); + deliverMatchingPointsToItem(item, event, handlersOnly); if (event->allPointsAccepted()) break; } @@ -2395,7 +2397,7 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) return event->allPointsAccepted(); } -void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent) +void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly) { Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -2403,7 +2405,15 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // Let the Item's handlers (if any) have the event first. itemPrivate->handlePointerEvent(pointerEvent); - if (pointerEvent->allPointsAccepted()) + if (handlersOnly) + return; + if (pointerEvent->allPointsAccepted() && !pointerEvent->isReleaseEvent()) + return; + + // If all points are released and the item is not the grabber, it doesn't get the event. + // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario. + if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent() + && !pointerEvent->exclusiveGrabbers().contains(item)) return; // TODO: unite this mouse point delivery with the synthetic mouse event below @@ -2464,11 +2474,11 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo if (eventAccepted) { // If the touch was accepted (regardless by whom or in what form), // update accepted new points. - bool isPress = pointerEvent->isPressEvent(); + bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent(); for (auto point: qAsConst(touchEvent->touchPoints())) { auto pointerEventPoint = ptEvent->pointById(point.id()); pointerEventPoint->setAccepted(); - if (isPress) + if (isPressOrRelease) pointerEventPoint->setGrabberItem(item); } } else { diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index f66809cc8a..fa39eca9a6 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -168,9 +168,9 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPressEvent(QQuickPointerEvent *); + bool deliverPressOrReleaseEvent(QQuickPointerEvent *, bool handlersOnly = false); void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event); - void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent); + void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false); QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const; QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const; |