diff options
-rw-r--r-- | src/quick/util/qquickdeliveryagent.cpp | 33 | ||||
-rw-r--r-- | src/quick/util/qquickdeliveryagent_p_p.h | 1 | ||||
-rw-r--r-- | src/quicktestutils/quick/viewtestutils.cpp | 35 | ||||
-rw-r--r-- | tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml | 3 | ||||
-rw-r--r-- | tests/auto/quick/qquicklistview/tst_qquicklistview.cpp | 65 |
5 files changed, 122 insertions, 15 deletions
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index 1b2a888a22..9c63cf6b19 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -1538,6 +1538,28 @@ bool QQuickDeliveryAgentPrivate::isSynthMouse(const QPointerEvent *ev) return (!isEventFromMouseOrTouchpad(ev) && isMouseEvent(ev)); } +/*! + Returns \c true if \a dev is a type of device that only sends + QSinglePointEvents. +*/ +bool QQuickDeliveryAgentPrivate::isSinglePointDevice(const QInputDevice *dev) +{ + switch (dev->type()) { + case QInputDevice::DeviceType::Mouse: + case QInputDevice::DeviceType::TouchPad: + case QInputDevice::DeviceType::Puck: + case QInputDevice::DeviceType::Stylus: + case QInputDevice::DeviceType::Airbrush: + return true; + case QInputDevice::DeviceType::TouchScreen: + case QInputDevice::DeviceType::Keyboard: + case QInputDevice::DeviceType::Unknown: + case QInputDevice::DeviceType::AllDevices: + return false; + } + return false; +} + QQuickPointingDeviceExtra *QQuickDeliveryAgentPrivate::deviceExtra(const QInputDevice *device) { QInputDevicePrivate *devPriv = QInputDevicePrivate::get(const_cast<QInputDevice *>(device)); @@ -1843,17 +1865,18 @@ void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice switch (transition) { case QPointingDevice::CancelGrabExclusive: case QPointingDevice::UngrabExclusive: - if (isDeliveringTouchAsMouse() - || point.device()->type() == QInputDevice::DeviceType::Mouse - || point.device()->type() == QInputDevice::DeviceType::TouchPad) { + if (isDeliveringTouchAsMouse() || isSinglePointDevice(point.device())) { + // If an EventPoint from the mouse or the synth-mouse or from any + // mouse-like device is ungrabbed, call QQuickItem::mouseUngrabEvent(). QMutableSinglePointEvent e(QEvent::UngrabMouse, point.device(), point); hasFiltered.clear(); if (!sendFilteredMouseEvent(&e, grabberItem, grabberItem->parentItem())) { lastUngrabbed = grabberItem; grabberItem->mouseUngrabEvent(); } - } - if (point.device()->type() == QInputDevice::DeviceType::TouchScreen) { + } else { + // Multi-point event: call QQuickItem::touchUngrabEvent() only if + // all eventpoints are released or cancelled. bool allReleasedOrCancelled = true; if (transition == QPointingDevice::UngrabExclusive && event) { for (const auto &pt : event->points()) { diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h index 878561bc12..3dd60db98f 100644 --- a/src/quick/util/qquickdeliveryagent_p_p.h +++ b/src/quick/util/qquickdeliveryagent_p_p.h @@ -151,6 +151,7 @@ public: static bool isTabletEvent(const QPointerEvent *ev); static bool isEventFromMouseOrTouchpad(const QPointerEvent *ev); static bool isSynthMouse(const QPointerEvent *ev); + static bool isSinglePointDevice(const QInputDevice *dev); static QQuickPointingDeviceExtra *deviceExtra(const QInputDevice *device); // delivery of pointer events: diff --git a/src/quicktestutils/quick/viewtestutils.cpp b/src/quicktestutils/quick/viewtestutils.cpp index 053e864660..40a67a305e 100644 --- a/src/quicktestutils/quick/viewtestutils.cpp +++ b/src/quicktestutils/quick/viewtestutils.cpp @@ -9,6 +9,7 @@ #include <QtQuick/QQuickView> #include <QtGui/QScreen> #include <QtGui/qpa/qwindowsysteminterface.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <QtTest/QTest> @@ -537,13 +538,17 @@ namespace QQuickTest { break; case QPointingDevice::DeviceType::Puck: case QPointingDevice::DeviceType::Stylus: - case QPointingDevice::DeviceType::Airbrush: - QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); + case QPointingDevice::DeviceType::Airbrush:{ + const QPointF nativeLocal = QHighDpi::toNativeLocalPosition(p, window); + const QPointF nativeGlobal = QHighDpi::toNativeGlobalPosition(window->mapToGlobal(p), window); + const auto delay = QTest::defaultMouseDelay(); + QTest::lastMouseTimestamp += delay ? delay : 1; pressedTabletButton = button; pressedTabletModifiers = modifiers; - QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p), + QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, nativeLocal, nativeGlobal, button, 0.8, 0, 0, 0, 0, 0, modifiers); break; + } default: qWarning() << "can't send a press event from" << dev; break; @@ -563,11 +568,17 @@ namespace QQuickTest { break; case QPointingDevice::DeviceType::Puck: case QPointingDevice::DeviceType::Stylus: - case QPointingDevice::DeviceType::Airbrush: - QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); - QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p), - pressedTabletButton, 0, 0, 0, 0, 0, 0, pressedTabletModifiers); + case QPointingDevice::DeviceType::Airbrush: { + const QPointF nativeLocal = QHighDpi::toNativeLocalPosition(p, window); + const QPointF nativeGlobal = QHighDpi::toNativeGlobalPosition(window->mapToGlobal(p), window); + const auto delay = QTest::defaultMouseDelay(); + // often QTest::defaultMouseDelay() == 0; but avoid infinite velocity + QTest::lastMouseTimestamp += delay ? delay : 1; + QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, nativeLocal, nativeGlobal, + pressedTabletButton, pressedTabletButton == Qt::NoButton ? 0 : 0.75, + 0, 0, 0, 0, 0, pressedTabletModifiers); break; + } default: qWarning() << "can't send a move event from" << dev; break; @@ -588,11 +599,15 @@ namespace QQuickTest { break; case QPointingDevice::DeviceType::Puck: case QPointingDevice::DeviceType::Stylus: - case QPointingDevice::DeviceType::Airbrush: - QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); - QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p), + case QPointingDevice::DeviceType::Airbrush: { + const QPointF nativeLocal = QHighDpi::toNativeLocalPosition(p, window); + const QPointF nativeGlobal = QHighDpi::toNativeGlobalPosition(window->mapToGlobal(p), window); + const auto delay = QTest::defaultMouseDelay(); + QTest::lastMouseTimestamp += delay ? delay : 1; + QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, nativeLocal, nativeGlobal, Qt::NoButton, 0, 0, 0, 0, 0, 0, modifiers); break; + } default: qWarning() << "can't send a press event from" << dev; break; diff --git a/tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml b/tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml index e0b8222bfb..4bdf7033a9 100644 --- a/tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml +++ b/tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml @@ -17,12 +17,15 @@ ListView { width: 500 height: 500 color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1) + border.width: 4 + border.color: ma.pressed ? "red" : "white" Text { text: index font.pixelSize: 128 anchors.centerIn: parent } MouseArea { + id: ma anchors.fill: parent } } diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index a1e7d5b4ca..2629941135 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -39,6 +39,8 @@ Q_DECLARE_METATYPE(QQuickListView::Orientation) Q_DECLARE_METATYPE(QQuickFlickable::FlickableDirection) Q_DECLARE_METATYPE(Qt::Key) +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + using namespace QQuickViewTestUtils; using namespace QQuickVisualTestUtils; @@ -268,6 +270,8 @@ private slots: void addOnCompleted(); void setPositionOnLayout(); void touchCancel(); + void cancelDelegatePastDragThreshold_data(); + void cancelDelegatePastDragThreshold(); void resizeAfterComponentComplete(); void dragOverFloatingHeaderOrFooter(); @@ -340,6 +344,12 @@ private: QQuickView *m_view; QString testForView; QPointingDevice *touchDevice = QTest::createTouchDevice(); +#if QT_CONFIG(tabletevent) + QScopedPointer<const QPointingDevice> tabletStylusDevice = QScopedPointer<const QPointingDevice>( + QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType::Stylus, + QPointingDevice::PointerType::Pen, + QPointingDeviceUniqueId::fromNumericId(1234567890))); +#endif }; class TestObject : public QObject @@ -9744,6 +9754,61 @@ void tst_QQuickListView::touchCancel() // QTBUG-74679 QTRY_COMPARE(listview->contentY(), 500.0); } +void tst_QQuickListView::cancelDelegatePastDragThreshold_data() +{ + QTest::addColumn<const QPointingDevice *>("device"); + + QTest::newRow("primary") << QPointingDevice::primaryPointingDevice(); + QTest::newRow("touch") << static_cast<const QPointingDevice*>(touchDevice); // TODO QTBUG-107864 +#if QT_CONFIG(tabletevent) && !defined(Q_OS_QNX) + QTest::newRow("stylus") << tabletStylusDevice.get(); +#endif +} + +void tst_QQuickListView::cancelDelegatePastDragThreshold() // QTBUG-118903 +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QFETCH(const QPointingDevice *, device); + +#if QT_CONFIG(tabletevent) + QVERIFY(qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents)); +#endif + + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("delegateWithMouseArea.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickListView *listview = qobject_cast<QQuickListView *>(window->rootObject()); + QVERIFY(listview); + QQuickMouseArea *mouseArea = listview->currentItem()->findChild<QQuickMouseArea *>(); + QVERIFY(mouseArea); + QSignalSpy canceledSpy(mouseArea, &QQuickMouseArea::canceled); + + QPoint p = mouseArea->mapToScene(mouseArea->boundingRect().center()).toPoint(); + // MouseArea grabs on press + QQuickTest::pointerPress(device, window.get(), 1, p); + QTRY_VERIFY(mouseArea->isPressed()); + // drag past the drag threshold until ListView takes over the grab + p -= {0, dragThreshold + 1}; + QQuickTest::pointerMove(device, window.get(), 1, p); + int movesWhenGrabbed = 1; + for (int i = 2; i < 6; ++i) { + p -= {0, 1}; + QQuickTest::pointerMove(device, window.get(), 1, p); + if (device == tabletStylusDevice.get()) + QTest::qWait(1); + if (listview->isDragging()) + movesWhenGrabbed = i; + } + qCDebug(lcTests) << "ListView took over grab after" << movesWhenGrabbed << "moves"; + QVERIFY(listview->isDragging()); + // MouseArea's grab got canceled + QCOMPARE(canceledSpy.size(), 1); + QCOMPARE(mouseArea->isPressed(), false); + QQuickTest::pointerRelease(device, window.get(), 1, p); +} + void tst_QQuickListView::resizeAfterComponentComplete() // QTBUG-76487 { QScopedPointer<QQuickView> window(createView()); |