aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--src/particles/qquickparticlesystem.cpp7
-rw-r--r--src/particles/qquickparticlesystem_p.h2
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp9
-rw-r--r--src/quick/items/qquickmousearea.cpp6
-rw-r--r--src/quick/items/qquickmousearea_p.h1
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp48
-rw-r--r--src/quick/items/qquickmultipointtoucharea_p.h4
-rw-r--r--src/quick/util/qquickbehavior.cpp2
-rw-r--r--src/quickwidgets/qquickwidget.cpp4
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml58
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/mousearea_interop.pro15
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp160
-rw-r--r--tests/auto/quick/pointerhandlers/pointerhandlers.pro1
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml1
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp22
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp66
17 files changed, 378 insertions, 30 deletions
diff --git a/.qmake.conf b/.qmake.conf
index adc0d2128b..31c907f02f 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,4 @@
load(qt_build_config)
CONFIG += warning_clean
-MODULE_VERSION = 5.12.6
+MODULE_VERSION = 5.12.7
diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp
index 1499df0360..8e8c73320e 100644
--- a/src/particles/qquickparticlesystem.cpp
+++ b/src/particles/qquickparticlesystem.cpp
@@ -389,16 +389,21 @@ QQuickParticleData* QQuickParticleGroupData::newDatum(bool respectsLimits)
bool QQuickParticleGroupData::recycle()
{
+ m_latestAliveParticles.clear();
+
while (dataHeap.top() <= m_system->timeInt) {
foreach (QQuickParticleData* datum, dataHeap.pop()) {
if (!datum->stillAlive(m_system)) {
freeList.free(datum->index);
} else {
- prepareRecycler(datum); //ttl has been altered mid-way, put it back
+ m_latestAliveParticles.push_back(datum);
}
}
}
+ for (auto particle : m_latestAliveParticles)
+ prepareRecycler(particle); //ttl has been altered mid-way, put it back
+
//TODO: If the data is clear, gc (consider shrinking stack size)?
return freeList.count() == 0;
}
diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h
index 73351fb99a..d88e4949e3 100644
--- a/src/particles/qquickparticlesystem_p.h
+++ b/src/particles/qquickparticlesystem_p.h
@@ -226,6 +226,8 @@ public:
private:
int m_size;
QQuickParticleSystem* m_system;
+ // Only used in recycle() for tracking of alive particles after latest recycling round
+ QVector<QQuickParticleData*> m_latestAliveParticles;
};
struct Color4ub {
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index 61f97868e8..276b80abaa 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -140,17 +140,16 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point)
}
break;
case QQuickEventPoint::Stationary:
- // Never react in any way when the point hasn't moved.
- // In autotests, the point's position may not even be correct, because
- // QTest::touchEvent(window, touchDevice).stationary(1)
- // provides no opportunity to give a position, so it ends up being random.
+ // If the point hasn't moved since last time, the return value should be the same as last time.
+ // If we return false here, QQuickPointerHandler::handlePointerEvent() will call setActive(false).
+ ret = point->pointId() == this->point().id();
break;
}
// If this is the grabber, returning false from this function will cancel the grab,
// so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called.
// But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but
// we still don't want to be pressed anymore.
- if (!ret && point->pointId() == this->point().id() && point->state() != QQuickEventPoint::Stationary)
+ if (!ret && point->pointId() == this->point().id())
setPressed(false, true, point);
return ret;
}
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 0b345697ec..5124802dbf 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -938,6 +938,12 @@ void QQuickMouseArea::mouseUngrabEvent()
ungrabMouse();
}
+void QQuickMouseArea::touchUngrabEvent()
+{
+ // allow a Pointer Handler to steal the grab from MouseArea
+ ungrabMouse();
+}
+
bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event)
{
Q_D(QQuickMouseArea);
diff --git a/src/quick/items/qquickmousearea_p.h b/src/quick/items/qquickmousearea_p.h
index 0a8449957f..b1a48ef2b7 100644
--- a/src/quick/items/qquickmousearea_p.h
+++ b/src/quick/items/qquickmousearea_p.h
@@ -169,6 +169,7 @@ protected:
void mouseDoubleClickEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseUngrabEvent() override;
+ void touchUngrabEvent() override;
void hoverEnterEvent(QHoverEvent *event) override;
void hoverMoveEvent(QHoverEvent *event) override;
void hoverLeaveEvent(QHoverEvent *event) override;
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/src/quick/util/qquickbehavior.cpp b/src/quick/util/qquickbehavior.cpp
index d024c0099b..d9b13067ad 100644
--- a/src/quick/util/qquickbehavior.cpp
+++ b/src/quick/util/qquickbehavior.cpp
@@ -177,7 +177,7 @@ void QQuickBehavior::write(const QVariant &value)
bool bypass = !d->enabled || !d->finalized || QQmlEnginePrivate::designerMode();
if (!bypass)
qmlExecuteDeferred(this);
- if (!d->animation || bypass) {
+ if (QQmlData::wasDeleted(d->animation) || bypass) {
if (d->animationInstance)
d->animationInstance->stop();
QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index b63f7de9f1..55c095af8e 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -878,7 +878,9 @@ void QQuickWidgetPrivate::createContext()
context = new QOpenGLContext;
context->setFormat(offscreenWindow->requestedFormat());
-
+ const QWindow *win = q->window()->windowHandle();
+ if (win && win->screen())
+ context->setScreen(win->screen());
QOpenGLContext *shareContext = qt_gl_global_share_context();
if (!shareContext)
shareContext = QWidgetPrivate::get(q->window())->shareContext();
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml
new file mode 100644
index 0000000000..48b1dc86f0
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+Item {
+ width: 640
+ height: 480
+
+ Rectangle {
+ width: 200
+ height: 200
+ color: mouseArea.pressed ? "red" : "blue"
+ opacity: 0.6
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ }
+ }
+ Rectangle {
+ y: 100
+ z: -1
+ width: 200
+ height: 200
+ color: dragHandler.active ? "orange" : "green"
+ opacity: 0.6
+
+ DragHandler {
+ id: dragHandler
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/mousearea_interop.pro b/tests/auto/quick/pointerhandlers/mousearea_interop/mousearea_interop.pro
new file mode 100644
index 0000000000..0bf0ec86a9
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/mousearea_interop.pro
@@ -0,0 +1,15 @@
+CONFIG += testcase
+
+TARGET = tst_mousearea_interop
+QT += core-private gui-private qml-private quick-private testlib
+
+macos:CONFIG -= app_bundle
+
+SOURCES += tst_mousearea_interop.cpp
+
+include (../../../shared/util.pri)
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+OTHER_FILES += data/dragTakeOverFromSibling.qml
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
new file mode 100644
index 0000000000..03cff92f46
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlproperty.h>
+#include <QtQuick/private/qquickdraghandler_p.h>
+#include <QtQuick/private/qquickmousearea_p.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickview.h>
+
+#include "../../../shared/util.h"
+#include "../../shared/viewtestutil.h"
+
+Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
+
+class tst_MouseAreaInterop : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_MouseAreaInterop()
+ : touchDevice(QTest::createTouchDevice())
+ , touchPointerDevice(QQuickPointerDevice::touchDevice(touchDevice))
+ {}
+
+private slots:
+ void dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse();
+ void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch();
+
+private:
+ void createView(QScopedPointer<QQuickView> &window, const char *fileName);
+ QTouchDevice *touchDevice;
+ QQuickPointerDevice *touchPointerDevice;
+};
+
+void tst_MouseAreaInterop::createView(QScopedPointer<QQuickView> &window, const char *fileName)
+{
+ window.reset(new QQuickView);
+ window->setSource(testFileUrl(fileName));
+ QTRY_COMPARE(window->status(), QQuickView::Ready);
+ QQuickViewTestUtil::centerOnScreen(window.data());
+ QQuickViewTestUtil::moveMouseAway(window.data());
+
+ window->show();
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
+ QVERIFY(window->rootObject() != nullptr);
+}
+
+void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse()
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "dragTakeOverFromSibling.qml");
+ QQuickView * window = windowPtr.data();
+ auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(QQuickPointerDevice::genericMouseDevice());
+
+ QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>();
+ QVERIFY(handler);
+ QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>();
+ QVERIFY(ma);
+
+ QPoint p1(150, 150);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QCOMPARE(window->mouseGrabberItem(), ma);
+ QCOMPARE(ma->pressed(), true);
+
+ // Start dragging
+ // DragHandler keeps monitoring, due to its passive grab,
+ // and eventually steals the exclusive grab from MA
+ int dragStoleGrab = 0;
+ for (int i = 0; i < 4; ++i) {
+ p1 += QPoint(dragThreshold / 2, 0);
+ QTest::mouseMove(window, p1);
+ if (!dragStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == handler)
+ dragStoleGrab = i;
+ }
+ if (dragStoleGrab)
+ qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab);
+ QVERIFY(dragStoleGrab > 1);
+ QCOMPARE(handler->active(), true);
+ QCOMPARE(ma->pressed(), false);
+
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QCOMPARE(handler->active(), false);
+}
+
+void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch() // QTBUG-77624
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "dragTakeOverFromSibling.qml");
+ QQuickView * window = windowPtr.data();
+ auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(touchPointerDevice);
+
+ QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>();
+ QVERIFY(handler);
+ QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>();
+ QVERIFY(ma);
+
+ QPoint p1(150, 150);
+ QTest::QTouchEventSequence touch = QTest::touchEvent(window, touchDevice);
+
+ touch.press(1, p1).commit();
+ QQuickTouchUtils::flush(window);
+ QTRY_VERIFY(pointerEvent->point(0)->passiveGrabbers().contains(handler));
+ QCOMPARE(pointerEvent->point(0)->grabberItem(), ma);
+ QCOMPARE(window->mouseGrabberItem(), ma);
+ QCOMPARE(ma->pressed(), true);
+
+ // Start dragging
+ // DragHandler keeps monitoring, due to its passive grab,
+ // and eventually steals the exclusive grab from MA
+ int dragStoleGrab = 0;
+ for (int i = 0; i < 4; ++i) {
+ p1 += QPoint(dragThreshold / 2, 0);
+ touch.move(1, p1).commit();
+ QQuickTouchUtils::flush(window);
+ if (!dragStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == handler)
+ dragStoleGrab = i;
+ }
+ if (dragStoleGrab)
+ qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab);
+ QVERIFY(dragStoleGrab > 1);
+ QCOMPARE(handler->active(), true);
+ QCOMPARE(ma->pressed(), false);
+
+ touch.release(1, p1).commit();
+ QQuickTouchUtils::flush(window);
+ QCOMPARE(handler->active(), false);
+}
+
+QTEST_MAIN(tst_MouseAreaInterop)
+
+#include "tst_mousearea_interop.moc"
diff --git a/tests/auto/quick/pointerhandlers/pointerhandlers.pro b/tests/auto/quick/pointerhandlers/pointerhandlers.pro
index 950d6835eb..ce616430e8 100644
--- a/tests/auto/quick/pointerhandlers/pointerhandlers.pro
+++ b/tests/auto/quick/pointerhandlers/pointerhandlers.pro
@@ -3,6 +3,7 @@ TEMPLATE = subdirs
qtConfig(private_tests) {
SUBDIRS += \
flickableinterop \
+ mousearea_interop \
multipointtoucharea_interop \
qquickdraghandler \
qquickhoverhandler \
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
index 042b730799..800c25c77d 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
@@ -31,6 +31,7 @@ import QtQuick 2.12
Rectangle {
id: root
property alias label: label.text
+ property alias active: tap.active
property alias pressed: tap.pressed
property bool checked: false
property alias gesturePolicy: tap.gesturePolicy
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
index e77ea97518..419afed3ac 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
@@ -600,10 +600,32 @@ void tst_TapHandler::buttonsMultiTouch()
touchSeq.stationary(1).press(2, p2, window).commit();
QQuickTouchUtils::flush(window);
QTRY_VERIFY(buttonWithinBounds->property("pressed").toBool());
+ QVERIFY(buttonWithinBounds->property("active").toBool());
QPoint p3 = buttonReleaseWithinBounds->mapToScene(QPointF(20, 20)).toPoint();
touchSeq.stationary(1).stationary(2).press(3, p3, window).commit();
QQuickTouchUtils::flush(window);
QTRY_VERIFY(buttonReleaseWithinBounds->property("pressed").toBool());
+ QVERIFY(buttonReleaseWithinBounds->property("active").toBool());
+ QVERIFY(buttonWithinBounds->property("pressed").toBool());
+ QVERIFY(buttonWithinBounds->property("active").toBool());
+ QVERIFY(buttonDragThreshold->property("pressed").toBool());
+
+ // combinations of small touchpoint movements and stationary points should not cause state changes
+ p1 += QPoint(2, 0);
+ p2 += QPoint(3, 0);
+ touchSeq.move(1, p1).move(2, p2).stationary(3).commit();
+ QVERIFY(buttonDragThreshold->property("pressed").toBool());
+ QVERIFY(buttonWithinBounds->property("pressed").toBool());
+ QVERIFY(buttonWithinBounds->property("active").toBool());
+ QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool());
+ QVERIFY(buttonReleaseWithinBounds->property("active").toBool());
+ p3 += QPoint(4, 0);
+ touchSeq.stationary(1).stationary(2).move(3, p3).commit();
+ QVERIFY(buttonDragThreshold->property("pressed").toBool());
+ QVERIFY(buttonWithinBounds->property("pressed").toBool());
+ QVERIFY(buttonWithinBounds->property("active").toBool());
+ QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool());
+ QVERIFY(buttonReleaseWithinBounds->property("active").toBool());
// can release top button and press again: others stay pressed the whole time
touchSeq.stationary(2).stationary(3).release(1, p1, window).commit();
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()
{