aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickwindow.cpp
diff options
context:
space:
mode:
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)