Standardize Drag/PinchHandler active/persistent scale, rotation, translation

PinchHandler.scale is persistent between gestures, whereas rotation and
translation were active-gesture properties; that wasn't consistent.
We fixed up DragHandler in just this way in 6.2.

The translationChanged() signal now comes with a vector delta, which is
often useful when writing an onTranslationChanged JS handler. Likewise,
scaleChanged() and rotationChanged() come with qreal deltas. The
scaleChanged() delta is multiplicative rather than additive, because
an additive delta would not be useful in cases when some target item's
scale can be adjusted by alternative means: you need to multiply it
in your onScaleChanged function.

Now that PinchHandler has 4 axes as grouped properties, some properties
in the handlers themselves begin to look redundant; but at least the
translation properties are useful to group x and y together. So in this
patch we continue to follow the pattern that was set in
60d11e1f69. PinchHandler.scale is
equivalent to persistentScale, whereas rotation is equivalent to
activeRotation; so we have a reason to deprecate those properties, as in
ea63ee5233.

The persistent values have setters, to provide another way for
applications to compensate when the values are adjusted by other means,
as an alternative to incremental changes via a script such as
rotationAxis.onValueDelta, onRotationChanged etc.

[ChangeLog][QtQuick][Event Handlers] PinchHandler.activeTranslation now
holds the amount of movement since the pinch gesture began.
PinchHandler.persistentTranslation holds the accumulated sum of
movement that has occurred during subsequent pinch gestures, and can
be set to arbitrary values between gestures. Likewise, activeScale,
persistentScale, activeRotation and persistentRotation follow the
same pattern. The scaleChanged, rotationChanged, and translationChanged
signals include delta arguments, which are useful for incrementally
adjusting a non-default item property when the target is null.

Fixes: QTBUG-76739
Task-number: QTBUG-94168
Change-Id: I6aaa1aa3356b85e6d27abc64bfa67781ecb4f062
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Shawn Rutledge 2022-12-01 18:39:02 +01:00
parent 74634a9317
commit a432970b25
10 changed files with 365 additions and 110 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 KiB

View File

