diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index 02086bc4bd..626d9eb772 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -354,6 +354,7 @@ void QQuickPinchArea::clearPinch(QTouchEvent *event) } } setKeepTouchGrab(false); + setKeepMouseGrab(false); } void QQuickPinchArea::cancelPinch(QTouchEvent *event) @@ -395,6 +396,7 @@ void QQuickPinchArea::cancelPinch(QTouchEvent *event) event->setExclusiveGrabber(point, nullptr); } setKeepTouchGrab(false); + setKeepMouseGrab(false); } void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering) @@ -427,6 +429,7 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering) pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); pe.setPoint1(mapFromScene(d->lastPoint1)); pe.setPoint2(mapFromScene(d->lastPoint2)); + setKeepMouseGrab(false); emit pinchFinished(&pe); d->pinchStartDist = 0; d->pinchActivated = false; @@ -525,6 +528,9 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering) event->setExclusiveGrabber(touchPoint1, this); event->setExclusiveGrabber(touchPoint2, this); setKeepTouchGrab(true); + // So that PinchArea works in PathView, grab mouse events too. + // We should be able to remove these setKeepMouseGrab calls when QTBUG-105567 is fixed. + setKeepMouseGrab(true); d->inPinch = true; if (d->pinch && d->pinch->target()) { auto targetParent = pinch()->target()->parentItem(); diff --git a/tests/auto/quick/qquickpincharea/data/pinchAreaInPathView.qml b/tests/auto/quick/qquickpincharea/data/pinchAreaInPathView.qml new file mode 100644 index 0000000000..dc909c2c7c --- /dev/null +++ b/tests/auto/quick/qquickpincharea/data/pinchAreaInPathView.qml @@ -0,0 +1,48 @@ +import QtQuick + +PathView { + width: 600 + height: 200 + + model: 3 + delegate: Rectangle { + width: 200 + height: 200 + color: "salmon" + opacity: PathView.isCurrentItem ? 1 : 0.5 + + property alias pinchArea: pinchArea + + Text { + text: "Test" + font.pixelSize: 100 + anchors.fill: parent + fontSizeMode: Text.Fit + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + PinchArea { + id: pinchArea + anchors.fill: parent + pinch.target: parent + pinch.dragAxis: Pinch.XAndYAxis + pinch.minimumScale: 1.0 + pinch.maximumScale: 5.0 + + onPinchFinished: (pinch) => { + parent.scale = 1 + parent.x = 0 + parent.y = 0 + } + } + } + path: Path { + startX: 100 + startY: 100 + PathLine { + x: 700 + y: 100 + } + } +} diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp index d23f804690..c5e07ac7b5 100644 --- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp +++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include #include #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +32,7 @@ private slots: void transformedPinchArea(); void dragTransformedPinchArea_data(); void dragTransformedPinchArea(); + void pinchAreaKeepsDragInView(); private: QQuickView *createView(); @@ -618,6 +621,92 @@ void tst_QQuickPinchArea::dragTransformedPinchArea() // QTBUG-63673 QCOMPARE(pinchArea->pinch()->active(), false); } +template +void forEachLerpStep(int steps, F &&func) +{ + for (int i = 0; i < steps; ++i) { + const qreal t = qreal(i) / steps; + func(t); + } +} + +QPoint lerpPoints(const QPoint &point1, const QPoint &point2, qreal t) +{ + return QPoint(_q_interpolate(point1.x(), point2.x(), t), _q_interpolate(point1.y(), point2.y(), t)); +}; + +// QTBUG-105058 +void tst_QQuickPinchArea::pinchAreaKeepsDragInView() +{ + QQuickView view; + view.setSource(testFileUrl("pinchAreaInPathView.qml")); + QVERIFY(view.rootObject()); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QQuickPathView *pathView = qobject_cast(view.rootObject()); + QVERIFY(pathView); + QCOMPARE(pathView->count(), 3); + + const QQuickItem *pinchDelegateItem = pathView->itemAtIndex(0); + QQuickPinchArea *pinchArea = pinchDelegateItem->property("pinchArea").value(); + QVERIFY(pinchArea); + + // Press. + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(&view, device); + QPoint point1Start = { 80, 120 }; + QPoint point2Start = { 120, 80 }; + const int dragThreshold = qApp->styleHints()->startDragDistance(); + pinchSequence.press(1, pinchArea->mapToScene(point1Start).toPoint(), &view) + .press(2, pinchArea->mapToScene(point2Start).toPoint(), &view).commit(); + QQuickTouchUtils::flush(&view); + + // Move past the drag threshold to begin the pinch. + const int steps = 30; + QPoint point1End = point1Start + QPoint(-dragThreshold, dragThreshold); + QPoint point2End = point2Start + QPoint(dragThreshold, -dragThreshold); + forEachLerpStep(steps, [&](qreal t) { + pinchSequence.move(1, lerpPoints(point1Start, point1End, t), &view) + .move(2, lerpPoints(point2Start, point2End, t), &view).commit(); + QQuickTouchUtils::flush(&view); + QTest::qWait(5); + }); + QCOMPARE(pinchArea->pinch()->active(), true); + QVERIFY2(pinchDelegateItem->scale() > 1.0, qPrintable(QString::number(pinchDelegateItem->scale()))); + // The PathView contents shouldn't have moved. + QCOMPARE(pathView->offset(), 0); + + // Release a touch point. + pinchSequence.stationary(1).release(2, point2End, &view).commit(); + QQuickTouchUtils::flush(&view); + + // Press it again. + pinchSequence.stationary(1).press(2, point2End, &view).commit(); + QQuickTouchUtils::flush(&view); + QCOMPARE(pinchArea->pinch()->active(), true); + + // Drag to the right; the PathView still shouldn't move. + point1Start = point1End; + point2Start = point2End; + point1End = point1Start + QPoint(100, 0); + point2End = point2Start + QPoint(100, 0); + forEachLerpStep(steps, [&](qreal t) { + pinchSequence.move(1, lerpPoints(point1Start, point1End, t), &view) + .move(2, lerpPoints(point2Start, point2End, t), &view).commit(); + QQuickTouchUtils::flush(&view); + QTest::qWait(5); + }); + QCOMPARE(pinchArea->pinch()->active(), true); + QVERIFY2(pinchDelegateItem->scale() > 1.0, qPrintable(QString::number(pinchDelegateItem->scale()))); + QCOMPARE(pathView->offset(), 0); + + // Release pinch. + pinchSequence.release(1, point1End, &view).release(2, point2End, &view).commit(); + QQuickTouchUtils::flush(&view); + QCOMPARE(pinchArea->pinch()->active(), false); + QCOMPARE(pathView->offset(), 0); +} + QQuickView *tst_QQuickPinchArea::createView() { QQuickView *window = new QQuickView(nullptr);