aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquickflickable.cpp3
-rw-r--r--src/quick/items/qquickwindow.cpp84
-rw-r--r--src/quick/items/qquickwindow_p.h5
-rw-r--r--tests/auto/qmltest/events/tst_touch.qml10
-rw-r--r--tests/auto/quick/qquickflickable/data/nestedStopAtBounds.qml3
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 {