aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp33
-rw-r--r--src/quick/util/qquickdeliveryagent_p_p.h1
-rw-r--r--src/quicktestutils/quick/viewtestutils.cpp35
-rw-r--r--tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml3
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp65
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());