aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2021-03-23 09:23:52 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-03-25 09:57:04 +0000
commit69b4706dbf52d4d089018e815e7c38ed7b761025 (patch)
treef18ddbf134c69c02072f444a360f919e6efa80aa
parent92d1cf4db7f1c03db0ffa1a380ff7955d162cd65 (diff)
PinchHandler: scale incrementally when new pinch gesture begins
When the gesture begins, we begin multiplying the target item's scale by 1.0 at first; it doesn't make sense to start immediately with the accumulated scale remembered from previous pinch gestures, because the target item remembers its own scale. When QQuickPinchHandler::wantsPointerEvent() returns false because some irrelevant gesture was received (for example a PanNativeGesture), that's not a good reason to deactivate. Deactivating and re-activating with each ZoomNativeGesture event results in extreme behavior, because PinchHandler depends on the BeginNativeGesture and EndNativeGesture events to reset internal state. Likewise, the fact that the button state is NoButton is not a good reason for wantsPointerEvent() to return false. Added an autotest: the first of its kind that actually simulates the native gesture events. Fixes: QTBUG-92064 Change-Id: I3a9b92d70f99497ee58ad8557d90d521fbe16d41 Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io> (cherry picked from commit fc636af3a723ee8b4ee42cf71864ae0df5ca4621) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp6
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp2
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp3
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp74
4 files changed, 81 insertions, 4 deletions
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index b1dca9a905..fffcc2f848 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -91,6 +91,8 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent)
: QQuickMultiPointHandler(parent, 2)
{
+ // Tell QQuickPointerDeviceHandler::wantsPointerEvent() to ignore button state
+ d_func()->acceptedButtons = Qt::NoButton;
}
/*!
@@ -235,7 +237,7 @@ void QQuickPinchHandler::onActiveChanged()
m_startRotation = t->rotation();
m_startPos = t->position();
} else {
- m_startScale = m_accumulatedScale;
+ m_startScale = 1;
m_startRotation = 0;
}
qCDebug(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation;
@@ -448,7 +450,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
qCDebug(lcPinchHandler) << "centroid" << centroid().scenePressPosition() << "->" << centroid().scenePosition()
<< ", distance" << m_startDistance << "->" << dist
- << ", startScale" << m_startScale << "->" << m_accumulatedScale
+ << ", scale" << m_startScale << "->" << m_accumulatedScale
<< ", activeRotation" << m_activeRotation
<< ", rotation" << rotation
<< " from " << event->device()->type();
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index a838f46d83..27f9c3fc36 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -305,7 +305,7 @@ bool QQuickPointerDeviceHandler::wantsPointerEvent(QPointerEvent *event)
return false;
if (d->acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != d->acceptedModifiers)
return false;
- // HoverHandler sets acceptedButtons to Qt::NoButton to indicate that button state is irrelevant.
+ // Some handlers (HoverHandler, PinchHandler) set acceptedButtons to Qt::NoButton to indicate that button state is irrelevant.
if (event->pointingDevice()->pointerType() != QPointingDevice::PointerType::Finger &&
acceptedButtons() != Qt::NoButton && event->type() != QEvent::Wheel &&
(static_cast<QSinglePointEvent *>(event)->buttons() & acceptedButtons()) == 0 &&
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index f9ae620c75..11c29ee812 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -625,7 +625,8 @@ void QQuickPointerHandler::handlePointerEvent(QPointerEvent *event)
if (wants) {
handlePointerEventImpl(event);
} else {
- setActive(false);
+ if (event->type() != QEvent::NativeGesture)
+ setActive(false);
for (int i = 0; i < event->pointCount(); ++i) {
auto &pt = event->point(i);
if (event->exclusiveGrabber(pt) == this && pt.state() != QEventPoint::Stationary) {
diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
index cd24d8643e..3c46c415f6 100644
--- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp
@@ -50,6 +50,7 @@ private slots:
void pinchProperties();
void scale();
void scaleThreeFingers();
+ void scaleNativeGesture();
void pan();
void dragAxesEnabled_data();
void dragAxesEnabled();
@@ -353,6 +354,79 @@ void tst_QQuickPinchHandler::scaleThreeFingers()
QCOMPARE(pinchHandler->active(), false);
}
+void tst_QQuickPinchHandler::scaleNativeGesture()
+{
+ QQuickView *window = createView();
+ QScopedPointer<QQuickView> scope(window);
+ window->setSource(testFileUrl("pinchproperties.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+ QVERIFY(window->rootObject() != nullptr);
+ qApp->processEvents();
+
+ QQuickPinchHandler *pinchHandler = window->rootObject()->findChild<QQuickPinchHandler*>("pinchHandler");
+ QVERIFY(pinchHandler != nullptr);
+ QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
+ QVERIFY(root != nullptr);
+ QQuickItem *target = window->rootObject()->findChild<QQuickItem*>("blackrect");
+ QVERIFY(target != nullptr);
+
+ QPointF targetPos = target->position();
+ ulong ts = 1;
+
+ // first pinch: scale it up
+ const qreal expectedScale = 1.1;
+ QPointF pinchPos(75, 75);
+ QPointF pinchLocalPos = target->mapFromScene(pinchPos);
+ // target position is adjusted in QQuickItemPrivate::adjustedPosForTransform()
+ // so as to compensate for the change in size, to hold the centroid in place
+ const QPointF expectedPos = targetPos + QPointF( (pinchPos.x() - target->x()) * (expectedScale - 1),
+ (pinchPos.y() - target->y()) * (expectedScale - 1) );
+ QWindowSystemInterface::handleGestureEvent(window, ts++, QPointingDevice::primaryPointingDevice(),
+ Qt::BeginNativeGesture, pinchPos, pinchPos);
+ QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, QPointingDevice::primaryPointingDevice(),
+ Qt::ZoomNativeGesture, expectedScale - 1, pinchPos, pinchPos);
+ QTRY_COMPARE(target->scale(), expectedScale);
+ QCOMPARE(pinchHandler->active(), true);
+ QCOMPARE(pinchHandler->centroid().position(), pinchLocalPos);
+ QCOMPARE(pinchHandler->centroid().scenePosition(), pinchPos);
+ QVERIFY(qAbs(target->position().x() - expectedPos.x()) < 0.001);
+ QVERIFY(qAbs(target->position().y() - expectedPos.y()) < 0.001);
+ QCOMPARE(pinchHandler->scale(), expectedScale);
+ QCOMPARE(pinchHandler->activeScale(), expectedScale);
+ QCOMPARE(pinchHandler->translation(), QVector2D());
+ QCOMPARE(pinchHandler->rotation(), 0);
+ QWindowSystemInterface::handleGestureEvent(window, ts++, QPointingDevice::primaryPointingDevice(),
+ Qt::EndNativeGesture, pinchPos, pinchPos);
+ QTRY_COMPARE(pinchHandler->active(), false);
+ QCOMPARE(target->scale(), expectedScale);
+ QCOMPARE(pinchHandler->scale(), expectedScale);
+ QCOMPARE(pinchHandler->activeScale(), 1);
+ QCOMPARE(pinchHandler->translation(), QVector2D());
+ QCOMPARE(pinchHandler->rotation(), 0);
+
+ // second pinch at a different position: scale it down to original size again
+ const qreal reverseScale = (1 / expectedScale);
+ pinchPos = QPointF(125, 125);
+ pinchLocalPos = target->mapFromScene(pinchPos);
+ QWindowSystemInterface::handleGestureEvent(window, ts++, QPointingDevice::primaryPointingDevice(),
+ Qt::BeginNativeGesture, pinchPos, pinchPos);
+ QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, QPointingDevice::primaryPointingDevice(),
+ Qt::ZoomNativeGesture, reverseScale - 1, pinchPos, pinchPos);
+ QTRY_COMPARE(target->scale(), 1);
+ QCOMPARE(pinchHandler->active(), true);
+ QCOMPARE(pinchHandler->centroid().position(), pinchLocalPos);
+ QCOMPARE(pinchHandler->centroid().scenePosition(), pinchPos);
+ QCOMPARE(pinchHandler->scale(), 1);
+ QCOMPARE(pinchHandler->activeScale(), reverseScale);
+ QWindowSystemInterface::handleGestureEvent(window, ts++, QPointingDevice::primaryPointingDevice(),
+ Qt::EndNativeGesture, pinchPos, pinchPos);
+ QTRY_COMPARE(pinchHandler->active(), false);
+ QCOMPARE(target->scale(), 1);
+ QCOMPARE(pinchHandler->scale(), 1);
+ QCOMPARE(pinchHandler->activeScale(), 1);
+}
+
void tst_QQuickPinchHandler::pan()
{
QQuickView *window = createView();