diff options
3 files changed, 97 insertions, 21 deletions
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index 708cfff76b..d347472741 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -42,6 +42,7 @@ #include <private/qsgadaptationlayer_p.h> #include <private/qevent_p.h> #include <private/qquickitem_p.h> +#include <private/qquickwindow_p.h> #include <private/qguiapplication_p.h> #include <QEvent> #include <QMouseEvent> @@ -448,6 +449,7 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent) : QQuickItem(parent), _minimumTouchPoints(0), _maximumTouchPoints(INT_MAX), + _touchMouseDevice(nullptr), _stealMouse(false), _mouseEnabled(true) { @@ -586,10 +588,10 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) bool ended = false; bool moved = false; bool started = false; - bool isMouseEvent = false; clearTouchLists(); QList<QTouchEvent::TouchPoint> touchPoints; + QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window()); switch (event->type()) { case QEvent::TouchBegin: @@ -598,6 +600,9 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) touchPoints = static_cast<QTouchEvent*>(event)->touchPoints(); break; case QEvent::MouseButtonPress: + _mouseQpaTouchPoint = QTouchEvent::TouchPoint(windowPriv->touchMouseId); + _touchMouseDevice = windowPriv->touchMouseDevice->qTouchDevice(); + Q_FALLTHROUGH(); case QEvent::MouseMove: case QEvent::MouseButtonRelease: { QMouseEvent *me = static_cast<QMouseEvent*>(event); @@ -617,7 +622,6 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) _mouseQpaTouchPoint.setState(Qt::TouchPointPressed); } touchPoints << _mouseQpaTouchPoint; - isMouseEvent = true; break; } default: @@ -625,11 +629,6 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) break; } - if (!isMouseEvent && _mouseTouchPoint) { - QQuickWindow *c = window(); - if (c && c->mouseGrabberItem() == this) - touchPoints << _mouseQpaTouchPoint; - } int numTouchPoints = touchPoints.count(); //always remove released touches, and make sure we handle all releases before adds. for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) { @@ -756,7 +755,7 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QMouseEvent *e) dtp = new QQuickTouchPoint(false); updateTouchPoint(dtp, e); dtp->setPressed(true); - _touchPoints.insert(-1, dtp); + _touchPoints.insert(_touchMouseDevice && _mouseQpaTouchPoint.id() > 0 ? _mouseQpaTouchPoint.id() : -1, dtp); _pressedTouchPoints.append(dtp); _mouseTouchPoint = dtp; } @@ -839,8 +838,7 @@ void QQuickMultiPointTouchArea::mousePressEvent(QMouseEvent *event) setKeepMouseGrab(false); event->setAccepted(true); _mousePos = event->localPos(); - - if (event->source() != Qt::MouseEventNotSynthesized) + if (event->source() != Qt::MouseEventNotSynthesized && event->source() != Qt::MouseEventSynthesizedByQt) return; if (_touchPoints.count() >= _minimumTouchPoints - 1 && _touchPoints.count() < _maximumTouchPoints) { @@ -855,7 +853,7 @@ void QQuickMultiPointTouchArea::mouseMoveEvent(QMouseEvent *event) return; } - if (event->source() != Qt::MouseEventNotSynthesized) + if (event->source() != Qt::MouseEventNotSynthesized && event->source() != Qt::MouseEventSynthesizedByQt) return; _movedTouchPoints.clear(); @@ -870,7 +868,7 @@ void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event) return; } - if (event->source() != Qt::MouseEventNotSynthesized) + if (event->source() != Qt::MouseEventNotSynthesized && event->source() != Qt::MouseEventSynthesizedByQt) return; if (_mouseTouchPoint) { @@ -880,18 +878,16 @@ void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event) _mouseTouchPoint = nullptr; } - QQuickWindow *c = window(); - if (c && c->mouseGrabberItem() == this) - ungrabMouse(); setKeepMouseGrab(false); } -void QQuickMultiPointTouchArea::ungrab() +void QQuickMultiPointTouchArea::ungrab(bool normalRelease) { _stealMouse = false; setKeepMouseGrab(false); setKeepTouchGrab(false); - ungrabTouchPoints(); + if (!normalRelease) + ungrabTouchPoints(); if (_touchPoints.count()) { for (QObject *obj : qAsConst(_touchPoints)) @@ -969,13 +965,25 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *receiver, QEve if (!isEnabled() || !isVisible()) return QQuickItem::childMouseEventFilter(receiver, event); switch (event->type()) { - case QEvent::MouseButtonPress: + case QEvent::MouseButtonPress: { + QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window()); + // If we already got a chance to filter the touchpoint that generated this synth-mouse-press, + // and chose not to filter it, ignore it now, too. + if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventSynthesizedByQt && + _lastFilterableTouchPointIds.contains(windowPriv->touchMouseId)) + return false; + } Q_FALLTHROUGH(); case QEvent::MouseMove: case QEvent::MouseButtonRelease: return sendMouseEvent(static_cast<QMouseEvent *>(event)); - break; case QEvent::TouchBegin: + _lastFilterableTouchPointIds.clear(); + Q_FALLTHROUGH(); case QEvent::TouchUpdate: + for (auto tp : static_cast<QTouchEvent*>(event)->touchPoints()) { + if (tp.state() == Qt::TouchPointPressed) + _lastFilterableTouchPointIds << tp.id(); + } if (!shouldFilter(event)) return false; updateTouchData(event); @@ -984,7 +992,7 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *receiver, QEve if (!shouldFilter(event)) return false; updateTouchData(event); - ungrab(); + ungrab(true); } break; default: diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h index 634ea1c2e2..23f4ebff75 100644 --- a/src/quick/items/qquickmultipointtoucharea_p.h +++ b/src/quick/items/qquickmultipointtoucharea_p.h @@ -284,7 +284,7 @@ protected: #endif private: - void ungrab(); + void ungrab(bool normalRelease = false); QMap<int,QQuickTouchPoint*> _touchPrototypes; //TouchPoints defined in QML QMap<int,QObject*> _touchPoints; //All current touch points QList<QObject*> _releasedTouchPoints; @@ -292,8 +292,10 @@ private: QList<QObject*> _movedTouchPoints; int _minimumTouchPoints; int _maximumTouchPoints; + QVector<int> _lastFilterableTouchPointIds; QPointer<QQuickTouchPoint> _mouseTouchPoint; // exists when mouse button is down and _mouseEnabled is true; null otherwise QTouchEvent::TouchPoint _mouseQpaTouchPoint; // synthetic QPA touch point to hold state and position of the mouse + const QTouchDevice *_touchMouseDevice; QPointF _mousePos; bool _stealMouse; bool _mouseEnabled; diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 9c19e8e522..7f5adff062 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -31,12 +31,15 @@ #include <private/qquickmultipointtoucharea_p.h> #include <private/qquickflickable_p.h> #include <private/qquickmousearea_p.h> +#include <private/qquickwindow_p.h> #include <qpa/qwindowsysteminterface.h> #include <QtQuick/qquickview.h> #include <QtGui/QScreen> #include "../../shared/util.h" #include "../shared/viewtestutil.h" +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + class tst_QQuickMultiPointTouchArea : public QQmlDataTest { Q_OBJECT @@ -62,6 +65,7 @@ private slots: void nested(); void inFlickable(); void inFlickable2(); + void inFlickableWithPressDelay(); void inMouseArea(); void mouseAsTouchpoint(); void invisible(); @@ -812,6 +816,68 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() QTRY_VERIFY(!flickable->isMoving()); } +void tst_QQuickMultiPointTouchArea::inFlickableWithPressDelay() // QTBUG-78818 +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> window(createAndShowView("inFlickable.qml")); + QVERIFY(window->rootObject() != nullptr); + QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data()); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(window->rootObject()); + QVERIFY(flickable != nullptr); + flickable->setPressDelay(50); + + QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>(); + QVERIFY(mpta != nullptr); + mpta->setMinimumTouchPoints(1); + QQuickTouchPoint *point11 = window->rootObject()->findChild<QQuickTouchPoint*>("point1"); + QPoint p1(20,100); + + // press: Flickable prevents delivery of TouchBegin, but sends mouse press instead, after the delay. + // MPTA handles the mouse press, and its first declared touchpoint is pressed. + QTest::touchEvent(window.data(), device).press(0, p1); + QQuickTouchUtils::flush(window.data()); + QTRY_COMPARE(point11->pressed(), true); + auto pointerEvent = windowPriv->pointerEventInstance(QQuickPointerDevice::touchDevices().at(0)); + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), mpta); + + // release: MPTA receives TouchEnd (which is asymmetric with mouse press); does NOT emit canceled. + QTest::touchEvent(window.data(), device).release(0, p1); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(flickable->property("cancelCount").toInt(), 0); + + // press again + QTest::touchEvent(window.data(), device).press(0, p1); + QQuickTouchUtils::flush(window.data()); + QTRY_COMPARE(point11->pressed(), true); // wait until pressDelay exceeded + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), mpta); + + // drag past the threshold: Flickable takes over the grab, MPTA gets touchUngrab and is no longer pressed + int i = 0; + for (; i < 10 && window->mouseGrabberItem() != flickable; ++i) { + p1 += QPoint(0,dragThreshold); + QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); + } + QCOMPARE(window->mouseGrabberItem(), flickable); + qCDebug(lcTests, "Flickable stole grab from MPTA after %d moves", i); + QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), flickable); + QCOMPARE(point11->pressed(), false); + QVERIFY(flickable->property("cancelCount").toInt() > 0); // actually 2 because 2 touchPoints are declared... but only one was really cancelled + + // drag a little more and the Flickable moves + p1 += QPoint(0,1); + QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); + QVERIFY(flickable->contentY() < 0); + QVERIFY(flickable->isMoving()); + + QTest::touchEvent(window.data(), device).release(0, p1); + QQuickTouchUtils::flush(window.data()); + + QTRY_VERIFY(!flickable->isMoving()); +} + // QTBUG-31047 void tst_QQuickMultiPointTouchArea::inMouseArea() { |