PinchArea: fix pinches being stolen when in PathView

Keep the mouse grab in addition to touch, as PathView
apparently doesn't deal with touch events yet.

Fixes: QTBUG-105058
Pick-to: 6.2 6.3 6.4
Change-Id: Id94768aec847138cccdeccfa92e4bc72a19810fe
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
Mitch Curtis 2022-07-27 19:44:30 +08:00
parent 212f31f9ea
commit 10800723ab
3 changed files with 143 additions and 0 deletions

View File

@ -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();

View File

@ -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
}
}
}

View File

@ -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 <QtCore/private/qvariantanimation_p.h>
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
#include <QtGui/QStyleHints>
@ -8,6 +9,7 @@
#include <QtGui/private/qeventpoint_p.h>
#include <qpa/qwindowsysteminterface.h>
#include <private/qquickpincharea_p.h>
#include <QtQuick/private/qquickpathview_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/qquickview.h>
#include <QtQml/qqmlcontext.h>
@ -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<typename F>
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<QQuickPathView*>(view.rootObject());
QVERIFY(pathView);
QCOMPARE(pathView->count(), 3);
const QQuickItem *pinchDelegateItem = pathView->itemAtIndex(0);
QQuickPinchArea *pinchArea = pinchDelegateItem->property("pinchArea").value<QQuickPinchArea*>();
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);