From 566afc2d2e4156712ffec081715f12307cf46628 Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Mon, 12 Aug 2013 13:28:17 +1000 Subject: [PATCH] Dragging MouseArea nested in Flickable does not work with touch to mouse A MouseArea with a drag target nested in a Flickable does not work. This is due to QQuickWindow calling childMouseEventFilter() twice - once in sendFilteredTouchEvent() and later in sendEvent(). Since childMouseEventFilter() has already been called, deliver the mouse event directly in sendFilteredTouchEvent(). Task-number: QTBUG-32920 Change-Id: I22acee3c66ee6c06e71c9c876fb02dbcb6119a8d Reviewed-by: Andrew den Exter --- src/quick/items/qquickflickable.cpp | 6 +- src/quick/items/qquickwindow.cpp | 8 +-- .../quick/qquickflickable/tst_qquickflickable.cpp | 69 +++++++++++++--------- .../quick/qquickmousearea/tst_qquickmousearea.cpp | 13 ++-- tests/auto/quick/touchmouse/tst_touchmouse.cpp | 3 + 5 files changed, 58 insertions(+), 41 deletions(-) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 46f95f1..8fef1c9 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -982,6 +982,7 @@ void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event) q->setKeepMouseGrab(stealMouse); clearDelayedPress(); pressed = true; + if (hData.transitionToBounds) hData.transitionToBounds->stopTransition(); if (vData.transitionToBounds) @@ -2095,7 +2096,8 @@ bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event) d->lastPosTime = -1; returnToBounds(); } - if (event->type() == QEvent::MouseButtonRelease) { + if (event->type() == QEvent::MouseButtonRelease || (grabber && grabber->keepMouseGrab() && !grabberDisabled)) { + // mouse released, or another item has claimed the grab d->lastPosTime = -1; d->clearDelayedPress(); d->stealMouse = false; @@ -2108,7 +2110,7 @@ bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event) bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e) { Q_D(QQuickFlickable); - if (!isVisible() || !isEnabled()) + if (!isVisible() || !isEnabled() || !isInteractive()) return QQuickItem::childMouseEventFilter(i, e); switch (e->type()) { case QEvent::MouseButtonPress: diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 525c3d1..0ab929f 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -484,7 +484,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e item->grabMouse(); item->grabTouchPoints(QVector() << touchMouseId); - q->sendEvent(item, mousePress.data()); + QQuickItemPrivate::get(item)->deliverMouseEvent(mousePress.data()); event->setAccepted(mousePress->isAccepted()); if (!mousePress->isAccepted()) { touchMouseId = -1; @@ -497,7 +497,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) { QScopedPointer mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item)); - q->sendEvent(item, mouseDoubleClick.data()); + QQuickItemPrivate::get(item)->deliverMouseEvent(mouseDoubleClick.data()); event->setAccepted(mouseDoubleClick->isAccepted()); if (mouseDoubleClick->isAccepted()) { return true; @@ -518,7 +518,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e if (p.state() & Qt::TouchPointMoved) { if (mouseGrabberItem) { QScopedPointer me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem)); - q->sendEvent(mouseGrabberItem, me.data()); + QQuickItemPrivate::get(item)->deliverMouseEvent(me.data()); event->setAccepted(me->isAccepted()); if (me->isAccepted()) { itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent() @@ -548,7 +548,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e touchMouseId = -1; if (mouseGrabberItem) { QScopedPointer me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem)); - q->sendEvent(mouseGrabberItem, me.data()); + QQuickItemPrivate::get(item)->deliverMouseEvent(me.data()); if (mouseGrabberItem) // might have ungrabbed due to event mouseGrabberItem->ungrabMouse(); return me->isAccepted(); diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index a8055b3..dd9fa47 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -94,9 +94,10 @@ private slots: void flickTwiceUsingTouches(); void nestedStopAtBounds(); void nestedStopAtBounds_data(); + void nestedMouseAreaUsingTouch(); private: - void flickWithTouch(QWindow *window, QTouchDevice *touchDevice); + void flickWithTouch(QWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to); QQmlEngine engine; }; @@ -1268,13 +1269,13 @@ void tst_qquickflickable::flickTwiceUsingTouches() QVERIFY(flickable != 0); QCOMPARE(flickable->contentY(), 0.0f); - flickWithTouch(window, touchDevice); + flickWithTouch(window, touchDevice, QPoint(100, 400), QPoint(100, 240)); qreal contentYAfterFirstFlick = flickable->contentY(); qDebug() << "contentYAfterFirstFlick " << contentYAfterFirstFlick; QVERIFY(contentYAfterFirstFlick > 50.0f); - flickWithTouch(window, touchDevice); + flickWithTouch(window, touchDevice, QPoint(100, 400), QPoint(100, 240)); // In the original bug, that second flick would cause Flickable to halt immediately qreal contentYAfterSecondFlick = flickable->contentY(); @@ -1284,34 +1285,19 @@ void tst_qquickflickable::flickTwiceUsingTouches() delete window; } -void tst_qquickflickable::flickWithTouch(QWindow *window, QTouchDevice *touchDevice) +void tst_qquickflickable::flickWithTouch(QWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to) { QTest::touchEvent(window, touchDevice) - .press(0, QPoint(100, 400), window); - QTest::qWait(1); - QTest::touchEvent(window, touchDevice) - .move(0, QPoint(100, 380), window); - QTest::qWait(1); - QTest::touchEvent(window, touchDevice) - .move(0, QPoint(100, 360), window); - QTest::qWait(1); - QTest::touchEvent(window, touchDevice) - .move(0, QPoint(100, 340), window); - QTest::qWait(1); - QTest::touchEvent(window, touchDevice) - .move(0, QPoint(100, 320), window); - QTest::qWait(1); - QTest::touchEvent(window, touchDevice) - .move(0, QPoint(100, 300), window); - QTest::qWait(1); - QTest::touchEvent(window, touchDevice) - .move(0, QPoint(100, 280), window); - QTest::qWait(1); - QTest::touchEvent(window, touchDevice) - .move(0, QPoint(100, 260), window); + .press(0, from, window); QTest::qWait(1); + QPoint diff = to - from; + for (int i = 1; i <= 8; ++i) { + QTest::touchEvent(window, touchDevice) + .move(0, from + i*diff/8, window); + QTest::qWait(1); + } QTest::touchEvent(window, touchDevice) - .release(0, QPoint(100, 240), window); + .release(0, to, window); QTest::qWait(1); } @@ -1384,6 +1370,35 @@ void tst_qquickflickable::nestedStopAtBounds() QTRY_VERIFY(!outer->isMoving()); } +void tst_qquickflickable::nestedMouseAreaUsingTouch() +{ + QTouchDevice *touchDevice = new QTouchDevice; + touchDevice->setName("Fake Touchscreen"); + touchDevice->setType(QTouchDevice::TouchScreen); + touchDevice->setCapabilities(QTouchDevice::Position); + QWindowSystemInterface::registerTouchDevice(touchDevice); + + QQuickView *window = new QQuickView; + window->setSource(testFileUrl("nestedmousearea.qml")); + window->show(); + QVERIFY(window->rootObject() != 0); + + QQuickFlickable *flickable = qobject_cast(window->rootObject()); + QVERIFY(flickable != 0); + + QCOMPARE(flickable->contentY(), 50.0f); + flickWithTouch(window, touchDevice, QPoint(100, 300), QPoint(100, 200)); + + // flickable should not have moved + QCOMPARE(flickable->contentY(), 50.0); + + // draggable item should have moved up + QQuickItem *nested = window->rootObject()->findChild("nested"); + QVERIFY(nested->y() < 100.0); + + delete window; +} + QTEST_MAIN(tst_qquickflickable) #include "tst_qquickflickable.moc" diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index a582c62..f92612e 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -1198,18 +1198,15 @@ void tst_QQuickMouseArea::transformedMouseArea() foreach (const QPoint &point, points) { // check hover QTest::mouseMove(window, point); - QTest::qWait(10); - QCOMPARE(mouseArea->property("containsMouse").toBool(), insideTarget); + QTRY_COMPARE(mouseArea->property("containsMouse").toBool(), insideTarget); // check mouse press QTest::mousePress(window, Qt::LeftButton, 0, point); - QTest::qWait(10); - QCOMPARE(mouseArea->property("pressed").toBool(), insideTarget); + QTRY_COMPARE(mouseArea->property("pressed").toBool(), insideTarget); // check mouse release QTest::mouseRelease(window, Qt::LeftButton, 0, point); - QTest::qWait(10); - QCOMPARE(mouseArea->property("pressed").toBool(), false); + QTRY_COMPARE(mouseArea->property("pressed").toBool(), false); } delete window; @@ -1514,8 +1511,8 @@ void tst_QQuickMouseArea::nestedStopAtBounds() QTest::mouseMove(&view, position); axis += invert ? -threshold : threshold; QTest::mouseMove(&view, position); - QCOMPARE(outer->drag()->active(), false); - QCOMPARE(inner->drag()->active(), true); + QTRY_COMPARE(outer->drag()->active(), false); + QTRY_COMPARE(inner->drag()->active(), true); QTest::mouseRelease(&view, Qt::LeftButton, 0, position); } diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 4779942..6cf0aa4 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -867,6 +867,9 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() QGuiApplication::processEvents(); //QVERIFY(flickable->isMovingHorizontally()); + + // Wait for flick to end + QTRY_VERIFY(!flickable->isMoving()); qDebug() << "Pos: " << rect->position(); // pinch -- 2.7.4