diff options
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() { |