@ -1,22 +1,31 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
import QtQuick 2.12
import QtQuick
Item {
width: 640
height: 480
Window {
width: 320; height: 240
visible: true
title: handler.persistentRotation.toFixed(1) + "° " +
handler.persistentTranslation.x.toFixed(1) + ", " +
handler.persistentTranslation.y.toFixed(1) + " " +
(handler.persistentScale * 100).toFixed(1) + "%"
PinchHandler {
id: handler
target: null
persistentScale: 0.25
onTranslationChanged: (delta) => {
image.x -= delta.x
image.y -= delta.y
}
}
Text {
color: handler.active ? "darkgreen" : "black"
text: handler.rotation.toFixed(1) + " degrees\n" +
handler.translation.x.toFixed(1) + ", " + handler.translation.y.toFixed(1) + "\n" +
(handler.scale * 100).toFixed(1) + "%"
Image {
id: image
source: "images/album-cover.jpg"
scale: handler.persistentScale
x: -600; y: -450
}
}
//![0]

View File

@ -0,0 +1,17 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//![0]
import QtQuick
Rectangle {
width: 100; height: 100
color: "lightsteelblue"
PinchHandler {
id: handler
target: null
onRotationChanged: (delta) => parent.rotation += delta // add
onScaleChanged: (delta) => parent.scale *= delta // multiply
}
}
//![0]

View File

@ -290,7 +290,7 @@ void QQuickDragHandler::setPersistentTranslation(const QVector2D &trans)
m_xAxis.updateValue(m_xAxis.activeValue(), trans.x());
m_yAxis.updateValue(m_yAxis.activeValue(), trans.y());
emit translationChanged();
emit translationChanged({});
}
void QQuickDragHandler::setActiveTranslation(const QVector2D &trans)
@ -304,7 +304,7 @@ void QQuickDragHandler::setActiveTranslation(const QVector2D &trans)
qCDebug(lcDragHandler) << "translation: delta" << delta
<< "active" << trans << "accumulated" << persistentTranslation();
emit translationChanged();
emit translationChanged(delta);
}
/*!

View File

@ -65,7 +65,7 @@ public:
void enforceConstraints();
Q_SIGNALS:
void translationChanged();
void translationChanged(QVector2D delta);
Q_REVISION(2, 14) void snapModeChanged();
protected:

View File

@ -96,6 +96,78 @@ void QQuickPinchHandler::setMaximumScale(qreal maximumScale)
emit maximumScaleChanged();
}
/*!
\readonly
\qmlproperty real QtQuick::PinchHandler::activeScale
The scale factor while the pinch gesture is being performed.
It is 1.0 when the gesture begins, increases as the touchpoints are spread
apart, and decreases as the touchpoints are brought together.
If \l target is not null, its \l {Item::scale}{scale} will be automatically
multiplied by this value.
Otherwise, bindings can be used to do arbitrary things with this value.
\sa QtQuick::PinchHandler::scaleAxis.activeValue
*/
void QQuickPinchHandler::setActiveScale(qreal scale)
{
if (scale == activeScale())
return;
qreal delta = scale / m_scaleAxis.activeValue();
m_scaleAxis.updateValue(scale, m_scaleAxis.m_startValue * scale, delta);
emit scaleChanged(delta);
}
/*!
\qmlsignal QtQuick::PinchHandler::scaleChanged(qreal delta)
The \c scaleChanged signal is emitted when \l activeScale (and therefore
\l persistentScale) changes. The \a delta value gives the multiplicative
change in scale. For example, if the user moves fingers to change the pinch
distance so that \c activeScale changes from 2 to 2.5, \c
scaleChanged(1.25) will be emitted. You can use that to incrementally
change the scale of an item:
\snippet pointerHandlers/pinchHandlerScaleOrRotationChanged.qml 0
\note If you set the \l persistentScale property directly, \c delta is \c 1.
*/
/*!
\readonly
\qmlproperty QVector2D QtQuick::PinchHandler::scale
\deprecated [6.5] Use persistentScale
*/
/*!
\readonly
\qmlproperty real QtQuick::PinchHandler::persistentScale
The scale factor that will automatically be set on the \l target if it is not null.
Otherwise, bindings can be used to do arbitrary things with this value.
While the pinch gesture is being performed, it is continuously multiplied by
\l activeScale; after the gesture ends, it stays the same; and when the next
pinch gesture begins, it begins to be multiplied by activeScale again.
It's possible to set this property, as a way of synchronizing the basis
scale with a scale that was set in some other way, for example by another
handler. If you set this property directly, \c activeScale does not change,
and \c scaleChanged(1) is emitted.
\sa QtQuick::PinchHandler::scaleAxis.persistentValue
*/
void QQuickPinchHandler::setPersistentScale(qreal rot)
{
if (rot == persistentScale())
return;
m_scaleAxis.updateValue(m_scaleAxis.activeValue(), rot);
emit scaleChanged(1);
}
/*!
\qmlproperty real QtQuick::PinchHandler::minimumRotation
@ -126,6 +198,136 @@ void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation)
emit maximumRotationChanged();
}
/*!
\qmlsignal QtQuick::PinchHandler::rotationChanged(qreal delta)
The \c rotationChanged signal is emitted when \l activeRotation (and
therefore \l persistentRotation) changes. The \a delta value gives the
additive change in rotation. For example, if the user moves fingers to
change the pinch distance so that \c activeRotation changes from 10 to 30
degrees, \c rotationChanged(20) will be emitted. You can use that to
incrementally change the rotation of an item:
\snippet pointerHandlers/pinchHandlerScaleOrRotationChanged.qml 0
\note If you set the \l persistentRotation property directly, \c delta is \c 0.
*/
/*!
\readonly
\qmlproperty QVector2D QtQuick::PinchHandler::rotation
\deprecated [6.5] Use activeRotation
*/
/*!
\readonly
\qmlproperty real QtQuick::PinchHandler::activeRotation
The rotation of the pinch gesture in degrees, with positive values clockwise.
It is \c 0 when the gesture begins. If \l target is not null, this will be
automatically added to its \l {Item::rotation}{rotation}. Otherwise,
bindings can be used to do arbitrary things with this value.
\sa QtQuick::PinchHandler::rotationAxis.activeValue
*/
void QQuickPinchHandler::setActiveRotation(qreal rot)
{
if (rot == activeRotation())
return;
qreal delta = rot - m_rotationAxis.activeValue();
m_rotationAxis.updateValue(rot, m_rotationAxis.m_startValue + rot, delta);
emit rotationChanged(delta);
}
/*!
\readonly
\qmlproperty real QtQuick::PinchHandler::persistentRotation
The rotation to be applied to the \l target if it is not null.
Otherwise, bindings can be used to do arbitrary things with this value.
While the pinch gesture is being performed, \l activeRotation is continuously
added; after the gesture ends, it stays the same; and when the next
pinch gesture begins, it begins to be modified by activeRotation again.
\sa QtQuick::PinchHandler::rotationAxis.persistentValue
It's possible to set this property, as a way of synchronizing the basis
rotation with a rotation that was set in some other way, for example by
another handler. If you set this property directly, \c activeRotation does
not change, and \c rotationChanged(0) is emitted.
*/
void QQuickPinchHandler::setPersistentRotation(qreal rot)
{
if (rot == persistentRotation())
return;
m_rotationAxis.updateValue(m_rotationAxis.activeValue(), rot);
emit rotationChanged(0);
}
/*!
\qmlsignal QtQuick::PinchHandler::translationChanged(QVector2D delta)
The \c translationChanged signal is emitted when \l activeTranslation (and
therefore \l persistentTranslation) changes. The \a delta vector gives the
change in translation. You can use that to incrementally change the
position of an item:
\snippet pointerHandlers/pinchHandlerNullTarget.qml 0
\note If you set the \l persistentTranslation property directly,
\c delta is \c {0, 0}.
*/
/*!
\readonly
\qmlproperty QVector2D QtQuick::PinchHandler::translation
\deprecated [6.5] Use activeTranslation
*/
/*!
\readonly
\qmlproperty QPointF QtQuick::PinchHandler::activeTranslation
The translation of the cluster of points while the pinch gesture is being
performed. It is \c {0, 0} when the gesture begins, and increases as the
event point(s) are dragged downward and to the right. After the gesture
ends, it stays the same; and when the next pinch gesture begins, it is
reset to \c {0, 0} again.
\note On some touchpads, such as on a \macos trackpad, native gestures do
not generate any translation values, and this property stays at \c (0, 0).
*/
/*!
\qmlproperty QPointF QtQuick::PinchHandler::persistentTranslation
The translation to be applied to the \l target if it is not \c null.
Otherwise, bindings can be used to do arbitrary things with this value.
While the pinch gesture is being performed, \l activeTranslation is
continuously added to it; after the gesture ends, it stays the same.
It's possible to set this property, as a way of synchronizing the basis
translation with a translation that was set in some other way, for example
by another handler. If you set this property directly, \c activeTranslation
does not change, and \c translationChanged({0, 0}) is emitted.
\note On some touchpads, such as on a \macos trackpad, native gestures do
not generate any translation values, and this property stays at \c (0, 0).
*/
void QQuickPinchHandler::setPersistentTranslation(const QPointF &trans)
{
if (trans == persistentTranslation())
return;
m_xAxis.updateValue(m_xAxis.activeValue(), trans.x());
m_yAxis.updateValue(m_yAxis.activeValue(), trans.y());
emit translationChanged({});
}
bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event)
{
if (!QQuickMultiPointHandler::wantsPointerEvent(event))
@ -221,7 +423,7 @@ bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event)
\c maximum is the maximum acceptable scale.
If \c enabled is true, scaling is allowed.
\c activeValue is the same as \l {QtQuick::PinchHandler::activeScale}.
\c persistentValue is the same as \l {QtQuick::PinchHandler::scale}.
\c persistentValue is the same as \l {QtQuick::PinchHandler::persistentScale}.
The \c activeValueChanged signal is emitted when \c activeValue (and therefore
\c persistentValue) changes, to provide the multiplier for the incremental change.
@ -249,8 +451,8 @@ bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event)
\c minimum is the minimum acceptable rotation.
\c maximum is the maximum acceptable rotation.
If \c enabled is true, rotation is allowed.
\c activeValue is the same as \l {QtQuick::PinchHandler::rotation}.
\c persistentValue holds the accumulated value across multiple gestures.
\c activeValue is the same as \l {QtQuick::PinchHandler::activeRotation}.
\c persistentValue is the same as \l {QtQuick::PinchHandler::persistentRotation}.
The \c activeValueChanged signal is emitted when \c activeValue (and therefore
\c persistentValue) changes, to provide the increment by which it changed.
@ -324,11 +526,10 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
emit updated();
return;
case Qt::ZoomNativeGesture:
m_scaleAxis.updateValue(1 + gesture->value(), m_scaleAxis.m_startValue * (1 + gesture->value()));
setActiveScale(1 + gesture->value());
break;
case Qt::RotateNativeGesture:
m_rotationAxis.updateValue(m_rotationAxis.m_activeValue + gesture->value(),
m_rotationAxis.m_startValue + m_rotationAxis.m_activeValue + gesture->value());
setActiveRotation(m_rotationAxis.activeValue() + gesture->value());
break;
default:
// Nothing of interest (which is unexpected, because wantsPointerEvent() should have returned false)
@ -461,17 +662,14 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
activeScale = dist / m_startDistance;
activeScale = qBound(m_scaleAxis.minimum() / m_scaleAxis.m_startValue, activeScale,
m_scaleAxis.maximum() / m_scaleAxis.m_startValue);
m_scaleAxis.updateValue(activeScale, m_scaleAxis.m_startValue * activeScale,
activeScale / m_scaleAxis.activeValue());
setActiveScale(activeScale);
}
// 2. rotate
if (m_rotationAxis.enabled()) {
QVector<PointData> newAngles = angles(centroid().scenePosition());
const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
m_rotationAxis.updateValue(m_rotationAxis.m_activeValue + angleDelta,
m_rotationAxis.m_startValue + m_rotationAxis.m_activeValue + angleDelta,
angleDelta);
setActiveRotation(m_rotationAxis.m_activeValue + angleDelta);
m_startAngles = std::move(newAngles);
}
@ -484,10 +682,10 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
const QPointF centroidParentPos = target()->parentItem()->mapFromScene(centroid().scenePosition());
// 3. Drag/translate
const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(centroid().sceneGrabPosition());
auto activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos);
auto activeTranslation = centroidParentPos - centroidStartParentPos;
// apply rotation + scaling around the centroid - then apply translation.
QPointF pos = QQuickItemPrivate::get(target())->adjustedPosForTransform(centroidParentPos,
startPos(), activeTranslation,
startPos(), QVector2D(activeTranslation),
m_scaleAxis.m_startValue,
m_scaleAxis.persistentValue() / m_scaleAxis.m_startValue,
m_rotationAxis.m_startValue,
@ -502,18 +700,22 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
else
pos.ry() -= qreal(activeTranslation.y());
m_xAxis.updateValue(activeTranslation.x(), pos.x(), activeTranslation.x() - m_xAxis.activeValue());
m_yAxis.updateValue(activeTranslation.y(), pos.y(), activeTranslation.y() - m_yAxis.activeValue());
const QVector2D delta(activeTranslation.x() - m_xAxis.activeValue(),
activeTranslation.y() - m_yAxis.activeValue());
m_xAxis.updateValue(activeTranslation.x(), pos.x(), delta.x());
m_yAxis.updateValue(activeTranslation.y(), pos.y(), delta.y());
emit translationChanged(delta);
target()->setPosition(QPointF(m_xAxis.persistentValue(), m_yAxis.persistentValue()));
target()->setRotation(m_rotationAxis.persistentValue());
target()->setScale(m_scaleAxis.persistentValue());
} else {
auto activeTranslation = centroid().scenePosition() - centroid().scenePressPosition();
auto accumulated = QPointF(m_xAxis.m_startValue, m_yAxis.m_startValue) + activeTranslation;
m_xAxis.updateValue(activeTranslation.x(), accumulated.x(),
activeTranslation.x() - m_xAxis.activeValue());
m_yAxis.updateValue(activeTranslation.y(), accumulated.y(),
const QVector2D delta(activeTranslation.x() - m_xAxis.activeValue(),
activeTranslation.y() - m_yAxis.activeValue());
m_xAxis.updateValue(activeTranslation.x(), accumulated.x(), delta.x());
m_yAxis.updateValue(activeTranslation.y(), accumulated.y(), delta.y());
emit translationChanged(delta);
}
qCDebug(lcPinchHandler) << "centroid" << centroid().scenePressPosition() << "->" << centroid().scenePosition()
@ -538,50 +740,6 @@ QPointF QQuickPinchHandler::startPos()
The \l target will be rotated around this point.
*/
/*!
\readonly
\qmlproperty real QtQuick::PinchHandler::scale
The scale factor that will automatically be set on the \l target if it is not null.
Otherwise, bindings can be used to do arbitrary things with this value.
While the pinch gesture is being performed, it is continuously multiplied by
\l activeScale; after the gesture ends, it stays the same; and when the next
pinch gesture begins, it begins to be multiplied by activeScale again.
*/
/*!
\readonly
\qmlproperty real QtQuick::PinchHandler::activeScale
The scale factor while the pinch gesture is being performed.
It is 1.0 when the gesture begins, increases as the touchpoints are spread
apart, and decreases as the touchpoints are brought together.
If \l target is not null, its \l {Item::scale}{scale} will be automatically
multiplied by this value.
Otherwise, bindings can be used to do arbitrary things with this value.
*/
/*!
\readonly
\qmlproperty real QtQuick::PinchHandler::rotation
The rotation of the pinch gesture in degrees, with positive values clockwise.
It is 0 when the gesture begins. If \l target is not null, this will be
automatically applied to its \l {Item::rotation}{rotation}. Otherwise,
bindings can be used to do arbitrary things with this value.
*/
/*!
\readonly
\qmlproperty QVector2D QtQuick::PinchHandler::translation
The translation of the gesture \l centroid. It is \c (0, 0) when the
gesture begins.
\note On some touchpads, such as on a \macos trackpad, native gestures do
not generate any translation values, and this property stays at \c (0, 0).
*/
QT_END_NAMESPACE
#include "moc_qquickpinchhandler_p.cpp"

View File

@ -28,45 +28,73 @@ using namespace Qt::StringLiterals;
class Q_QUICK_PRIVATE_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
{
Q_OBJECT
Q_PROPERTY(QQuickDragAxis *scaleAxis READ scaleAxis CONSTANT)
Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged)
Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged)
#if QT_DEPRECATED_SINCE(6, 5)
Q_PROPERTY(qreal scale READ scale NOTIFY updated)
#endif
Q_PROPERTY(qreal activeScale READ activeScale NOTIFY scaleChanged)
Q_PROPERTY(qreal persistentScale READ persistentScale WRITE setPersistentScale NOTIFY scaleChanged)
Q_PROPERTY(QQuickDragAxis *rotationAxis READ rotationAxis CONSTANT)
Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged)
Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged)
Q_PROPERTY(qreal scale READ scale NOTIFY updated)
Q_PROPERTY(qreal activeScale READ activeScale NOTIFY updated)
#if QT_DEPRECATED_SINCE(6, 5)
Q_PROPERTY(qreal rotation READ rotation NOTIFY updated)
Q_PROPERTY(QVector2D translation READ translation NOTIFY updated)
#endif
Q_PROPERTY(qreal activeRotation READ activeRotation NOTIFY rotationChanged)
Q_PROPERTY(qreal persistentRotation READ persistentRotation WRITE setPersistentRotation NOTIFY rotationChanged)
Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT)
Q_PROPERTY(QQuickDragAxis * scaleAxis READ scaleAxis CONSTANT)
Q_PROPERTY(QQuickDragAxis * rotationAxis READ rotationAxis CONSTANT)
#if QT_DEPRECATED_SINCE(6, 5)
Q_PROPERTY(QVector2D translation READ translation NOTIFY updated)
#endif
Q_PROPERTY(QPointF activeTranslation READ activeTranslation NOTIFY translationChanged REVISION(6, 5))
Q_PROPERTY(QPointF persistentTranslation READ persistentTranslation WRITE setPersistentTranslation NOTIFY translationChanged REVISION(6, 5))
QML_NAMED_ELEMENT(PinchHandler)
QML_ADDED_IN_VERSION(2, 12)
public:
explicit QQuickPinchHandler(QQuickItem *parent = nullptr);
qreal minimumScale() const { return m_scaleAxis.minimum(); }
void setMinimumScale(qreal minimumScale);
qreal maximumScale() const { return m_scaleAxis.maximum(); }
void setMaximumScale(qreal maximumScale);
qreal minimumRotation() const { return m_rotationAxis.minimum(); }
void setMinimumRotation(qreal minimumRotation);
qreal maximumRotation() const { return m_rotationAxis.maximum(); }
void setMaximumRotation(qreal maximumRotation);
QVector2D translation() const { return QVector2D(QPointF(m_xAxis.activeValue(), m_yAxis.activeValue())); }
qreal scale() const { return m_scaleAxis.m_accumulatedValue; }
qreal activeScale() const { return m_scaleAxis.m_activeValue; }
qreal rotation() const { return m_rotationAxis.m_accumulatedValue; }
QQuickDragAxis *xAxis() { return &m_xAxis; }
QQuickDragAxis *yAxis() { return &m_yAxis; }
#if QT_DEPRECATED_SINCE(6, 5)
QVector2D translation() const { return QVector2D(activeTranslation()); }
#endif
QPointF activeTranslation() const { return QPointF(m_xAxis.activeValue(), m_yAxis.activeValue()); }
QPointF persistentTranslation() const { return QPointF(m_xAxis.persistentValue(), m_yAxis.persistentValue()); }
void setPersistentTranslation(const QPointF &trans);
QQuickDragAxis *scaleAxis() { return &m_scaleAxis; }
qreal minimumScale() const { return m_scaleAxis.minimum(); }
void setMinimumScale(qreal minimumScale);
qreal maximumScale() const { return m_scaleAxis.maximum(); }
void setMaximumScale(qreal maximumScale);
#if QT_DEPRECATED_SINCE(6, 5)
qreal scale() const { return persistentScale(); }
#endif
qreal activeScale() const { return m_scaleAxis.activeValue(); }
void setActiveScale(qreal scale);
qreal persistentScale() const { return m_scaleAxis.persistentValue(); }
void setPersistentScale(qreal scale);
QQuickDragAxis *rotationAxis() { return &m_rotationAxis; }
qreal minimumRotation() const { return m_rotationAxis.minimum(); }
void setMinimumRotation(qreal minimumRotation);
qreal maximumRotation() const { return m_rotationAxis.maximum(); }
void setMaximumRotation(qreal maximumRotation);
#if QT_DEPRECATED_SINCE(6, 5)
qreal rotation() const { return activeRotation(); }
#endif
qreal activeRotation() const { return m_rotationAxis.activeValue(); }
void setActiveRotation(qreal rot);
qreal persistentRotation() const { return m_rotationAxis.persistentValue(); }
void setPersistentRotation(qreal rot);
Q_SIGNALS:
void minimumScaleChanged();
@ -74,6 +102,9 @@ Q_SIGNALS:
void minimumRotationChanged();
void maximumRotationChanged();
void updated();
Q_REVISION(6, 5) void scaleChanged(qreal delta);
Q_REVISION(6, 5) void rotationChanged(qreal delta);
Q_REVISION(6, 5) void translationChanged(QVector2D delta);
protected:
bool wantsPointerEvent(QPointerEvent *event) override;

