Dragging MouseArea nested in Flickable does not work with touch to mouse
authorMartin Jones <martin.jones@jollamobile.com>
Mon, 12 Aug 2013 03:28:17 +0000 (13:28 +1000)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 19 Aug 2013 00:50:29 +0000 (02:50 +0200)
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 <andrew.den.exter@qinetic.com.au>
src/quick/items/qquickflickable.cpp
src/quick/items/qquickwindow.cpp
tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
tests/auto/quick/touchmouse/tst_touchmouse.cpp

index 46f95f1..8fef1c9 100644 (file)
@@ -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:
index 525c3d1..0ab929f 100644 (file)
@@ -484,7 +484,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
                 item->grabMouse();
             item->grabTouchPoints(QVector<int>() << 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<QMouseEvent> 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<QMouseEvent> 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<QMouseEvent> 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();
index a8055b3..dd9fa47 100644 (file)
@@ -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<QQuickFlickable*>(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<QQuickItem*>("nested");
+    QVERIFY(nested->y() < 100.0);
+
+    delete window;
+}
+
 QTEST_MAIN(tst_qquickflickable)
 
 #include "tst_qquickflickable.moc"
index a582c62..f92612e 100644 (file)
@@ -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);
 }
 
index 4779942..6cf0aa4 100644 (file)
@@ -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