aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/handlers/qquickpinchhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/handlers/qquickpinchhandler.cpp')
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp540
1 files changed, 386 insertions, 154 deletions
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index b1dca9a905..8de7390ae4 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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 Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** 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-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickpinchhandler_p.h"
#include <QtQml/qqmlinfo.h>
@@ -85,73 +49,286 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
\image touchpoints-pinchhandler.png
- \sa PinchArea
+ \note The pinch begins when the number of fingers pressed is between
+ \l {MultiPointHandler::minimumPointCount}{minimumPointCount} and
+ \l {MultiPointHandler::maximumPointCount}{maximumPointCount}, inclusive.
+ Until then, PinchHandler tracks the positions of any pressed fingers,
+ but if it's a disallowed number, it does not scale or rotate
+ its \l target, and the \l active property remains \c false.
+
+ \sa PinchArea, QPointerEvent::pointCount(), QNativeGestureEvent::fingerCount(), {Qt Quick Examples - Pointer Handlers}
*/
QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent)
: QQuickMultiPointHandler(parent, 2)
{
+ // Tell QQuickPointerDeviceHandler::wantsPointerEvent() to ignore button state
+ d_func()->acceptedButtons = Qt::NoButton;
}
+#if QT_DEPRECATED_SINCE(6, 5)
/*!
\qmlproperty real QtQuick::PinchHandler::minimumScale
+ \deprecated [6.5] Use scaleAxis.minimum
The minimum acceptable \l {Item::scale}{scale} to be applied
to the \l target.
*/
void QQuickPinchHandler::setMinimumScale(qreal minimumScale)
{
- if (qFuzzyCompare(m_minimumScale, minimumScale))
+ if (qFuzzyCompare(m_scaleAxis.minimum(), minimumScale))
return;
- m_minimumScale = minimumScale;
+ m_scaleAxis.setMinimum(minimumScale);
emit minimumScaleChanged();
}
/*!
\qmlproperty real QtQuick::PinchHandler::maximumScale
+ \deprecated [6.5] Use scaleAxis.maximum
The maximum acceptable \l {Item::scale}{scale} to be applied
to the \l target.
*/
void QQuickPinchHandler::setMaximumScale(qreal maximumScale)
{
- if (qFuzzyCompare(m_maximumScale, maximumScale))
+ if (qFuzzyCompare(m_scaleAxis.maximum(), maximumScale))
return;
- m_maximumScale = maximumScale;
+ m_scaleAxis.setMaximum(maximumScale);
emit maximumScaleChanged();
}
+#endif
+
+/*!
+ \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
+*/
+
+/*!
+ \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.
+*/
+
+void QQuickPinchHandler::setPersistentScale(qreal scale)
+{
+ if (scale == persistentScale())
+ return;
+
+ m_scaleAxis.updateValue(m_scaleAxis.activeValue(), scale);
+ emit scaleChanged(1);
+}
+
+#if QT_DEPRECATED_SINCE(6, 5)
+/*!
\qmlproperty real QtQuick::PinchHandler::minimumRotation
+ \deprecated [6.5] Use rotationAxis.minimum
The minimum acceptable \l {Item::rotation}{rotation} to be applied
to the \l target.
*/
void QQuickPinchHandler::setMinimumRotation(qreal minimumRotation)
{
- if (qFuzzyCompare(m_minimumRotation, minimumRotation))
+ if (qFuzzyCompare(m_rotationAxis.minimum(), minimumRotation))
return;
- m_minimumRotation = minimumRotation;
+ m_rotationAxis.setMinimum(minimumRotation);
emit minimumRotationChanged();
}
/*!
\qmlproperty real QtQuick::PinchHandler::maximumRotation
+ \deprecated [6.5] Use rotationAxis.maximum
The maximum acceptable \l {Item::rotation}{rotation} to be applied
to the \l target.
*/
void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation)
{
- if (qFuzzyCompare(m_maximumRotation, maximumRotation))
+ if (qFuzzyCompare(m_rotationAxis.maximum(), maximumRotation))
return;
- m_maximumRotation = maximumRotation;
+ m_rotationAxis.setMaximum(maximumRotation);
emit maximumRotationChanged();
}
+#endif
+
+/*!
+ \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);
+}
+
+/*!
+ \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.
+
+ 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
+ \l {eventPoint}{eventPoint(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)
{
@@ -161,7 +338,8 @@ bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event)
#if QT_CONFIG(gestures)
if (event->type() == QEvent::NativeGesture) {
const auto gesture = static_cast<const QNativeGestureEvent *>(event);
- if (minimumPointCount() == 2) {
+ if (!gesture->fingerCount() || (gesture->fingerCount() >= minimumPointCount() &&
+ gesture->fingerCount() <= maximumPointCount())) {
switch (gesture->gestureType()) {
case Qt::BeginNativeGesture:
case Qt::EndNativeGesture:
@@ -185,72 +363,144 @@ bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event)
\qmlproperty real QtQuick::PinchHandler::xAxis.minimum
\qmlproperty real QtQuick::PinchHandler::xAxis.maximum
\qmlproperty bool QtQuick::PinchHandler::xAxis.enabled
+ \qmlproperty real QtQuick::PinchHandler::xAxis.activeValue
\c xAxis controls the constraints for horizontal translation of the \l target item.
\c minimum is the minimum acceptable x coordinate of the translation.
\c maximum is the maximum acceptable x coordinate of the translation.
If \c enabled is true, horizontal dragging is allowed.
- */
+
+ The \c activeValueChanged signal is emitted when \c activeValue changes, to
+ provide the increment by which it changed.
+ This is intended for incrementally adjusting one property via multiple handlers.
+
+ \snippet pointerHandlers/pinchHandlerAxisValueDeltas.qml 0
+
+ \note The snippet is contrived: PinchHandler already knows how to move,
+ scale and rotate its parent item, but this code achieves different behavior
+ in a less-declarative way, to illustrate how to use \c activeValueChanged
+ in special cases.
+*/
/*!
\qmlpropertygroup QtQuick::PinchHandler::yAxis
\qmlproperty real QtQuick::PinchHandler::yAxis.minimum
\qmlproperty real QtQuick::PinchHandler::yAxis.maximum
\qmlproperty bool QtQuick::PinchHandler::yAxis.enabled
+ \qmlproperty real QtQuick::PinchHandler::yAxis.activeValue
\c yAxis controls the constraints for vertical translation of the \l target item.
\c minimum is the minimum acceptable y coordinate of the translation.
\c maximum is the maximum acceptable y coordinate of the translation.
If \c enabled is true, vertical dragging is allowed.
- */
+
+ The \c activeValueChanged signal is emitted when \c activeValue changes, to
+ provide the increment by which it changed.
+ This is intended for incrementally adjusting one property via multiple handlers.
+
+ \snippet pointerHandlers/pinchHandlerAxisValueDeltas.qml 0
+
+ \note The snippet is contrived: PinchHandler already knows how to move,
+ scale and rotate its parent item, but this code achieves different behavior
+ in a less-declarative way, to illustrate how to use \c activeValueChanged
+ in special cases.
+*/
/*!
- \qmlproperty int QtQuick::PinchHandler::minimumTouchPoints
+ \qmlpropertygroup QtQuick::PinchHandler::scaleAxis
+ \qmlproperty real QtQuick::PinchHandler::scaleAxis.minimum
+ \qmlproperty real QtQuick::PinchHandler::scaleAxis.maximum
+ \qmlproperty bool QtQuick::PinchHandler::scaleAxis.enabled
+ \qmlproperty real QtQuick::PinchHandler::scaleAxis.activeValue
+
+ \c scaleAxis controls the constraints for setting the \l {QtQuick::Item::scale}{scale}
+ of the \l target item according to the distance between the touchpoints.
+
+ \c minimum is the minimum acceptable scale.
+ \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}.
+
+ The \c activeValueChanged signal is emitted when \c activeValue changes, to
+ provide the multiplier for the incremental change.
+ This is intended for incrementally adjusting one property via multiple handlers.
+
+ \snippet pointerHandlers/pinchHandlerAxisValueDeltas.qml 0
+
+ \note The snippet is contrived: PinchHandler already knows how to move,
+ scale and rotate its parent item, but this code achieves different behavior
+ in a less-declarative way, to illustrate how to use \c activeValueChanged
+ in special cases.
+*/
- The pinch begins when this number of fingers are pressed.
- Until then, PinchHandler tracks the positions of any pressed fingers,
- but if it's an insufficient number, it does not scale or rotate
- its \l target, and the \l active property will remain false.
+/*!
+ \qmlpropertygroup QtQuick::PinchHandler::rotationAxis
+ \qmlproperty real QtQuick::PinchHandler::rotationAxis.minimum
+ \qmlproperty real QtQuick::PinchHandler::rotationAxis.maximum
+ \qmlproperty bool QtQuick::PinchHandler::rotationAxis.enabled
+ \qmlproperty real QtQuick::PinchHandler::rotationAxis.activeValue
+
+ \c rotationAxis controls the constraints for setting the \l {QtQuick::Item::rotation}{rotation}
+ of the \l target item according to the rotation of the group of touchpoints.
+
+ \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::activeRotation}.
+
+ The \c activeValueChanged signal is emitted when \c activeValue changes, to
+ provide the increment by which it changed.
+ This is intended for incrementally adjusting one property via multiple handlers.
+
+ \snippet pointerHandlers/pinchHandlerAxisValueDeltas.qml 0
+
+ \note The snippet is contrived: PinchHandler already knows how to move,
+ scale and rotate its parent item, but this code achieves different behavior
+ in a less-declarative way, to illustrate how to use \c activeValueChanged
+ in special cases.
*/
/*!
+ \readonly
\qmlproperty bool QtQuick::PinchHandler::active
- This property is true when all the constraints (epecially \l minimumTouchPoints)
- are satisfied and the \l target, if any, is being manipulated.
+ This property is \c true when all the constraints (epecially
+ \l {MultiPointHandler::minimumPointCount}{minimumPointCount} and
+ \l {MultiPointHandler::maximumPointCount}{maximumPointCount}) are satisfied
+ and the \l target, if any, is being manipulated.
*/
void QQuickPinchHandler::onActiveChanged()
{
QQuickMultiPointHandler::onActiveChanged();
- if (active()) {
+ const bool curActive = active();
+ m_xAxis.onActiveChanged(curActive, 0);
+ m_yAxis.onActiveChanged(curActive, 0);
+ m_scaleAxis.onActiveChanged(curActive, 1);
+ m_rotationAxis.onActiveChanged(curActive, 0);
+
+ if (curActive) {
m_startAngles = angles(centroid().sceneGrabPosition());
m_startDistance = averageTouchPointDistance(centroid().sceneGrabPosition());
- m_activeRotation = 0;
- m_activeTranslation = QVector2D();
- if (const QQuickItem *t = target()) {
- m_startScale = t->scale(); // TODO incompatible with independent x/y scaling
- m_startRotation = t->rotation();
- m_startPos = t->position();
- } else {
- m_startScale = m_accumulatedScale;
- m_startRotation = 0;
- }
- qCDebug(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation;
+ m_startTargetPos = target() ? target()->position() : QPointF();
+ qCDebug(lcPinchHandler) << "activated with starting scale" << m_scaleAxis.m_startValue
+ << "rotation" << m_rotationAxis.m_startValue
+ << "target pos" << m_startTargetPos;
} else {
- qCDebug(lcPinchHandler) << "deactivated with scale" << m_activeScale << "rotation" << m_activeRotation;
+ m_startTargetPos = QPointF();
+ qCDebug(lcPinchHandler) << "deactivated with scale" << m_scaleAxis.m_activeValue << "rotation" << m_rotationAxis.m_activeValue;
}
}
void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
{
+ QQuickMultiPointHandler::handlePointerEventImpl(event);
if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) {
for (const QQuickHandlerPoint &p : currentPoints())
qCDebug(lcPinchHandler) << Qt::hex << p.id() << p.sceneGrabPosition() << "->" << p.scenePosition();
}
- QQuickMultiPointHandler::handlePointerEventImpl(event);
qreal dist = 0;
#if QT_CONFIG(gestures)
@@ -258,30 +508,26 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
const auto gesture = static_cast<const QNativeGestureEvent *>(event);
mutableCentroid().reset(event, event->point(0));
switch (gesture->gestureType()) {
+ case Qt::BeginNativeGesture:
+ setActive(true);
+ // Native gestures for 2-finger pinch do not allow dragging, so
+ // the centroid won't move during the gesture, and translation stays at zero
+ return;
case Qt::EndNativeGesture:
- m_activeScale = 1;
- m_activeRotation = 0;
- m_activeTranslation = QVector2D();
mutableCentroid().reset();
setActive(false);
emit updated();
return;
case Qt::ZoomNativeGesture:
- m_activeScale *= 1 + gesture->value();
+ setActiveScale(m_scaleAxis.activeValue() * (1 + gesture->value()));
break;
case Qt::RotateNativeGesture:
- m_activeRotation += gesture->value();
+ setActiveRotation(m_rotationAxis.activeValue() + gesture->value());
break;
default:
// Nothing of interest (which is unexpected, because wantsPointerEvent() should have returned false)
return;
}
- if (!active()) {
- setActive(true);
- // Native gestures for 2-finger pinch do not allow dragging, so
- // the centroid won't move during the gesture, and translation stays at zero
- m_activeTranslation = QVector2D();
- }
} else
#endif // QT_CONFIG(gestures)
{
@@ -364,10 +610,12 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
++numberOfPointsDraggedOverThreshold;
}
- const bool requiredNumberOfPointsDraggedOverThreshold = numberOfPointsDraggedOverThreshold >= minimumPointCount() && numberOfPointsDraggedOverThreshold <= maximumPointCount();
- accumulatedMovementMagnitude /= currentPoints().count();
+ const bool requiredNumberOfPointsDraggedOverThreshold =
+ numberOfPointsDraggedOverThreshold >= minimumPointCount() &&
+ numberOfPointsDraggedOverThreshold <= maximumPointCount();
+ accumulatedMovementMagnitude /= currentPoints().size();
- QVector2D avgDrag = accumulatedDrag / currentPoints().count();
+ QVector2D avgDrag = accumulatedDrag / currentPoints().size();
if (!xAxis()->enabled())
avgDrag.setX(0);
if (!yAxis()->enabled())
@@ -377,7 +625,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
qreal distanceToCentroidDelta = qAbs(accumulatedCentroidDistance - m_accumulatedStartCentroidDistance); // Used to detect scale
if (numberOfPointsDraggedOverThreshold >= 1) {
- if (requiredNumberOfPointsDraggedOverThreshold && avgDrag.lengthSquared() >= dragThresholdSquared && accumulatedMovementMagnitude < dragThreshold) {
+ if (requiredNumberOfPointsDraggedOverThreshold &&
+ avgDrag.lengthSquared() >= dragThresholdSquared && accumulatedMovementMagnitude < dragThreshold) {
// Drag
if (grabPoints(event, chosenPoints))
setActive(true);
@@ -400,109 +649,92 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
// avoid mapping the minima and maxima, as they might have unmappable values
// such as -inf/+inf. Because of this we perform the bounding to min/max in local coords.
// 1. scale
- dist = averageTouchPointDistance(centroid().scenePosition());
- m_activeScale = dist / m_startDistance;
- m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale);
+ qreal activeScale = 1;
+ if (m_scaleAxis.enabled()) {
+ dist = averageTouchPointDistance(centroid().scenePosition());
+ activeScale = dist / m_startDistance;
+ activeScale = qBound(m_scaleAxis.minimum() / m_scaleAxis.m_startValue, activeScale,
+ m_scaleAxis.maximum() / m_scaleAxis.m_startValue);
+ setActiveScale(activeScale);
+ }
// 2. rotate
- QVector<PointData> newAngles = angles(centroid().scenePosition());
- const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
- m_activeRotation += angleDelta;
- m_startAngles = std::move(newAngles);
+ if (m_rotationAxis.enabled()) {
+ QVector<PointData> newAngles = angles(centroid().scenePosition());
+ const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
+ setActiveRotation(m_rotationAxis.m_activeValue + angleDelta);
+ m_startAngles = std::move(newAngles);
+ }
if (!containsReleasedPoints)
acceptPoints(chosenPoints);
}
- const qreal totalRotation = m_startRotation + m_activeRotation;
- const qreal rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation);
- m_activeRotation += (rotation - totalRotation); //adjust for the potential bounding above
- m_accumulatedScale = m_startScale * m_activeScale;
if (target() && target()->parentItem()) {
- const QPointF centroidParentPos = target()->parentItem()->mapFromScene(centroid().scenePosition());
+ auto *t = target();
+ const QPointF centroidParentPos = t->parentItem()->mapFromScene(centroid().scenePosition());
// 3. Drag/translate
- const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(centroid().sceneGrabPosition());
- m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos);
+ const QPointF centroidStartParentPos = t->parentItem()->mapFromScene(centroid().sceneGrabPosition());
+ auto activeTranslation = centroidParentPos - centroidStartParentPos;
// apply rotation + scaling around the centroid - then apply translation.
- QPointF pos = QQuickItemPrivate::get(target())->adjustedPosForTransform(centroidParentPos,
- m_startPos, m_activeTranslation,
- m_startScale, m_activeScale,
- m_startRotation, m_activeRotation);
+ QPointF pos = QQuickItemPrivate::get(t)->adjustedPosForTransform(centroidParentPos,
+ m_startTargetPos, QVector2D(activeTranslation),
+ t->scale(), m_scaleAxis.persistentValue() / m_scaleAxis.m_startValue,
+ t->rotation(), m_rotationAxis.persistentValue() - m_rotationAxis.m_startValue);
if (xAxis()->enabled())
pos.setX(qBound(xAxis()->minimum(), pos.x(), xAxis()->maximum()));
else
- pos.rx() -= qreal(m_activeTranslation.x());
+ pos.rx() -= qreal(activeTranslation.x());
if (yAxis()->enabled())
pos.setY(qBound(yAxis()->minimum(), pos.y(), yAxis()->maximum()));
else
- pos.ry() -= qreal(m_activeTranslation.y());
-
- target()->setPosition(pos);
- target()->setRotation(rotation);
- target()->setScale(m_accumulatedScale);
+ pos.ry() -= qreal(activeTranslation.y());
+
+ const QVector2D delta(activeTranslation.x() - m_xAxis.activeValue(),
+ activeTranslation.y() - m_yAxis.activeValue());
+ m_xAxis.updateValue(activeTranslation.x(), m_xAxis.persistentValue() + delta.x(), delta.x());
+ m_yAxis.updateValue(activeTranslation.y(), m_yAxis.persistentValue() + delta.y(), delta.y());
+ emit translationChanged(delta);
+ t->setPosition(pos);
+ t->setRotation(m_rotationAxis.persistentValue());
+ t->setScale(m_scaleAxis.persistentValue());
} else {
- m_activeTranslation = QVector2D(centroid().scenePosition() - centroid().scenePressPosition());
+ auto activeTranslation = centroid().scenePosition() - centroid().scenePressPosition();
+ auto accumulated = QPointF(m_xAxis.m_startValue, m_yAxis.m_startValue) + activeTranslation;
+ 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()
<< ", distance" << m_startDistance << "->" << dist
- << ", startScale" << m_startScale << "->" << m_accumulatedScale
- << ", activeRotation" << m_activeRotation
- << ", rotation" << rotation
+ << ", scale" << m_scaleAxis.m_startValue << "->" << m_scaleAxis.m_accumulatedValue
+ << ", rotation" << m_rotationAxis.m_startValue << "->" << m_rotationAxis.m_accumulatedValue
+ << ", translation" << persistentTranslation()
<< " from " << event->device()->type();
emit updated();
}
/*!
- \readonly
- \qmlproperty QtQuick::HandlerPoint QtQuick::PinchHandler::centroid
-
- A point exactly in the middle of the currently-pressed touch points.
- The \l target will be rotated around this point.
-*/
-
-/*!
- \readonly
- \qmlproperty real QtQuick::PinchHandler::scale
+ \internal
+ \qmlproperty flags QtQuick::PinchHandler::acceptedButtons
- 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.
+ This property is not used in PinchHandler.
*/
/*!
\readonly
- \qmlproperty real QtQuick::PinchHandler::activeScale
+ \qmlproperty QtQuick::handlerPoint QtQuick::PinchHandler::centroid
- 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.
+ A point exactly in the middle of the currently-pressed touch points.
+ The \l target will be rotated around this point.
*/
QT_END_NAMESPACE
+
+#include "moc_qquickpinchhandler_p.cpp"