View File

@ -273,7 +273,7 @@ void tst_FlickableInterop::touchDragSlider()
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>();
QVERIFY(flickable);
QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped()));
QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged()));
QSignalSpy translationChangedSpy(drag, &QQuickDragHandler::translationChanged);
// Drag the slider in the allowed (vertical) direction
tappedSpy.clear();
@ -353,7 +353,7 @@ void tst_FlickableInterop::mouseDragSlider()
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>();
QVERIFY(flickable);
QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped()));
QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged()));
QSignalSpy translationChangedSpy(drag, &QQuickDragHandler::translationChanged);
// Drag the slider
tappedSpy.clear();
@ -411,7 +411,7 @@ void tst_FlickableInterop::touchDragFlickableBehindSlider()
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>();
QVERIFY(flickable);
QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped()));
QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged()));
QSignalSpy translationChangedSpy(drag, &QQuickDragHandler::translationChanged);
// Button is no longer pressed if touchpoint goes beyond dragThreshold,
// because Flickable steals the grab
@ -457,7 +457,7 @@ void tst_FlickableInterop::mouseDragFlickableBehindSlider()
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>();
QVERIFY(flickable);
QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped()));
QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged()));
QSignalSpy translationChangedSpy(drag, &QQuickDragHandler::translationChanged);
// Button is no longer pressed if touchpoint goes beyond dragThreshold,
// because Flickable steals the grab

