From af21677f473bce12ee7c725b1151ae794384ae76 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 7 Feb 2017 13:24:49 +0100 Subject: QQuickWindow: remove sendFilteredTouchEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functionality is now completely assimilated into sendFilteredPointerEvent. Flickable can now steal the touch grab from a TapHandler child, for example: the TapHandler grabbed the eventpoint, whereas QQuickFlickable::childMouseEventFilter() was given a synthetic mouse event derived from the touchpoint which the eventpoint represented, and we fully follow through the consequences of that. If an Item filters a press event, avoid doing normal delivery to that same Item again. Change-Id: Icd9c88ab752f2b728f7d612504013c6dc72ff9fe Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 13 +++ src/quick/items/qquickevents_p_p.h | 3 + src/quick/items/qquickwindow.cpp | 181 +++++++++++++++---------------------- src/quick/items/qquickwindow_p.h | 7 +- 4 files changed, 91 insertions(+), 113 deletions(-) (limited to 'src/quick') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index c3f6964ba6..0af19ff750 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -896,6 +896,10 @@ bool QQuickPointerMouseEvent::allPointsAccepted() const { return m_mousePoint->isAccepted(); } +bool QQuickPointerMouseEvent::allUpdatedPointsAccepted() const { + return m_mousePoint->state() == QQuickEventPoint::Pressed || m_mousePoint->isAccepted(); +} + bool QQuickPointerMouseEvent::allPointsGrabbed() const { return m_mousePoint->grabber() != nullptr; @@ -941,6 +945,15 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const { return true; } +bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const { + for (int i = 0; i < m_pointCount; ++i) { + auto point = m_touchPoints.at(i); + if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted()) + return false; + } + return true; +} + bool QQuickPointerTouchEvent::allPointsGrabbed() const { for (int i = 0; i < m_pointCount; ++i) { diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 5386eacad9..d042f318e9 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -400,6 +400,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 allUpdatedPointsAccepted() const = 0; virtual bool allPointsGrabbed() const = 0; bool isAccepted() { return m_event->isAccepted(); } void setAccepted(bool accepted) { m_event->setAccepted(accepted); } @@ -439,6 +440,7 @@ public: QQuickEventPoint *point(int i) const override; QQuickEventPoint *pointById(quint64 pointId) const override; bool allPointsAccepted() const override; + bool allUpdatedPointsAccepted() const override; bool allPointsGrabbed() const override; QVector grabbers() const override; void clearGrabbers() const override; @@ -472,6 +474,7 @@ public: QQuickEventPoint *pointById(quint64 pointId) const override; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; + bool allUpdatedPointsAccepted() const override; bool allPointsGrabbed() const override; QVector grabbers() const override; void clearGrabbers() const override; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 10144e6eb9..7321ebdfb2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1689,8 +1689,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven bool delivered = false; if (pointerEvent->isPressEvent()) { // send initial press - QSet hasFiltered; - delivered = deliverPressEvent(pointerEvent, &hasFiltered); + delivered = deliverPressEvent(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: @@ -2250,11 +2249,10 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); - QSet hasFiltered; if (event->isPressEvent()) - deliverPressEvent(event, &hasFiltered); - if (!event->allPointsAccepted()) - deliverUpdatedTouchPoints(event, &hasFiltered); + deliverPressEvent(event); + if (!event->allUpdatedPointsAccepted()) + deliverUpdatedTouchPoints(event); // Remove released points from itemForTouchPointId bool allReleased = true; @@ -2281,7 +2279,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) } // Deliver touch points to existing grabbers -bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) { const auto grabbers = event->grabbers(); for (auto grabber : grabbers) { @@ -2300,7 +2298,7 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve } // If the grabber is an item or the grabbing handler didn't handle it, // then deliver the event to the item (which may have multiple handlers). - deliverMatchingPointsToItem(receiver, event, hasFiltered); + deliverMatchingPointsToItem(receiver, event); } // If some points weren't grabbed, deliver only to non-grabber PointerHandlers @@ -2324,6 +2322,8 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve QVector targetItems; for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); + if (point->state() == QQuickEventPoint::Pressed) + continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints QVector targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); @@ -2348,7 +2348,7 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve } // Deliver newly pressed touch points -bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) { const QVector points = event->unacceptedPressedPointScenePositions(); QVector targetItems; @@ -2368,7 +2368,7 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSetallPointsAccepted()) break; } @@ -2376,7 +2376,7 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSetallPointsAccepted(); } -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent) { Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -2415,28 +2415,22 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo return false; } - QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent(); - if (!event) + QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent(); + if (!ptEvent) return false; - QScopedPointer touchEvent(event->touchEventForItem(item)); + QScopedPointer touchEvent(ptEvent->touchEventForItem(item)); if (!touchEvent) return false; - qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item; + qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item; bool eventAccepted = false; - // First check whether the parent wants to be a filter, - // and if the parent accepts the event we are done. - if (sendFilteredTouchEvent(item->parentItem(), item, event, hasFiltered)) { - // If the touch was accepted (regardless by whom or in what form), - // update acceptedNewPoints - qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; - for (auto point: qAsConst(touchEvent->touchPoints())) { - event->pointById(point.id())->setAccepted(); - } + // If any parent filters the event, we're done. + // updateFilteringParentItems was called when the press occurred, + // and we assume that the filtering relationships don't change between press and release. + if (sendFilteredPointerEvent(pointerEvent, item)) return true; - } // Deliver the touch event to the given item qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; @@ -2446,7 +2440,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - if (deliverTouchAsMouse(item, event)) + if (deliverTouchAsMouse(item, ptEvent)) eventAccepted = true; } @@ -2454,7 +2448,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // If the touch was accepted (regardless by whom or in what form), // update accepted new points. for (auto point: qAsConst(touchEvent->touchPoints())) { - auto pointerEventPoint = event->pointById(point.id()); + auto pointerEventPoint = ptEvent->pointById(point.id()); pointerEventPoint->setAccepted(); if (point.state() == Qt::TouchPointPressed) pointerEventPoint->setGrabberItem(item); @@ -2464,9 +2458,9 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // will not be interested in further updates for those touchpoint IDs either. for (auto point: qAsConst(touchEvent->touchPoints())) { if (point.state() == Qt::TouchPointPressed) { - if (event->pointById(point.id())->grabber() == item) { + if (ptEvent->pointById(point.id())->grabber() == item) { qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; - event->pointById(point.id())->setGrabberItem(nullptr); + ptEvent->pointById(point.id())->setGrabberItem(nullptr); } } } @@ -2645,82 +2639,6 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif -// TODO assimilate this logic and remove this function -bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet *hasFiltered) -{ - Q_Q(QQuickWindow); - - if (!target) - return false; - - bool filtered = false; - - QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); - if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { - hasFiltered->insert(target); - QScopedPointer targetEvent(event->touchEventForItem(target, true)); - if (targetEvent) { - if (target->childMouseEventFilter(item, targetEvent.data())) { - qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target; - QVector touchIds; - const int touchPointCount = targetEvent->touchPoints().size(); - touchIds.reserve(touchPointCount); - for (int i = 0; i < touchPointCount; ++i) - touchIds.append(targetEvent->touchPoints().at(i).id()); - target->grabTouchPoints(touchIds); - if (q->mouseGrabberItem()) { - q->mouseGrabberItem()->ungrabMouse(); - touchMouseId = -1; - touchMouseDevice = nullptr; - } - filtered = true; - } - - for (int i = 0; i < targetEvent->touchPoints().size(); ++i) { - const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().at(i); - - QEvent::Type t; - switch (tp.state()) { - case Qt::TouchPointPressed: - t = QEvent::MouseButtonPress; - break; - case Qt::TouchPointReleased: - t = QEvent::MouseButtonRelease; - break; - case Qt::TouchPointStationary: - continue; - default: - t = QEvent::MouseMove; - break; - } - - // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId - if (touchMouseId == -1 || touchMouseId == tp.id()) { - // targetEvent is already transformed wrt local position, velocity, etc. - - // FIXME: remove asTouchEvent!!! - QScopedPointer mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false)); - if (target->childMouseEventFilter(item, mouseEvent.data())) { - qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target; - if (t != QEvent::MouseButtonRelease) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; - touchMouseId = tp.id(); - touchMouseDevice = event->device(); - touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabberItem(target); - target->grabMouse(); - } - filtered = true; - } - // Only one event can be filtered as a mouse event. - break; - } - } - } - } - - return sendFilteredTouchEvent(target->parentItem(), item, event, hasFiltered) || filtered; -} - void QQuickWindowPrivate::updateFilteringParentItems(const QVector &targetItems) { if (Q_UNLIKELY(DBG_MOUSE_TARGET().isDebugEnabled())) { @@ -2770,16 +2688,61 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ } } } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { - QTouchEvent *te = pte->asTouchEvent(); for (QPair itemAndParent : filteringParentItems) { QQuickItem *item = receiver ? receiver : itemAndParent.first; QQuickItem *filteringParent = itemAndParent.second; if (item == filteringParent) continue; // a filtering item never needs to filter for itself - if (filteringParent->childMouseEventFilter(item, te)) { - for (auto point: qAsConst(te->touchPoints())) + // get a touch event customized for delivery to filteringParent + QScopedPointer filteringParentTouchEvent(pte->touchEventForItem(filteringParent, true)); + if (!filteringParentTouchEvent) + continue; // all points are stationary, or no touchpoints inside that parent + if (filteringParent->childMouseEventFilter(item, filteringParentTouchEvent.data())) { + qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent; + for (auto point: qAsConst(filteringParentTouchEvent->touchPoints())) event->pointById(point.id())->setAccepted(); - return true; + ret = true; + break; + } + // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event. + for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) { + const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i); + + QEvent::Type t; + switch (tp.state()) { + case Qt::TouchPointPressed: + t = QEvent::MouseButtonPress; + break; + case Qt::TouchPointReleased: + t = QEvent::MouseButtonRelease; + break; + case Qt::TouchPointStationary: + continue; + default: + t = QEvent::MouseMove; + break; + } + + // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId + if (touchMouseId == -1 || touchMouseId == tp.id()) { + // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.) + // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that + QScopedPointer mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), item, false)); + if (filteringParent->childMouseEventFilter(item, mouseEvent.data())) { + qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent; + if (t != QEvent::MouseButtonRelease) { + qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << filteringParent; + touchMouseId = tp.id(); + touchMouseDevice = event->device(); + touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabberItem(filteringParent); + filteringParent->grabMouse(); + } + ret = true; + } + // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter + // has been called once, we're done with this loop over the touchpoints. + break; + } } } } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 38c1b0a4d4..b0d4d1ce01 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -168,10 +168,9 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPressEvent(QQuickPointerEvent *, QSet *); - bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered); - bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet *filtered); - bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet *filtered); + bool deliverPressEvent(QQuickPointerEvent *); + bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event); + bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent); QVector pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; QVector mergePointerTargets(const QVector &list1, const QVector &list2) const; -- cgit v1.2.3