aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@theqtcompany.com>2015-08-11 18:14:02 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2016-09-01 20:15:23 +0000
commit678d3de4b53f22ead68a0874df2892137ad3b7f7 (patch)
tree3668f51e1f0c9490433e1084b96693958731931d
parent149911b0f45fbe1d1a4766a0bb0c9b7c3b54366d (diff)
Introduce PinchHandler
Similar to PinchArea but with improvements: - Allows rotating and scaling about an arbitrary point rather than the center - It's possible to require more than 2 fingers. E.g. maybe you use 3 fingers to manage a window, 2 fingers to manipulate content inside. This could be achieved with two independent PinchHandlers. Change-Id: Ifd40cfee115d7bc298378b26a58318bea40a8230 Reviewed-by: Jan Arve Sæther <jan-arve.saether@theqtcompany.com>
-rw-r--r--src/quick/handlers/handlers.pri2
-rw-r--r--src/quick/handlers/qquickhandlersmodule.cpp2
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp201
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h131
-rw-r--r--tests/manual/pointer/main.qml1
-rw-r--r--tests/manual/pointer/pinchHandler.qml93
-rw-r--r--tests/manual/pointer/qml.qrc2
7 files changed, 431 insertions, 1 deletions
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
index 3d7e656071..f32f330d4e 100644
--- a/src/quick/handlers/handlers.pri
+++ b/src/quick/handlers/handlers.pri
@@ -3,6 +3,7 @@ HEADERS += \
$$PWD/qquickpointerhandler_p.h \
$$PWD/qquickpointerdevicehandler_p.h \
$$PWD/qquickmultipointerhandler_p.h \
+ $$PWD/qquickpinchhandler_p.h \
$$PWD/qquickpointersinglehandler_p.h \
$$PWD/qquickhandlersmodule_p.h \
$$PWD/qquickpointerdevicehandler_p.h \
@@ -13,5 +14,6 @@ SOURCES += \
$$PWD/qquickpointerhandler.cpp \
$$PWD/qquickpointerdevicehandler.cpp \
$$PWD/qquickmultipointerhandler.cpp \
+ $$PWD/qquickpinchhandler.cpp \
$$PWD/qquickpointersinglehandler.cpp \
$$PWD/qquickhandlersmodule.cpp \
diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp
index 398ca0484f..97d6f1d499 100644
--- a/src/quick/handlers/qquickhandlersmodule.cpp
+++ b/src/quick/handlers/qquickhandlersmodule.cpp
@@ -40,6 +40,7 @@
#include "qquickhandlersmodule_p.h"
#include "qquickpointerhandler_p.h"
#include "qquickdraghandler_p.h"
+#include "qquickpinchhandler_p.h"
static void initResources()
{
@@ -78,6 +79,7 @@ static void qt_quickhandlers_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickDragHandler>(uri,major,minor,"DragHandler");
qmlRegisterUncreatableType<QQuickDragAxis>(uri, major, minor, "DragAxis",
QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler"));
+ qmlRegisterType<QQuickPinchHandler>(uri,major,minor,"PinchHandler");
}
void QQuickHandlersModule::defineModule()
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
new file mode 100644
index 0000000000..ddec7eee87
--- /dev/null
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qquickpinchhandler_p.h"
+#include <QtQuick/qquickwindow.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qquickitem_p.h>
+#include <private/qguiapplication_p.h>
+#include <private/qquickwindow_p.h>
+#include <QEvent>
+#include <QMouseEvent>
+#include <QDebug>
+#include <qpa/qplatformnativeinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
+
+/*!
+ \qmltype PinchHandler
+ \instantiates QQuickPinchHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-handlers
+ \brief Handler for pinch gestures
+
+ PinchHandler is a handler that is used to interactively rotate and zoom an Item.
+*/
+
+QQuickPinchHandler::QQuickPinchHandler(QObject *parent)
+ : QQuickMultiPointerHandler(parent, 2)
+ , m_startScale(1)
+ , m_startRotation(0)
+ , m_minimumScale(-qInf())
+ , m_maximumScale(qInf())
+ , m_minimumRotation(-qInf())
+ , m_maximumRotation(qInf())
+ , m_pinchOrigin(PinchCenter)
+{
+ connect(this, &QQuickPinchHandler::activeChanged, this, &QQuickPinchHandler::onActiveChanged);
+ connect(this, &QQuickPinchHandler::targetChanged, this, &QQuickPinchHandler::onTargetChanged);
+}
+
+QQuickPinchHandler::~QQuickPinchHandler()
+{
+}
+
+void QQuickPinchHandler::setMinimumScale(qreal minimumScale)
+{
+ if (m_minimumScale == minimumScale)
+ return;
+
+ m_minimumScale = minimumScale;
+ emit minimumScaleChanged();
+}
+
+void QQuickPinchHandler::setMaximumScale(qreal maximumScale)
+{
+ if (m_maximumScale == maximumScale)
+ return;
+
+ m_maximumScale = maximumScale;
+ emit maximumScaleChanged();
+}
+
+void QQuickPinchHandler::setMinimumRotation(qreal minimumRotation)
+{
+ if (m_minimumRotation == minimumRotation)
+ return;
+
+ m_minimumRotation = minimumRotation;
+ emit minimumRotationChanged();
+}
+
+void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation)
+{
+ if (m_maximumRotation == maximumRotation)
+ return;
+
+ m_maximumRotation = maximumRotation;
+ emit maximumRotationChanged();
+}
+
+void QQuickPinchHandler::setPinchOrigin(QQuickPinchHandler::PinchOrigin pinchOrigin)
+{
+ if (m_pinchOrigin == pinchOrigin)
+ return;
+
+ m_pinchOrigin = pinchOrigin;
+ emit pinchOriginChanged();
+}
+
+/*!
+ \qmlproperty QQuickPinchHandler::minimumTouchPoints
+
+ 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.
+*/
+
+/*!
+ \qmlproperty QQuickPinchHandler::active
+*/
+
+void QQuickPinchHandler::onActiveChanged()
+{
+ if (active()) {
+ m_startScale = m_scaleTransform.xScale(); // TODO incompatible with independent x/y scaling
+ m_startRotation = m_rotationTransform.angle();
+ qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation;
+ grabPoints(m_currentPoints);
+ }
+}
+
+void QQuickPinchHandler::onTargetChanged()
+{
+ if (target()) {
+ // TODO if m_target was previously set differently,
+ // does prepending to the new target remove it from the old one?
+ // If not, should we fix that there, or here?
+ m_scaleTransform.prependToItem(target());
+ m_rotationTransform.prependToItem(target());
+ }
+}
+
+void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ Q_UNUSED(event)
+
+ if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) {
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPos() << "->" << point->scenePos();
+ }
+
+ // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter
+ QPointF startCentroid = startingCentroid();
+ QPointF centroid = touchPointCentroid();
+ QVector3D origin(centroid);
+
+ qreal startDist = averageStartingDistance(startCentroid);
+ qreal dist = averageTouchPointDistance(centroid);
+ qreal zoom = dist / startDist;
+
+ qreal newScale = qBound(m_minimumScale, zoom * m_startScale, m_maximumScale);
+ m_scaleTransform.setOrigin(origin);
+ // TODO optionally allow separate x and y scaling?
+ m_scaleTransform.setXScale(newScale);
+ m_scaleTransform.setYScale(newScale);
+
+ qreal startAngle = averageStartingAngle(startCentroid);
+ qreal angle = averageTouchPointAngle(centroid);
+ qreal angleDelta = startAngle - angle;
+ m_rotationTransform.setOrigin(origin);
+ m_rotationTransform.setAngle(qMin(m_maximumRotation, qMax(m_minimumRotation, m_startRotation + angleDelta)));
+
+ // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place
+
+ qCDebug(lcPinchHandler) << "startCentroid" << startCentroid << "centroid" << centroid << "dist" << dist << "starting dist" << startDist
+ << "zoom" << zoom << "startScale" << m_startScale << "startAngle" << startAngle << "angle" << angle
+ << "scale" << scale() << "rotation" << rotation();
+
+ emit updated();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h
new file mode 100644
index 0000000000..0a66d5e02e
--- /dev/null
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QQUICKPINCHHANDLER_H
+#define QQUICKPINCHHANDLER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickitem.h"
+#include "qevent.h"
+#include "qquickmultipointerhandler_p.h"
+#include <private/qquicktranslate_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointerHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged)
+ Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged)
+ Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged)
+ Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged)
+ Q_PROPERTY(PinchOrigin pinchOrigin READ pinchOrigin WRITE setPinchOrigin NOTIFY pinchOriginChanged)
+ Q_PROPERTY(QPointF center READ center NOTIFY updated)
+ Q_PROPERTY(qreal scale READ scale NOTIFY updated)
+ Q_PROPERTY(qreal rotation READ rotation NOTIFY updated)
+
+public:
+ enum PinchOrigin {
+ FirstPoint, PinchCenter, TargetCenter
+ };
+ Q_ENUM(PinchOrigin)
+
+ QQuickPinchHandler(QObject *parent = 0);
+ ~QQuickPinchHandler();
+
+ qreal minimumScale() const { return m_minimumScale; }
+ void setMinimumScale(qreal minimumScale);
+
+ qreal maximumScale() const { return m_maximumScale; }
+ void setMaximumScale(qreal maximumScale);
+
+ qreal minimumRotation() const { return m_minimumRotation; }
+ void setMinimumRotation(qreal minimumRotation);
+
+ qreal maximumRotation() const { return m_maximumRotation; }
+ void setMaximumRotation(qreal maximumRotation);
+
+ PinchOrigin pinchOrigin() const { return m_pinchOrigin; }
+ void setPinchOrigin(PinchOrigin pinchOrigin);
+
+ QPointF center() const { return m_scaleTransform.origin().toPointF(); }
+ qreal scale() const { return m_scaleTransform.xScale(); }
+ qreal rotation() const { return m_rotationTransform.angle(); }
+
+signals:
+ void requiredPointCountChanged();
+ void minimumScaleChanged();
+ void maximumScaleChanged();
+ void minimumRotationChanged();
+ void maximumRotationChanged();
+ void pinchOriginChanged();
+ void updated();
+
+protected:
+ void onActiveChanged();
+ void onTargetChanged();
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+
+private:
+ qreal m_startScale;
+ qreal m_startRotation;
+ qreal m_minimumScale;
+ qreal m_maximumScale;
+ qreal m_minimumRotation;
+ qreal m_maximumRotation;
+ PinchOrigin m_pinchOrigin;
+ QQuickScale m_scaleTransform;
+ QQuickRotation m_rotationTransform;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPinchHandler)
+
+#endif // QQUICKPINCHHANDLER_H
diff --git a/tests/manual/pointer/main.qml b/tests/manual/pointer/main.qml
index efe603c57f..45a339b302 100644
--- a/tests/manual/pointer/main.qml
+++ b/tests/manual/pointer/main.qml
@@ -52,6 +52,7 @@ Window {
Component.onCompleted: {
addExample("joystick", "DragHandler: move one item inside another with any pointing device", Qt.resolvedUrl("joystick.qml"))
addExample("mixer", "mixing console", Qt.resolvedUrl("mixer.qml"))
+ addExample("pinch", "PinchHandler: scale, rotate and drag", Qt.resolvedUrl("pinchHandler.qml"))
}
}
}
diff --git a/tests/manual/pointer/pinchHandler.qml b/tests/manual/pointer/pinchHandler.qml
new file mode 100644
index 0000000000..e4482a249d
--- /dev/null
+++ b/tests/manual/pointer/pinchHandler.qml
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the manual tests of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import Qt.labs.handlers 1.0
+
+Rectangle {
+ id: root
+ width: 1024; height: 600
+ color: "black"
+
+ Rectangle {
+ width: 400
+ height: 300
+ color: "lightsteelblue"
+ antialiasing: true
+
+ Text {
+ anchors.centerIn: parent
+ text: "Pinch with 2 fingers to scale & rotate\nDrag with 1 finger\ncurrent rotation " + pinch2.rotation.toFixed(1)
+ }
+
+ DragHandler { objectName: "DragHandler" }
+
+ PinchHandler {
+ id: pinch2
+ objectName: "2-finger pinch"
+ minimumRotation: -45
+ maximumRotation: 45
+ minimumScale: 0.1
+ maximumScale: 10
+ pointDistanceThreshold: 150
+ }
+ }
+
+ Rectangle {
+ x: 512
+ width: 400
+ height: 300
+ color: "wheat"
+ antialiasing: true
+
+ Text {
+ anchors.centerIn: parent
+ text: "Pinch with 3 fingers to scale & rotate\ncurrent rotation " + pinch3.rotation.toFixed(1)
+ }
+
+ PinchHandler {
+ id: pinch3
+ objectName: "3-finger pinch"
+ requiredPointCount: 3
+ minimumScale: 0.1
+ maximumScale: 10
+ }
+ }
+}
diff --git a/tests/manual/pointer/qml.qrc b/tests/manual/pointer/qml.qrc
index b802a73f08..dac6b08b12 100644
--- a/tests/manual/pointer/qml.qrc
+++ b/tests/manual/pointer/qml.qrc
@@ -2,8 +2,8 @@
<qresource prefix="/">
<file>main.qml</file>
<file>joystick.qml</file>
- <file>main.qml</file>
<file>mixer.qml</file>
+ <file>pinchHandler.qml</file>
<file>content/Slider.qml</file>
<file>resources/mixer-knob.png</file>
<file>resources/redball.png</file>