View File

@ -141,7 +141,7 @@ void tst_DragHandler::touchDrag()
dragHandler->setDragThreshold(dragThreshold);
}
QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged()));
QSignalSpy translationChangedSpy(dragHandler, &QQuickDragHandler::translationChanged);
QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged()));
QSignalSpy xDeltaSpy(dragHandler->xAxis(), &QQuickDragAxis::activeValueChanged);
@ -189,6 +189,8 @@ void tst_DragHandler::touchDrag()
QCOMPARE(dragHandler->activeTranslation().x(), dragThreshold + 20);
QCOMPARE(dragHandler->persistentTranslation().y(), 0);
QCOMPARE(dragHandler->activeTranslation().y(), 0);
QCOMPARE(translationChangedSpy.size(), 1);
QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(), QVector2D(dragThreshold + 20, 0));
QVERIFY(dragHandler->centroid().velocity().x() > 0);
QCOMPARE(centroidChangedSpy.size(), 4);
QTest::touchEvent(window, touchDevice).release(1, p1, window);
@ -285,7 +287,7 @@ void tst_DragHandler::mouseDrag()
QVERIFY(dragHandler);
dragHandler->setAcceptedButtons(acceptedButtons); // QTBUG-76875
QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged()));
QSignalSpy translationChangedSpy(dragHandler, &QQuickDragHandler::translationChanged);
QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged()));
QSignalSpy xDeltaSpy(dragHandler->xAxis(), &QQuickDragAxis::activeValueChanged);
@ -352,9 +354,11 @@ void tst_DragHandler::mouseDrag()
QTest::mouseRelease(window, static_cast<Qt::MouseButton>(int(dragButton)), Qt::NoModifier, p1);
QTRY_VERIFY(!dragHandler->active());
QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton);
if (shouldDrag)
QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1);
QCOMPARE(translationChangedSpy.size(), shouldDrag ? 1 : 0);
if (shouldDrag) {
QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1);
QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(), QVector2D(dragThreshold + 20, 0));
}
QCOMPARE(xDeltaSpy.size(), shouldDrag ? 1 : 0);
QCOMPARE(centroidChangedSpy.size(), shouldDrag ? 5 : 0);
#if QT_CONFIG(cursor)
@ -390,7 +394,7 @@ void tst_DragHandler::mouseDragThreshold()
dragHandler->setDragThreshold(dragThreshold);
}
QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged()));
QSignalSpy translationChangedSpy(dragHandler, &QQuickDragHandler::translationChanged);
QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged()));
QSignalSpy xDeltaSpy(dragHandler->xAxis(), &QQuickDragAxis::activeValueChanged);
@ -416,6 +420,8 @@ void tst_DragHandler::mouseDragThreshold()
QTest::mouseMove(window, p1);
QTRY_VERIFY(dragHandler->active());
QCOMPARE(translationChangedSpy.size(), dragThreshold ? 0 : 1);
if (!dragThreshold)
QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(), QVector2D(2, 0));
QCOMPARE(xDeltaSpy.size(), dragThreshold ? 0 : 1);
QCOMPARE(centroidChangedSpy.size(), 3);
#if QT_DEPRECATED_SINCE(6, 2)
@ -442,6 +448,9 @@ QT_WARNING_POP
#endif
QCOMPARE(dragHandler->activeTranslation().x(), dragThreshold + (dragThreshold ? 20 : 21));
QCOMPARE(dragHandler->activeTranslation().y(), 0.0);
QCOMPARE(translationChangedSpy.size(), dragThreshold ? 1 : 2);
QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(),
QVector2D(dragThreshold ? dragThreshold + 20 : 2, 0));
QVERIFY(dragHandler->centroid().velocity().x() > 0);
QCOMPARE(centroidChangedSpy.size(), 4);
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
@ -586,7 +595,7 @@ void tst_DragHandler::touchDragMulti()
QVERIFY(ball1);
QQuickDragHandler *dragHandler1 = ball1->findChild<QQuickDragHandler*>();
QVERIFY(dragHandler1);
QSignalSpy translationChangedSpy1(dragHandler1, SIGNAL(translationChanged()));
QSignalSpy translationChangedSpy1(dragHandler1, &QQuickDragHandler::translationChanged);
QSignalSpy centroidChangedSpy1(dragHandler1, SIGNAL(centroidChanged()));
QSignalSpy xDeltaSpy1(dragHandler1->xAxis(), &QQuickDragAxis::activeValueChanged);
@ -594,7 +603,7 @@ void tst_DragHandler::touchDragMulti()
QVERIFY(ball2);
QQuickDragHandler *dragHandler2 = ball2->findChild<QQuickDragHandler*>();
QVERIFY(dragHandler2);
QSignalSpy translationChangedSpy2(dragHandler2, SIGNAL(translationChanged()));
QSignalSpy translationChangedSpy2(dragHandler2, &QQuickDragHandler::translationChanged);
QSignalSpy centroidChangedSpy2(dragHandler1, SIGNAL(centroidChanged()));
QSignalSpy yDeltaSpy2(dragHandler2->yAxis(), &QQuickDragAxis::activeValueChanged);
@ -702,6 +711,10 @@ QT_WARNING_POP
QCOMPARE(xDeltaSpy1.first().first().toReal(), dragThreshold + 20);
QCOMPARE(yDeltaSpy2.size(), 1);
QCOMPARE(yDeltaSpy2.first().first().toReal(), dragThreshold + 20);
QCOMPARE(translationChangedSpy1.size(), 1);
QCOMPARE(translationChangedSpy1.first().first().value<QVector2D>(), QVector2D(dragThreshold + 20, 0));
QCOMPARE(translationChangedSpy2.size(), 1);
QCOMPARE(translationChangedSpy2.first().first().value<QVector2D>(), QVector2D(0, dragThreshold + 20));
touchSeq.release(1, p1, window).stationary(2).commit();
QQuickTouchUtils::flush(window);
QTRY_VERIFY(!dragHandler1->active());

