aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickwindow.cpp
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2017-08-29 10:12:32 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2017-09-08 13:17:41 +0000
commit19054b5449755e27d54793492c41095cc630cb91 (patch)
treedaa02000845edd82cfcd3c83f1cb6cfa57612136 /src/quick/items/qquickwindow.cpp
parentf8ff08e31147a6ecf87d830b1cf632f64d19aab1 (diff)
sendFilteredPointerEvent: go back to parent traversal
The filteringParentItems vector-of-pairs is getting more and more questionable: it needs to have more pairs than we thought, then we need to remove them to prevent multiple-filtering, and then we also need to keep track of which ones we removed plus which ones are actually filtering, in order to avoid direct delivery of pointer events to the same Item after it already filtered from its children. The overhead doesn't seem worthwhile anymore. So this conceptually reverts 9b5fc80af28580e9672792dd511d876a93947882 but with some improvements. Also, do not deliver events to child-filtering items which have already filtered. Change-Id: I9d4f977dba695de7eb78ab536e0e6e8fd6a253a7 Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
Diffstat (limited to 'src/quick/items/qquickwindow.cpp')
-rw-r--r--src/quick/items/qquickwindow.cpp231
1 files changed, 100 insertions, 131 deletions
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index eb90e92b96..21f06febde 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -2427,25 +2427,24 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event,
}
}
- if (allowChildEventFiltering && !handlersOnly)
- updateFilteringParentItems(targetItems);
-
QVarLengthArray<QQuickItem *> filteredItems;
for (QQuickItem *item : targetItems) {
- if (sendFilteredPointerEvent(event, item)) {
+ if (!handlersOnly && sendFilteredPointerEvent(event, item)) {
if (event->isAccepted()) {
for (int i = 0; i < event->pointCount(); ++i)
event->point(i)->setAccepted();
return true;
}
- while (!filteringParentItems.isEmpty() && filteringParentItems.first().first == item)
- filteringParentItems.removeFirst();
filteredItems << item;
}
// Do not deliverMatchingPointsTo any item for which the parent-filter already intercepted the event
if (filteredItems.contains(item))
continue;
+ // Do not deliverMatchingPointsTo any item which already had a chance to filter
+ // e.g. if Flickable has filtered events from one of its children, it does not need normal delivery
+ if (hasFiltered.contains(item))
+ continue;
deliverMatchingPointsToItem(item, event, handlersOnly);
if (event->allPointsAccepted())
break;
@@ -2515,8 +2514,6 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
bool eventAccepted = false;
// 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;
@@ -2728,147 +2725,119 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
}
#endif
-void QQuickWindowPrivate::updateFilteringParentItems(const QVector<QQuickItem *> &targetItems)
+bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
- filteringParentItems.clear();
- for (QQuickItem *item : targetItems) {
-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
- bool acceptsTouchEvents = item->acceptTouchEvents();
-#else
- // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true.
- bool acceptsTouchEvents = false;
-#endif
- QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
- // If the item neither handles events nor has handlers which do, then it will never be a receiver, so filtering is irrelevant.
- if (!item->acceptedMouseButtons() && !acceptsTouchEvents &&
- !(itemPriv->extra.isAllocated() && !itemPriv->extra->pointerHandlers.isEmpty()))
- continue;
- QQuickItem *parent = item->parentItem();
- while (parent) {
- if (parent->filtersChildMouseEvents()) {
- bool foundParent = false;
- for (const QPair<QQuickItem*,QQuickItem*> existingItemAndParent : filteringParentItems)
- if (existingItemAndParent.second == parent)
- foundParent = true;
- if (!foundParent)
- filteringParentItems.append(QPair<QQuickItem*,QQuickItem*>(item, parent));
- }
- parent = parent->parentItem();
- }
- }
- if (Q_UNLIKELY(DBG_MOUSE_TARGET().isDebugEnabled())) {
- QStringList l;
- for (QPair<QQuickItem *,QQuickItem *> pair : filteringParentItems)
- l << (QLatin1String(pair.first->metaObject()->className()) + QLatin1Char(' ') + pair.first->objectName() +
- QLatin1String(" <- ") + pair.second->metaObject()->className()) + QLatin1Char(' ') + pair.second->objectName();
- qCDebug(DBG_MOUSE_TARGET) << "potential filtering parents:" << l;
- }
+ hasFiltered.clear();
+ return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem());
}
-bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver)
+bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
if (!allowChildEventFiltering)
return false;
- bool ret = false;
- QVarLengthArray<QQuickItem *> filteringParentsToSkip;
- if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) {
- for (QPair<QQuickItem *,QQuickItem *> 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 (filteringParentsToSkip.contains(filteringParent))
- continue;
- QPointF localPos = item->mapFromScene(pme->point(0)->scenePosition());
- QMouseEvent *me = pme->asMouseEvent(localPos);
- if (filteringParent->childMouseEventFilter(item, me))
- ret = true;
- filteringParentsToSkip.append(filteringParent);
- }
- } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) {
- QVarLengthArray<QPair<QQuickPointerHandler *, QQuickEventPoint *>, 32> passiveGrabsToCancel;
- for (QPair<QQuickItem *,QQuickItem *> itemAndParent : filteringParentItems) {
- QQuickItem *item = receiver ? receiver : itemAndParent.first;
- if (item != itemAndParent.first)
- continue;
- if (!item->acceptTouchEvents() && !item->acceptedMouseButtons())
- continue; // if this item won't accept, parents don't need to filter the touch for it
- QQuickItem *filteringParent = itemAndParent.second;
- if (item == filteringParent)
- continue; // a filtering item never needs to filter for itself
- // get a touch event customized for delivery to filteringParent
- QScopedPointer<QTouchEvent> 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();
- ret = true;
- break;
+ if (!filteringParent)
+ return false;
+ bool filtered = false;
+ if (filteringParent->filtersChildMouseEvents() && !hasFiltered.contains(filteringParent)) {
+ hasFiltered.append(filteringParent);
+ if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) {
+ if (receiver->acceptedMouseButtons()) {
+ QPointF localPos = receiver->mapFromScene(pme->point(0)->scenePosition());
+ QMouseEvent *me = pme->asMouseEvent(localPos);
+ if (filteringParent->childMouseEventFilter(receiver, me))
+ filtered = true;
}
- // 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;
- }
-
- bool touchMouseUnset = (touchMouseId == -1);
- // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
- if (touchMouseUnset || 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<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), item, false));
- // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should
- // report the touchpoint's grabber. Whenever we send a synthetic mouse event,
- // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed.
- touchMouseId = tp.id();
- touchMouseDevice = event->device();
- 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 (mouse)" << hex << tp.id() << "->" << filteringParent;
- pointerEventInstance(touchMouseDevice)->pointById(tp.id())->setGrabberItem(filteringParent);
- touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
- filteringParent->grabMouse();
- auto pointerEventPoint = pte->pointById(tp.id());
+ } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ bool acceptsTouchEvents = receiver->acceptTouchEvents();
+#else
+ // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true.
+ bool acceptsTouchEvents = false;
+#endif
+ if (acceptsTouchEvents || receiver->acceptedMouseButtons()) {
+ // get a touch event customized for delivery to filteringParent
+ QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(filteringParent, true));
+ if (filteringParentTouchEvent) {
+ QVarLengthArray<QPair<QQuickPointerHandler *, QQuickEventPoint *>, 32> passiveGrabsToCancel;
+ if (filteringParent->childMouseEventFilter(receiver, filteringParentTouchEvent.data())) {
+ qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
+ filteringParent->grabMouse();
+ for (auto point: qAsConst(filteringParentTouchEvent->touchPoints())) {
+ auto pointerEventPoint = pte->pointById(point.id());
for (auto handler : pointerEventPoint->passiveGrabbers()) {
QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint);
if (!passiveGrabsToCancel.contains(grab))
passiveGrabsToCancel.append(grab);
}
+ event->pointById(point.id())->setAccepted();
+ }
+ return true;
+ } else {
+ // 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;
+ }
+
+ bool touchMouseUnset = (touchMouseId == -1);
+ // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
+ if (touchMouseUnset || 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<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), receiver, false));
+ // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should
+ // report the touchpoint's grabber. Whenever we send a synthetic mouse event,
+ // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed.
+ touchMouseId = tp.id();
+ touchMouseDevice = event->device();
+ if (filteringParent->childMouseEventFilter(receiver, 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 (mouse)" << hex << tp.id() << "->" << filteringParent;
+ pointerEventInstance(touchMouseDevice)->pointById(tp.id())->setGrabberItem(filteringParent);
+ touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
+ filteringParent->grabMouse();
+ auto pointerEventPoint = pte->pointById(tp.id());
+ for (auto handler : pointerEventPoint->passiveGrabbers()) {
+ QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint);
+ if (!passiveGrabsToCancel.contains(grab))
+ passiveGrabsToCancel.append(grab);
+ }
+ }
+ filtered = true;
+ }
+ if (touchMouseUnset) {
+ // Now that we're done sending a synth mouse event, and it wasn't grabbed,
+ // the touchpoint is no longer acting as a synthetic mouse. Restore previous state.
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
+ }
+ // 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;
+ }
}
- ret = true;
- }
- if (touchMouseUnset) {
- // Now that we're done sending a synth mouse event, and it wasn't grabbed,
- // the touchpoint is no longer acting as a synthetic mouse. Restore previous state.
- touchMouseId = -1;
- touchMouseDevice = nullptr;
}
- // 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;
+ for (auto grab : passiveGrabsToCancel)
+ grab.second->cancelPassiveGrab(grab.first);
}
}
}
- for (auto grab : passiveGrabsToCancel)
- grab.second->cancelPassiveGrab(grab.first);
}
- return ret;
+ return sendFilteredPointerEventImpl(event, receiver, filteringParent->parentItem()) || filtered;
}
bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event, QSet<QQuickItem *> *hasFiltered)