diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index cda8e62587..59e1f65e7b 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1521,13 +1521,14 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event) if (d->delayedPressEvent) { d->replayDelayedPress(); - // Now send the release - if (auto grabber = qmlobject_cast(event->exclusiveGrabber(event->point(0)))) { - // not copying or detaching anything, so make sure we return the original event unchanged - const auto oldPosition = event->point(0).position(); - QMutableEventPoint::setPosition(event->point(0), grabber->mapFromScene(event->scenePosition())); + auto &firstPoint = event->point(0); + if (const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) { + // Since we sent the delayed press to the window, we need to resend the release to the window too. + // We're not copying or detaching, so restore the original event position afterwards. + const auto oldPosition = firstPoint.position(); + QMutableEventPoint::setPosition(firstPoint, event->scenePosition()); QCoreApplication::sendEvent(window(), event); - QMutableEventPoint::setPosition(event->point(0), oldPosition); + QMutableEventPoint::setPosition(firstPoint, oldPosition); } // And the event has been consumed @@ -1580,11 +1581,11 @@ void QQuickFlickable::touchEvent(QTouchEvent *event) if (d->delayedPressEvent) { d->replayDelayedPress(); - // Now send the release - auto &firstPoint = event->point(0); - if (auto grabber = qmlobject_cast(event->exclusiveGrabber(firstPoint))) { - const auto localPos = grabber->mapFromScene(firstPoint.scenePosition()); - QScopedPointer localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos)); + const auto &firstPoint = event->point(0); + if (const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) { + // Since we sent the delayed press to the window, we need to resend the release to the window too. + QScopedPointer localizedEvent( + QQuickDeliveryAgentPrivate::clonePointerEvent(event, firstPoint.scenePosition())); QCoreApplication::sendEvent(window(), localizedEvent.data()); } diff --git a/tests/auto/quick/qquickflickable/data/pressDelay.qml b/tests/auto/quick/qquickflickable/data/pressDelay.qml index a69c4af6de..97bc6b794f 100644 --- a/tests/auto/quick/qquickflickable/data/pressDelay.qml +++ b/tests/auto/quick/qquickflickable/data/pressDelay.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick Flickable { flickableDirection: Flickable.VerticalFlick @@ -15,7 +15,6 @@ Flickable { MouseArea { id: ma - objectName: "mouseArea" y: 100 anchors.horizontalCenter: parent.horizontalCenter width: 240 @@ -32,14 +31,7 @@ Flickable { Rectangle { anchors.fill: parent - color: parent.pressed ? 'blue' : 'green' - - Text { - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: 'Hello' - } + color: parent.pressed ? 'blue' : 'lightsteelblue' } } } diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 37810c967d..a9019683c3 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -165,6 +165,7 @@ private slots: void rebound(); void maximumFlickVelocity(); void flickDeceleration(); + void pressDelay_data(); void pressDelay(); void nestedPressDelay(); void filterReplayedPress(); @@ -229,6 +230,10 @@ private slots: private: void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to); QPointingDevice *touchDevice = QTest::createTouchDevice(); + const QPointingDevice *mouseDevice = new QPointingDevice( + "test mouse", 1000, QInputDevice::DeviceType::Mouse, QPointingDevice::PointerType::Generic, + QInputDevice::Capability::Position | QInputDevice::Capability::Hover | QInputDevice::Capability::Scroll, + 1, 5, QString(), QPointingDeviceUniqueId(), this); }; void tst_qquickflickable::initTestCase() @@ -238,6 +243,8 @@ void tst_qquickflickable::initTestCase() #endif QQmlDataTest::initTestCase(); qmlRegisterType("Test",1,0,"TouchDragArea"); + touchDevice->setParent(this); // avoid leak + QWindowSystemInterface::registerInputDevice(mouseDevice); } void tst_qquickflickable::cleanup() @@ -523,45 +530,54 @@ void tst_qquickflickable::flickDeceleration() delete flickable; } +void tst_qquickflickable::pressDelay_data() +{ + QTest::addColumn("device"); + const QPointingDevice *constTouchDevice = touchDevice; + + QTest::newRow("mouse") << mouseDevice; + QTest::newRow("touch") << constTouchDevice; +} + void tst_qquickflickable::pressDelay() { - QScopedPointer window(new QQuickView); - window->setSource(testFileUrl("pressDelay.qml")); - QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtils::centerOnScreen(window.data()); - QQuickViewTestUtils::moveMouseAway(window.data()); - window->show(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); - QVERIFY(window->rootObject() != nullptr); + QFETCH(const QPointingDevice *, device); - QQuickFlickable *flickable = qobject_cast(window->rootObject()); - QSignalSpy spy(flickable, SIGNAL(pressDelayChanged())); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("pressDelay.qml"))); + QQuickFlickable *flickable = qobject_cast(window.rootObject()); QVERIFY(flickable); - QCOMPARE(flickable->pressDelay(), 100); + QQuickMouseArea *mouseArea = flickable->findChild(); + QSignalSpy clickedSpy(mouseArea, &QQuickMouseArea::clicked); + // Test the pressDelay property itself + QSignalSpy pressDelayChangedSpy(flickable, &QQuickFlickable::pressDelayChanged); + QCOMPARE(flickable->pressDelay(), 100); flickable->setPressDelay(200); QCOMPARE(flickable->pressDelay(), 200); - QCOMPARE(spy.size(),1); + QCOMPARE(pressDelayChangedSpy.size(), 1); flickable->setPressDelay(200); - QCOMPARE(spy.size(),1); + QCOMPARE(pressDelayChangedSpy.size(), 1); - QQuickItem *mouseArea = window->rootObject()->findChild("mouseArea"); - QSignalSpy clickedSpy(mouseArea, SIGNAL(clicked(QQuickMouseEvent*))); - moveAndPress(window.data(), QPoint(150, 150)); + // Test the press delay + QPoint p(150, 150); + if (device->type() == QInputDevice::DeviceType::Mouse) + QQuickTest::pointerMove(device, &window, 0, p); + QQuickTest::pointerPress(device, &window, 0, p); // The press should not occur immediately - QVERIFY(!mouseArea->property("pressed").toBool()); + QCOMPARE(mouseArea->pressed(), false); // But, it should occur eventually - QTRY_VERIFY(mouseArea->property("pressed").toBool()); + QTRY_VERIFY(mouseArea->pressed()); - QCOMPARE(clickedSpy.size(),0); + QCOMPARE(clickedSpy.size(), 0); // On release the clicked signal should be emitted - QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(150, 150)); - QCOMPARE(clickedSpy.size(),1); + QQuickTest::pointerRelease(device, &window, 0, p); + QCOMPARE(clickedSpy.size(), 1); // Press and release position should match QCOMPARE(flickable->property("pressX").toReal(), flickable->property("releaseX").toReal()); @@ -569,38 +585,44 @@ void tst_qquickflickable::pressDelay() // Test a quick tap within the pressDelay timeout + p = QPoint(180, 180); clickedSpy.clear(); - moveAndPress(window.data(), QPoint(180, 180)); + if (device->type() == QInputDevice::DeviceType::Mouse) + QQuickTest::pointerMove(device, &window, 0, p); + QQuickTest::pointerPress(device, &window, 0, p); // The press should not occur immediately - QVERIFY(!mouseArea->property("pressed").toBool()); + QCOMPARE(mouseArea->pressed(), false); + QCOMPARE(clickedSpy.size(), 0); - QCOMPARE(clickedSpy.size(),0); - - // On release the press, release and clicked signal should be emitted - QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(180, 180)); - QCOMPARE(clickedSpy.size(),1); + // On release, the press, release and clicked signal should be emitted + QQuickTest::pointerRelease(device, &window, 0, p); + QCOMPARE(clickedSpy.size(), 1); // Press and release position should match QCOMPARE(flickable->property("pressX").toReal(), flickable->property("releaseX").toReal()); QCOMPARE(flickable->property("pressY").toReal(), flickable->property("releaseY").toReal()); - // QTBUG-31168 - moveAndPress(window.data(), QPoint(150, 110)); + // Test flick after press (QTBUG-31168) + QPoint startPosition(150, 110); + p = QPoint(150, 190); + clickedSpy.clear(); + if (device->type() == QInputDevice::DeviceType::Mouse) + QQuickTest::pointerMove(device, &window, 0, startPosition); + QQuickTest::pointerPress(device, &window, 0, startPosition); // The press should not occur immediately - QVERIFY(!mouseArea->property("pressed").toBool()); + QCOMPARE(mouseArea->pressed(), false); + QQuickTest::pointerMove(device, &window, 0, p); - QTest::mouseMove(window.data(), QPoint(150, 190)); + // Since we moved past the drag threshold, we should never receive the press + QCOMPARE(mouseArea->pressed(), false); + QTRY_COMPARE(mouseArea->pressed(), false); - // As we moved pass the drag threshold, we should never receive the press - QVERIFY(!mouseArea->property("pressed").toBool()); - QTRY_VERIFY(!mouseArea->property("pressed").toBool()); - - // On release the clicked signal should *not* be emitted - QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(150, 190)); - QCOMPARE(clickedSpy.size(),1); + // On release, the clicked signal should *not* be emitted + QQuickTest::pointerRelease(device, &window, 0, p); + QCOMPARE(clickedSpy.size(), 0); } // QTBUG-17361 @@ -1951,7 +1973,7 @@ void tst_qquickflickable::nestedStopAtBounds() // drag toward the aligned boundary. Outer flickable dragged. moveAndPress(&view, position); if (waitForPressDelay) { - QVERIFY(innerFiltering); // isPressed will never be true if the mouse area isn't enabled. + QVERIFY(innerFiltering); // pressed will never be true if the mouse area isn't enabled. QTRY_VERIFY(mouseArea->pressed()); }