View File

@ -225,6 +225,7 @@ void tst_QQuickPinchHandler::scale()
QQuickItem *blackRect = (hasTarget ? pinchHandler->target() : pinchHandler->parentItem());
QVERIFY(blackRect != nullptr);
QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint)));
QSignalSpy scaleChangedSpy(pinchHandler, &QQuickPinchHandler::scaleChanged);
if (lcPointerTests().isDebugEnabled()) QTest::qWait(500);
QPoint p0(80, 80);
@ -263,6 +264,7 @@ void tst_QQuickPinchHandler::scale()
// move the same point even further and observe the change in scale
for (int i = 0; i < 2; ++i) {
qreal lastScale = pinchHandler->activeScale();
p1 += pd;
pinchSequence.stationary(0).move(1, p1, &window).commit();
QQuickTouchUtils::flush(&window);
@ -270,13 +272,18 @@ void tst_QQuickPinchHandler::scale()
line.setP2(p1);
qreal expectedScale = line.length() / startLength;
qCDebug(lcPointerTests) << "pinchScale" << root->property("pinchScale").toReal()
<< "expected" << expectedScale << "; target scale" << blackRect->scale();
<< "expected" << expectedScale << "; target scale" << blackRect->scale()
<< "increments" << scaleChangedSpy.size()
<< "multiplier" << scaleChangedSpy.last().first().toReal();
QVERIFY(qFloatDistance(root->property("pinchScale").toReal(), expectedScale) < 10);
QVERIFY(qFloatDistance(blackRect->scale(), expectedScale) < 10);
QCOMPARE(pinchHandler->scale(), root->property("pinchScale").toReal());
QCOMPARE(pinchHandler->scale(), pinchHandler->activeScale()); // in sync for the first gesture
QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), pinchHandler->activeScale());
QCOMPARE(pinchHandler->scaleAxis()->activeValue(), pinchHandler->activeScale());
const qreal expectedIncrement = pinchHandler->activeScale() / lastScale;
QCOMPARE(scaleChangedSpy.size(), i + 1);
QCOMPARE(scaleChangedSpy.last().first().toReal(), expectedIncrement);
QPointF expectedCentroid = p0 + (p1 - p0) / 2;
QCOMPARE(pinchHandler->centroid().scenePosition(), expectedCentroid);
}
@ -315,15 +322,21 @@ void tst_QQuickPinchHandler::scale()
line.setP2(p1);
qreal expectedActiveScale = line.length() / startLength;
qCDebug(lcPointerTests) << i << "activeScale" << pinchHandler->activeScale()
<< "expected" << expectedActiveScale << "; scale" << pinchHandler->scale();
<< "expected" << expectedActiveScale << "; scale" << pinchHandler->scale()
<< "increments" << scaleChangedSpy.size()
<< "multiplier" << scaleChangedSpy.last().first().toReal();
QVERIFY(qFloatDistance(pinchHandler->activeScale(), expectedActiveScale) < 10);
QCOMPARE(pinchHandler->scale(), root->property("pinchScale").toReal());
QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), root->property("pinchScale").toReal());
QCOMPARE_NE(pinchHandler->scale(), pinchHandler->activeScale()); // not in sync anymore
QCOMPARE(pinchHandler->scaleAxis()->activeValue(), pinchHandler->activeScale());
const qreal expectedIncrement = pinchHandler->scale() / lastScale;
QCOMPARE(scaleChangedSpy.size(), i + 3);
QCOMPARE(scaleChangedSpy.last().first().toReal(), expectedIncrement);
}
// scale beyond maximumScale
lastScale = pinchHandler->activeScale();
p1 = QPoint(310, 310);
pinchSequence.stationary(0).move(1, p1, &window).commit();
QQuickTouchUtils::flush(&window);
@ -331,6 +344,9 @@ void tst_QQuickPinchHandler::scale()
QCOMPARE(blackRect->scale(), qreal(4));
QCOMPARE(pinchHandler->scale(), qreal(4)); // limited by maximumScale
QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), 4);
const qreal expectedIncrement = pinchHandler->activeScale() / lastScale;
QCOMPARE(scaleChangedSpy.size(), 5);
QCOMPARE(scaleChangedSpy.last().first().toReal(), expectedIncrement);
pinchSequence.release(0, p0, &window).release(1, p1, &window).commit();
QQuickTouchUtils::flush(&window);
QCOMPARE(pinchHandler->active(), false);
@ -530,6 +546,7 @@ void tst_QQuickPinchHandler::pan()
QQuickPinchHandler *pinchHandler = window->rootObject()->findChild<QQuickPinchHandler*>("pinchHandler");
QVERIFY(pinchHandler != nullptr);
QSignalSpy translationChangedSpy(pinchHandler, &QQuickPinchHandler::translationChanged);
QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(root != nullptr);
@ -578,6 +595,8 @@ void tst_QQuickPinchHandler::pan()
// blackrect starts at 50,50
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
QCOMPARE(translationChangedSpy.size(), 1);
QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(), QVector2D(0, 0));
p0 += QPoint(10, 0);
p1 += QPoint(10, 0);
@ -586,6 +605,8 @@ void tst_QQuickPinchHandler::pan()
QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 11, 90));
QCOMPARE(blackRect->x(), 60.0);
QCOMPARE(blackRect->y(), 50.0);
QCOMPARE(translationChangedSpy.size(), 2);
QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(10, 0));
p0 += QPoint(0, 10);
p1 += QPoint(0, 10);
@ -594,6 +615,8 @@ void tst_QQuickPinchHandler::pan()
QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 11, 90 + 10));
QCOMPARE(blackRect->x(), 60.0);
QCOMPARE(blackRect->y(), 60.0);
QCOMPARE(translationChangedSpy.size(), 3);
QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(0, 10));
p0 += QPoint(10, 10);
p1 += QPoint(10, 10);
@ -603,6 +626,8 @@ void tst_QQuickPinchHandler::pan()
QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 21, 90 + 20));
QCOMPARE(blackRect->x(), 70.0);
QCOMPARE(blackRect->y(), 70.0);
QCOMPARE(translationChangedSpy.size(), 4);
QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(10, 10));
}
// pan x beyond bound
@ -613,6 +638,8 @@ void tst_QQuickPinchHandler::pan()
QCOMPARE(blackRect->x(), 140.0);
QCOMPARE(blackRect->y(), 170.0);
QCOMPARE(translationChangedSpy.size(), 5);
QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(100, 100));
QTest::touchEvent(window, touchscreen).release(0, p0, window).release(1, p1, window);
QQuickTouchUtils::flush(window);