diff options
-rw-r--r-- | src/quick/items/qquickevents.cpp | 27 | ||||
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 6 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 9 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 2 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 40 | ||||
-rw-r--r-- | tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp | 57 |
6 files changed, 112 insertions, 29 deletions
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 0c18cb4104..a68e72b0b7 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -835,6 +835,11 @@ bool QQuickPointerMouseEvent::allPointsAccepted() const { return m_mousePoint->isAccepted(); } +bool QQuickPointerMouseEvent::allPointsGrabbed() const +{ + return m_mousePoint->grabber() != nullptr; +} + QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const { auto event = static_cast<QMouseEvent *>(m_event); @@ -854,6 +859,11 @@ void QQuickPointerMouseEvent::clearGrabbers() const { m_mousePoint->setGrabberItem(nullptr); } +bool QQuickPointerMouseEvent::hasGrabber(const QQuickPointerHandler *handler) const +{ + return m_mousePoint->grabber() == handler; +} + bool QQuickPointerMouseEvent::isPressEvent() const { auto me = static_cast<QMouseEvent*>(m_event); @@ -869,6 +879,15 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const { return true; } +bool QQuickPointerTouchEvent::allPointsGrabbed() const +{ + for (int i = 0; i < m_pointCount; ++i) { + if (!m_touchPoints.at(i)->grabber()) + return false; + } + return true; +} + QVector<QObject *> QQuickPointerTouchEvent::grabbers() const { QVector<QObject *> result; @@ -887,6 +906,14 @@ void QQuickPointerTouchEvent::clearGrabbers() const { point->setGrabber(nullptr); } +bool QQuickPointerTouchEvent::hasGrabber(const QQuickPointerHandler *handler) const +{ + for (auto point: m_touchPoints) + if (point->grabber() == handler) + return true; + return false; +} + bool QQuickPointerTouchEvent::isPressEvent() const { return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 4a78e98705..bbfc34dacf 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -393,6 +393,7 @@ public: // helpers for C++ only (during event delivery) virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } bool isValid() const { return m_event != nullptr; } virtual bool allPointsAccepted() const = 0; + virtual bool allPointsGrabbed() const = 0; bool isAccepted() { return m_event->isAccepted(); } void setAccepted(bool accepted) { m_event->setAccepted(accepted); } QVector<QPointF> unacceptedPressedPointScenePositions() const; @@ -402,6 +403,7 @@ public: // helpers for C++ only (during event delivery) virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; virtual QVector<QObject *> grabbers() const = 0; virtual void clearGrabbers() const = 0; + virtual bool hasGrabber(const QQuickPointerHandler *handler) const = 0; ulong timestamp() const { return m_event->timestamp(); } @@ -430,8 +432,10 @@ public: QQuickEventPoint *point(int i) const override; QQuickEventPoint *pointById(quint64 pointId) const override; bool allPointsAccepted() const override; + bool allPointsGrabbed() const override; QVector<QObject *> grabbers() const override; void clearGrabbers() const override; + bool hasGrabber(const QQuickPointerHandler *handler) const override; QMouseEvent *asMouseEvent(const QPointF& localPos) const; @@ -461,8 +465,10 @@ public: QQuickEventPoint *pointById(quint64 pointId) const override; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; + bool allPointsGrabbed() const override; QVector<QObject *> grabbers() const override; void clearGrabbers() const override; + bool hasGrabber(const QQuickPointerHandler *handler) const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 98cc3112df..46e381db7d 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -5091,15 +5091,20 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event) } } -void QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event) +bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidGrabber) { Q_Q(QQuickItem); + bool delivered = false; if (extra.isAllocated()) { for (QQuickPointerHandler *handler : extra->pointerHandlers) { qCDebug(lcPointerHandlerDispatch) << " delivering" << event << "to" << handler << "on" << q; - handler->handlePointerEvent(event); + if (!avoidGrabber || !event->hasGrabber(handler)) { + handler->handlePointerEvent(event); + delivered = true; + } } } + return delivered; } /*! diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index e7ead7fa87..b39c437da5 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -563,7 +563,7 @@ public: #endif void deliverShortcutOverrideEvent(QKeyEvent *); - virtual void handlePointerEvent(QQuickPointerEvent *); + virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidGrabber = false); bool isTransparentForPositioner() const; void setTransparentForPositioner(bool trans); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 6897240fbd..f7d6b245b2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1685,11 +1685,23 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem()) q->mouseGrabberItem()->ungrabMouse(); } else { - // send initial press bool delivered = false; if (pointerEvent->isPressEvent()) { + // send initial press QSet<QQuickItem*> hasFiltered; delivered = deliverPressEvent(pointerEvent, &hasFiltered); + } 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 non-grabber PointerHandlers + QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePos(), false); + for (QQuickItem *item : targetItems) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + pointerEvent->localize(item); + if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers + delivered = true; + if (point->grabber()) + break; + } } if (!delivered) @@ -2082,7 +2094,6 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) accepted = clearHover(event->timestamp()); } event->setAccepted(accepted); - return; } deliverPointerEvent(pointerEventInstance(event)); break; @@ -2271,6 +2282,31 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve deliverMatchingPointsToItem(receiver, event, hasFiltered); } + // If some points weren't grabbed, deliver only to non-grabber PointerHandlers + if (!event->allPointsGrabbed()) { + QVector<QQuickItem *> targetItems; + int pointCount = event->pointCount(); + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false); + if (targetItems.count()) { + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + } else { + targetItems = targetItemsForPoint; + } + } + + for (QQuickItem *item: targetItems) { + if (grabbers.contains(item)) + continue; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + event->localize(item); + itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers + if (event->allPointsGrabbed()) + break; + } + } + return false; } diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp index 94bb44a12b..8f65ec6d7f 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp @@ -76,7 +76,7 @@ class EventItem : public QQuickItem Q_OBJECT public: EventItem(QQuickItem *parent = 0) - : QQuickItem(parent), acceptPointer(false), acceptMouse(false), acceptTouch(false), filterTouch(false) + : QQuickItem(parent), acceptPointer(false), grabPointer(false), acceptMouse(false), acceptTouch(false), filterTouch(false) {} void touchEvent(QTouchEvent *event) @@ -125,6 +125,7 @@ public: QList<Event> eventList; bool acceptPointer; + bool grabPointer; bool acceptMouse; bool acceptTouch; bool filterTouch; // when used as event filter @@ -159,10 +160,11 @@ class EventHandler : public QQuickPointerHandler int c = event->pointCount(); for (int i = 0; i < c; ++i) { QQuickEventPoint *point = event->point(i); - point->setAccepted(item->acceptPointer); // has no effect: grabbing is up to us if (item->acceptPointer) - setGrab(point, true); - qCDebug(lcPointerTests) << " " << i << ":" << point << "grabbed?" << item->acceptPointer; + point->setAccepted(item->acceptPointer); // non-exclusive grab + if (item->grabPointer) + setGrab(point, true); // exclusive grab + qCDebug(lcPointerTests) << " " << i << ":" << point << "accepted?" << item->acceptPointer << "grabbed?" << (point->grabber() == this); item->eventList.append(Event(QEvent::Pointer, eventPos(point), point->scenePos())); } } @@ -236,17 +238,20 @@ void tst_PointerHandlers::touchEventDelivery() QPoint p1 = QPoint(20, 20); QTest::touchEvent(window, touchDevice).press(0, p1, window); QQuickTouchUtils::flush(window); - QTRY_COMPARE(eventItem1->eventList.size(), 3); + QTRY_COMPARE(eventItem1->eventList.size(), 4); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::Pointer); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(2).type, QEvent::MouseButtonPress); + QCOMPARE(eventItem1->eventList.at(3).type, QEvent::Pointer); p1 += QPoint(10, 0); QTest::touchEvent(window, touchDevice).move(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 5); + QCOMPARE(eventItem1->eventList.at(4).type, QEvent::Pointer); QTest::touchEvent(window, touchDevice).release(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 6); + QCOMPARE(eventItem1->eventList.at(5).type, QEvent::Pointer); eventItem1->eventList.clear(); // Accept touch @@ -321,18 +326,19 @@ void tst_PointerHandlers::touchEventDelivery() p1 = QPoint(20, 20); QTest::touchEvent(window, touchDevice).press(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 4); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::Pointer); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(2).type, QEvent::MouseButtonPress); + QCOMPARE(eventItem1->eventList.at(3).type, QEvent::Pointer); QCOMPARE(pointerEvent->point(0)->grabber(), nullptr); p1 += QPoint(10, 0); QTest::touchEvent(window, touchDevice).move(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 5); QTest::touchEvent(window, touchDevice).release(0, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 6); eventItem1->eventList.clear(); // wait to avoid getting a double click event @@ -362,6 +368,7 @@ void tst_PointerHandlers::touchEventDelivery() // Accept pointer events eventItem1->acceptPointer = true; + eventItem1->grabPointer = true; p1 = QPoint(20, 20); QTest::touchEvent(window, touchDevice).press(0, p1, window); QQuickTouchUtils::flush(window); @@ -398,12 +405,12 @@ void tst_PointerHandlers::mouseEventDelivery() // Do not accept anything QPoint p1 = QPoint(20, 20); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 3); p1 += QPoint(10, 0); QTest::mouseMove(window, p1); - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 4); QTest::mouseRelease(window, Qt::LeftButton); - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 4); eventItem1->eventList.clear(); // wait to avoid getting a double click event @@ -415,9 +422,10 @@ void tst_PointerHandlers::mouseEventDelivery() eventItem1->setAcceptedMouseButtons(Qt::LeftButton); p1 = QPoint(20, 20); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 3); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::Pointer); - QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); + QCOMPARE(eventItem1->eventList.at(1).type, QEvent::Pointer); + QCOMPARE(eventItem1->eventList.at(2).type, QEvent::MouseButtonPress); QCOMPARE(window->mouseGrabberItem(), eventItem1); QPointF localPos = eventItem1->mapFromScene(p1); @@ -429,30 +437,31 @@ void tst_PointerHandlers::mouseEventDelivery() p1 += QPoint(10, 0); QTest::mouseMove(window, p1); - QCOMPARE(eventItem1->eventList.size(), 3); - QCOMPARE(eventItem1->eventList.at(2).type, QEvent::MouseMove); + QCOMPARE(eventItem1->eventList.size(), 4); + QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QCOMPARE(eventItem1->eventList.size(), 5); - QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseButtonRelease); - QCOMPARE(eventItem1->eventList.at(4).type, QEvent::UngrabMouse); + QCOMPARE(eventItem1->eventList.size(), 6); + QCOMPARE(eventItem1->eventList.at(4).type, QEvent::MouseButtonRelease); + QCOMPARE(eventItem1->eventList.at(5).type, QEvent::UngrabMouse); eventItem1->eventList.clear(); // wait to avoid getting a double click event QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); - // Accept pointer events + // Grab pointer events eventItem1->acceptMouse = false; eventItem1->acceptPointer = true; + eventItem1->grabPointer = true; p1 = QPoint(20, 20); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(eventItem1->eventList.size(), 1); + QTRY_COMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::Pointer); p1 += QPoint(10, 0); QTest::mouseMove(window, p1); - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 3); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::Pointer); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 4); QCOMPARE(eventItem1->eventList.at(2).type, QEvent::Pointer); eventItem1->eventList.clear(); |