diff options
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 3 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 84 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 5 | ||||
-rw-r--r-- | tests/auto/qmltest/events/tst_touch.qml | 10 | ||||
-rw-r--r-- | tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml | 3 |
5 files changed, 102 insertions, 3 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 45bb2a367d..a09ffff816 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1557,6 +1557,8 @@ void QQuickFlickablePrivate::replayDelayedPress() // If we have the grab, release before delivering the event if (QQuickWindow *w = q->window()) { + QQuickWindowPrivate *wpriv = QQuickWindowPrivate::get(w); + wpriv->allowChildEventFiltering = false; // don't allow re-filtering during replay replayingPressEvent = true; if (w->mouseGrabberItem() == q) q->ungrabMouse(); @@ -1564,6 +1566,7 @@ void QQuickFlickablePrivate::replayDelayedPress() // Use the event handler that will take care of finding the proper item to propagate the event QCoreApplication::sendEvent(w, mouseEvent.data()); replayingPressEvent = false; + wpriv->allowChildEventFiltering = true; } } } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index fa3093c245..83aff5e07e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -496,6 +496,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , persistentSceneGraph(true) , lastWheelEventAccepted(false) , componentCompleted(true) + , allowChildEventFiltering(true) , lastFocusReason(Qt::OtherFocusReason) , renderTarget(0) , renderTargetId(0) @@ -1663,11 +1664,16 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven return; } + if (allowChildEventFiltering) { + if (sendFilteredPointerEvent(pointerEvent, grabber)) + return; + } + // send update QPointF localPos = grabber->mapFromScene(lastMousePosition); auto me = pointerEvent->asMouseEvent(localPos); me->accept(); - q->sendEvent(grabber, me); + QCoreApplication::sendEvent(grabber, me); point->setAccepted(me->isAccepted()); // release event, make sure to ungrab if there still is a grabber @@ -1887,7 +1893,7 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) for (QObject *grabber: qAsConst(grabbers)) { if (QQuickItem *grabberItem = qmlobject_cast<QQuickItem *>(grabber)) - q->sendEvent(grabberItem, event); + QCoreApplication::sendEvent(grabberItem, event); else //if (QQuickPointerHandler *grabberHandler = qmlobject_cast<QQuickPointerHandler *>(grabber)) // grabberHandler->handlePointerEvent() qWarning("unexpected: can't deliver touch cancel to a PointerHandler (yet?)"); @@ -2255,6 +2261,8 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber); if (!receiver) receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem(); + if (allowChildEventFiltering && sendFilteredPointerEvent(event, receiver)) + return true; deliverMatchingPointsToItem(receiver, event, hasFiltered); } @@ -2275,6 +2283,12 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui } } + if (allowChildEventFiltering) { + updateFilteringParentItems(targetItems); + if (sendFilteredPointerEvent(event, nullptr)) + return true; + } + for (QQuickItem *item: targetItems) { deliverMatchingPointsToItem(item, event, hasFiltered); if (event->allPointsAccepted()) @@ -2308,7 +2322,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo Q_ASSERT(item->contains(localPos)); // transform is checked already QMouseEvent *me = event->asMouseEvent(localPos); me->accept(); - q->sendEvent(item, me); + QCoreApplication::sendEvent(item, me); if (me->isAccepted()) { auto mouseGrabber = q->mouseGrabberItem(); if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) { @@ -2553,6 +2567,7 @@ 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<QQuickItem *> *hasFiltered) { Q_Q(QQuickWindow); @@ -2628,6 +2643,69 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem return sendFilteredTouchEvent(target->parentItem(), item, event, hasFiltered) || filtered; } +void QQuickWindowPrivate::updateFilteringParentItems(const QVector<QQuickItem *> &targetItems) +{ + if (Q_UNLIKELY(DBG_MOUSE_TARGET().isDebugEnabled())) { + // qDebug() << map(&objectName, targetItems) but done the hard way because C++ is still that primitive + QStringList targetNames; + for (QQuickItem *t : targetItems) + targetNames << (QLatin1String(t->metaObject()->className()) + QLatin1Char(' ') + t->objectName()); + qCDebug(DBG_MOUSE_TARGET) << "finding potential filtering parents of" << targetNames; + } + filteringParentItems.clear(); + for (QQuickItem *item : targetItems) { + 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() && !(itemPriv->extra.isAllocated() && !itemPriv->extra->pointerHandlers.isEmpty())) + continue; // TODO there's no acceptTouchEvents, so it's hard to avoid skipping any items which handle only touch + 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(); + } + } +} + +bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver) +{ + bool ret = false; + 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 + QPointF localPos = item->mapFromScene(pme->point(0)->scenePos()); + QMouseEvent *me = pme->asMouseEvent(localPos); + if (filteringParent->childMouseEventFilter(item, me)) { + event->setAccepted(true); + ret = true; + } + } + } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { + QTouchEvent *te = pte->asTouchEvent(); + 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 (filteringParent->childMouseEventFilter(item, te)) { + for (auto point: qAsConst(te->touchPoints())) + event->pointById(point.id())->setAccepted(); + return true; + } + } + } + return ret; +} + bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event, QSet<QQuickItem *> *hasFiltered) { if (!target) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 30e3b71d0a..38c1b0a4d4 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -148,6 +148,7 @@ public: static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *); + bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver); #if QT_CONFIG(wheelevent) bool deliverWheelEvent(QQuickItem *, QWheelEvent *); #endif @@ -174,6 +175,7 @@ public: QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const; + void updateFilteringParentItems(const QVector<QQuickItem *> &targetItems); // hover delivery bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted); @@ -222,6 +224,7 @@ public: QList<QSGNode *> cleanupNodeList; QVector<QQuickItem *> itemsToPolish; + QVector<QPair<QQuickItem *,QQuickItem *> > filteringParentItems; // item:parent pairs qreal devicePixelRatio; QMetaObject::Connection physicalDpiChangedConnection; @@ -259,6 +262,8 @@ public: uint lastWheelEventAccepted : 1; bool componentCompleted : 1; + bool allowChildEventFiltering : 1; + Qt::FocusReason lastFocusReason; QOpenGLFramebufferObject *renderTarget; diff --git a/tests/auto/qmltest/events/tst_touch.qml b/tests/auto/qmltest/events/tst_touch.qml index 5b209a6d0b..fd603e5a71 100644 --- a/tests/auto/qmltest/events/tst_touch.qml +++ b/tests/auto/qmltest/events/tst_touch.qml @@ -35,6 +35,16 @@ MultiPointTouchArea { width: 100 height: 100 + // touchUpdatedSpy stores the QQuickTouchPoint, and in some cases + // MultiPointTouchArea can delete it out from under us. + // (test_simpleChain was failing because touchUpdatedSpy.signalArguments[0][0][0] + // ended up as an empty object somehow.) If we declare + // all the touchpoints that this test will use, that won't happen. + touchPoints: [ + TouchPoint { }, + TouchPoint { } + ] + SignalSpy { id: touchUpdatedSpy target: touchArea diff --git a/tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml b/tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml index 81187f3c2f..902920babc 100644 --- a/tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml +++ b/tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml @@ -18,6 +18,8 @@ Flickable { height: 300 color: "yellow" + objectName: "yellowRect" + Flickable { id: inner objectName: "innerFlickable" @@ -30,6 +32,7 @@ Flickable { Rectangle { anchors.fill: parent anchors.margins: 100 + objectName: "blueRect" color: "blue" } MouseArea { |