aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/handlers')
-rw-r--r--src/quick/handlers/handlers.pri20
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp253
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h137
-rw-r--r--src/quick/handlers/qquickhandlersmodule.cpp104
-rw-r--r--src/quick/handlers/qquickhandlersmodule_p.h68
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp314
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p.h118
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp483
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h173
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp196
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p.h96
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp311
-rw-r--r--src/quick/handlers/qquickpointerhandler_p.h125
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp475
-rw-r--r--src/quick/handlers/qquicksinglepointhandler_p.h154
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp363
-rw-r--r--src/quick/handlers/qquicktaphandler_p.h132
17 files changed, 3522 insertions, 0 deletions
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
new file mode 100644
index 0000000000..9e32b9278c
--- /dev/null
+++ b/src/quick/handlers/handlers.pri
@@ -0,0 +1,20 @@
+HEADERS += \
+ $$PWD/qquickdraghandler_p.h \
+ $$PWD/qquickhandlersmodule_p.h \
+ $$PWD/qquickmultipointhandler_p.h \
+ $$PWD/qquickpinchhandler_p.h \
+ $$PWD/qquickpointerdevicehandler_p.h \
+ $$PWD/qquickpointerhandler_p.h \
+ $$PWD/qquicksinglepointhandler_p.h \
+ $$PWD/qquicktaphandler_p.h \
+
+SOURCES += \
+ $$PWD/qquickdraghandler.cpp \
+ $$PWD/qquickhandlersmodule.cpp \
+ $$PWD/qquickmultipointhandler.cpp \
+ $$PWD/qquickpinchhandler.cpp \
+ $$PWD/qquickpointerdevicehandler.cpp \
+ $$PWD/qquickpointerhandler.cpp \
+ $$PWD/qquicksinglepointhandler.cpp \
+ $$PWD/qquicktaphandler.cpp \
+
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
new file mode 100644
index 0000000000..12f1a29bca
--- /dev/null
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 "qquickdraghandler_p.h"
+#include <private/qquickwindow_p.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype DragHandler
+ \instantiates QQuickDragHandler
+ \inherits SinglePointHandler
+ \inqmlmodule Qt.labs.handlers
+ \ingroup qtquick-handlers
+ \brief Handler for dragging
+
+ DragHandler is a handler that is used to interactively move an Item.
+ Like other Pointer Handlers, by default it is fully functional, and
+ manipulates its \l target.
+
+ \snippet pointerHandlers/dragHandler.qml 0
+
+ It has properties to restrict the range of dragging.
+
+ If it is declared within one Item but is assigned a different \l target,
+ then it handles events within the bounds of the \l parent Item but
+ manipulates the \c target Item instead:
+
+ \snippet pointerHandlers/dragHandlerDifferentTarget.qml 0
+
+ A third way to use it is to set \l target to \c null and react to property
+ changes in some other way:
+
+ \snippet pointerHandlers/dragHandlerNullTarget.qml 0
+
+ At this time, drag-and-drop is not yet supported.
+
+ \sa Drag, MouseArea
+*/
+
+QQuickDragHandler::QQuickDragHandler(QObject *parent)
+ : QQuickSinglePointHandler(parent)
+{
+}
+
+QQuickDragHandler::~QQuickDragHandler()
+{
+}
+
+bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point)
+{
+ // If we've already been interested in a point, stay interested, even if it has strayed outside bounds.
+ return ((point->state() != QQuickEventPoint::Pressed && this->point().id() == point->pointId())
+ || QQuickSinglePointHandler::wantsEventPoint(point));
+}
+
+void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+{
+ if (grabber == this && stateChange == QQuickEventPoint::GrabExclusive)
+ // In case the grab got handled over from another grabber, we might not get the Press
+ initializeTargetStartPos(point);
+ enforceConstraints();
+ QQuickSinglePointHandler::onGrabChanged(grabber, stateChange, point);
+}
+
+void QQuickDragHandler::onActiveChanged()
+{
+ if (!active())
+ m_targetStartPos = QPointF();
+}
+
+void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ point->setAccepted();
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ initializeTargetStartPos(point);
+ setPassiveGrab(point);
+ break;
+ case QQuickEventPoint::Updated: {
+ QPointF delta = point->scenePosition() - point->scenePressPosition();
+ if (!m_xAxis.enabled())
+ delta.setX(0);
+ if (!m_yAxis.enabled())
+ delta.setY(0);
+ if (active()) {
+ setTranslation(QVector2D(delta));
+ if (target() && target()->parentItem()) {
+ QPointF pos = target()->parentItem()->mapFromScene(m_targetStartPos + delta);
+ enforceAxisConstraints(&pos);
+ moveTarget(pos, point);
+ }
+ } else if (!point->exclusiveGrabber() &&
+ ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) ||
+ (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point)))) {
+ setExclusiveGrab(point);
+ if (auto parent = parentItem()) {
+ if (point->pointerEvent()->asPointerTouchEvent())
+ parent->setKeepTouchGrab(true);
+ // tablet and mouse are treated the same by Item's legacy event handling, and
+ // touch becomes synth-mouse for Flickable, so we need to prevent stealing
+ // mouse grab too, whenever dragging occurs in an enabled direction
+ parent->setKeepMouseGrab(true);
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+}
+
+void QQuickDragHandler::enforceConstraints()
+{
+ if (!target() || !target()->parentItem())
+ return;
+ QPointF pos = target()->position();
+ QPointF copy(pos);
+ enforceAxisConstraints(&pos);
+ if (pos != copy)
+ target()->setPosition(pos);
+}
+
+void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos)
+{
+ if (m_xAxis.enabled())
+ localPos->setX(qBound(m_xAxis.minimum(), localPos->x(), m_xAxis.maximum()));
+ if (m_yAxis.enabled())
+ localPos->setY(qBound(m_yAxis.minimum(), localPos->y(), m_yAxis.maximum()));
+}
+
+void QQuickDragHandler::initializeTargetStartPos(QQuickEventPoint *point)
+{
+ if (target() && target()->parentItem() && m_targetStartPos.isNull()) { // prefer the m_targetStartPos we got when it got Pressed.
+ m_targetStartPos = target()->parentItem()->mapToScene(target()->position());
+ if (!target()->contains(point->position())) {
+ // If pressed outside of target item, move the target item so that the touchpoint is in its center,
+ // while still respecting the axis constraints.
+ const QPointF center = target()->parentItem()->mapFromScene(point->scenePosition());
+ const QPointF pointCenteredInItemPos = target()->parentItem()->mapToScene(center - QPointF(target()->width(), target()->height())/2);
+ if (m_xAxis.enabled())
+ m_targetStartPos.setX(pointCenteredInItemPos.x());
+ if (m_yAxis.enabled())
+ m_targetStartPos.setY(pointCenteredInItemPos.y());
+ }
+ }
+}
+
+void QQuickDragHandler::setTranslation(const QVector2D &trans)
+{
+ if (trans == m_translation) // fuzzy compare?
+ return;
+ m_translation = trans;
+ emit translationChanged();
+}
+
+/*!
+ \qmlpropertygroup QtQuick::DragHandler::xAxis
+ \qmlpropertygroup QtQuick::DragHandler::yAxis
+ \qmlproperty real QtQuick::DragHandler::DragAxis::minimum
+ \qmlproperty real QtQuick::DragHandler::DragAxis::maximum
+ \qmlproperty real QtQuick::DragHandler::DragAxis::enabled
+
+ \c xAxis and yAxis control the constraints for horizontal and vertical
+ dragging, respectively.
+
+ \value minimum
+ The minimum acceptable value of \l {Item::x}{x} or \l {Item::y}{y}
+ to be applied to the \l target
+ \value maximum
+ The maximum acceptable value of \l {Item::x}{x} or \l {Item::y}{y}
+ to be applied to the \l target
+ \value enabled
+ Whether dragging in this direction is allowed at all
+*/
+QQuickDragAxis::QQuickDragAxis()
+ : m_minimum(-DBL_MAX)
+ , m_maximum(DBL_MAX)
+ , m_enabled(true)
+{
+}
+
+void QQuickDragAxis::setMinimum(qreal minimum)
+{
+ if (m_minimum == minimum)
+ return;
+
+ m_minimum = minimum;
+ emit minimumChanged();
+}
+
+void QQuickDragAxis::setMaximum(qreal maximum)
+{
+ if (m_maximum == maximum)
+ return;
+
+ m_maximum = maximum;
+ emit maximumChanged();
+}
+
+void QQuickDragAxis::setEnabled(bool enabled)
+{
+ if (m_enabled == enabled)
+ return;
+
+ m_enabled = enabled;
+ emit enabledChanged();
+}
+
+/*!
+ \readonly
+ \qmlproperty QVector2D QtQuick::DragHandler::translation
+
+ The translation since the gesture began.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h
new file mode 100644
index 0000000000..d10084c654
--- /dev/null
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 QQUICKDRAGHANDLER_H
+#define QQUICKDRAGHANDLER_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 "qquicksinglepointhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
+ Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+
+public:
+ QQuickDragAxis();
+
+ qreal minimum() const { return m_minimum; }
+ void setMinimum(qreal minimum);
+
+ qreal maximum() const { return m_maximum; }
+ void setMaximum(qreal maximum);
+
+ bool enabled() const { return m_enabled; }
+ void setEnabled(bool enabled);
+
+signals:
+ void minimumChanged();
+ void maximumChanged();
+ void enabledChanged();
+
+private:
+ qreal m_minimum;
+ qreal m_maximum;
+ bool m_enabled;
+};
+
+class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickSinglePointHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
+ Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT)
+ Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)
+
+public:
+ explicit QQuickDragHandler(QObject *parent = 0);
+ ~QQuickDragHandler();
+
+ void handleEventPoint(QQuickEventPoint *point) override;
+
+ QQuickDragAxis *xAxis() { return &m_xAxis; }
+ QQuickDragAxis *yAxis() { return &m_yAxis; }
+
+ QVector2D translation() const { return m_translation; }
+ void setTranslation(const QVector2D &trans);
+
+ Q_INVOKABLE void enforceConstraints();
+
+Q_SIGNALS:
+// void gestureStarted(QQuickGestureEvent *gesture);
+ void translationChanged();
+
+protected:
+ bool wantsEventPoint(QQuickEventPoint *point) override;
+ void onActiveChanged() override;
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override;
+
+private:
+ void ungrab();
+ void enforceAxisConstraints(QPointF *localPos);
+ void initializeTargetStartPos(QQuickEventPoint *point);
+
+private:
+ QPointF m_targetStartPos;
+ QVector2D m_translation;
+ QQuickDragAxis m_xAxis;
+ QQuickDragAxis m_yAxis;
+
+ friend class QQuickDragAxis;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickDragHandler)
+QML_DECLARE_TYPE(QQuickDragAxis)
+
+#endif // QQUICKDRAGHANDLER_H
diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp
new file mode 100644
index 0000000000..8472c6a062
--- /dev/null
+++ b/src/quick/handlers/qquickhandlersmodule.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 "qquickhandlersmodule_p.h"
+#include "qquickpointerhandler_p.h"
+#include "qquickdraghandler_p.h"
+#include "qquickpinchhandler_p.h"
+#include "qquicktaphandler_p.h"
+
+static void initResources()
+{
+#ifdef QT_STATIC
+ Q_INIT_RESOURCE(qmake_Qt_labs_handlers);
+#endif
+}
+
+QT_BEGIN_NAMESPACE
+
+static QQmlPrivate::AutoParentResult handler_autoParent(QObject *obj, QObject *parent)
+{
+ if (qmlobject_cast<QQuickItem *>(parent)) {
+ QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj);
+ if (handler) {
+ handler->setParent(parent);
+ return QQmlPrivate::Parented;
+ }
+ }
+ return QQmlPrivate::IncompatibleObject;
+}
+
+static void qt_quickhandlers_defineModule(const char *uri, int major, int minor)
+{
+ QQmlPrivate::RegisterAutoParent autoparent = { 0, &handler_autoParent };
+ QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent);
+ qmlRegisterUncreatableType<QQuickPointerEvent>(uri, major, minor, "PointerEvent",
+ QQuickPointerHandler::tr("PointerEvent is only available as a parameter of several signals in PointerHandler"));
+ qmlRegisterUncreatableType<QQuickEventPoint>(uri, major, minor, "EventPoint",
+ QQuickPointerHandler::tr("EventPoint is only available as a member of PointerEvent"));
+ qmlRegisterUncreatableType<QQuickEventTouchPoint>(uri, major, minor, "EventTouchPoint",
+ QQuickPointerHandler::tr("EventTouchPoint is only available as a member of PointerEvent"));
+ qmlRegisterUncreatableType<QQuickPointerDevice>(uri, major, minor, "PointerDevice",
+ QQuickPointerHandler::tr("PointerDevice is only available as a property of PointerEvent"));
+ qRegisterMetaType<QPointingDeviceUniqueId>("QPointingDeviceUniqueId");
+ qmlRegisterUncreatableType<QPointingDeviceUniqueId>(uri, major, minor, "PointingDeviceUniqueId",
+ QQuickPointerHandler::tr("PointingDeviceUniqueId is only available as a property of PointerEvent"));
+
+ qmlRegisterUncreatableType<QQuickPointerHandler>(uri,major,minor,"PointerHandler",
+ QQuickPointerHandler::tr("PointerHandler is an abstract base class"));
+ 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");
+ qmlRegisterType<QQuickTapHandler>(uri,major,minor,"TapHandler");
+ qRegisterMetaType<QQuickHandlerPoint>();
+}
+
+void QQuickHandlersModule::defineModule()
+{
+ initResources();
+
+ const char uri[] = "Qt.labs.handlers";
+ int majorVersion = 1;
+ int minorVersion = 0;
+
+ qt_quickhandlers_defineModule(uri, majorVersion, minorVersion);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickhandlersmodule_p.h b/src/quick/handlers/qquickhandlersmodule_p.h
new file mode 100644
index 0000000000..7eb8d39b98
--- /dev/null
+++ b/src/quick/handlers/qquickhandlersmodule_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** 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 QQUICKHANDLERSMODULE_P_H
+#define QQUICKHANDLERSMODULE_P_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 <qqml.h>
+#include <private/qtquickglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickHandlersModule
+{
+public:
+ static void defineModule();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKHANDLERSMODULE_P_H
+
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
new file mode 100644
index 0000000000..ff4914394c
--- /dev/null
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** 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 "qquickmultipointhandler_p.h"
+#include <private/qquickitem_p.h>
+#include <QLineF>
+#include <QMouseEvent>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype MultiPointHandler
+ \since 5.10
+ \preliminary
+ \instantiates QQuickMultiPointHandler
+ \inherits PointerDeviceHandler
+ \inqmlmodule Qt.labs.handlers
+ \ingroup qtquick-handlers
+ \brief Abstract handler for multi-point Pointer Events.
+
+ An intermediate class (not registered as a QML type)
+ for any type of handler which requires and acts upon a specific number
+ of multiple touchpoints.
+*/
+QQuickMultiPointHandler::QQuickMultiPointHandler(QObject *parent, int minimumPointCount)
+ : QQuickPointerDeviceHandler(parent)
+ , m_minimumPointCount(minimumPointCount)
+ , m_maximumPointCount(-1)
+ , m_pointDistanceThreshold(0)
+{
+}
+
+QQuickMultiPointHandler::~QQuickMultiPointHandler()
+{
+}
+
+bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
+ return false;
+
+ if (event->asPointerNativeGestureEvent())
+ return true;
+
+ if (sameAsCurrentPoints(event))
+ return true;
+
+ const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event);
+ const bool ret = (candidatePoints.size() >= minimumPointCount() && candidatePoints.size() <= maximumPointCount());
+ if (ret)
+ m_currentPoints = candidatePoints;
+ return ret;
+}
+
+QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointerEvent *event)
+{
+ QVector<QQuickEventPoint *> ret;
+ int c = event->pointCount();
+ QRectF parentBounds = parentItem()->mapRectToScene(parentItem()->boundingRect())
+ .marginsAdded(QMarginsF(m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold));
+ // If one or more points are newly pressed or released, all non-released points are candidates for this handler.
+ // In other cases however, do not steal the grab: that is, if a point has a grabber,
+ // it's not a candidate for this handler.
+ bool stealingAllowed = event->isPressEvent() || event->isReleaseEvent();
+ for (int i = 0; i < c; ++i) {
+ QQuickEventPoint *p = event->point(i);
+ if (!stealingAllowed) {
+ QObject *exclusiveGrabber = p->exclusiveGrabber();
+ if (exclusiveGrabber && exclusiveGrabber != this)
+ continue;
+ }
+ if (p->state() != QQuickEventPoint::Released && parentBounds.contains(p->scenePosition()))
+ ret << p;
+ }
+ return ret;
+}
+
+/*!
+ \qmlproperty int MultiPointHandler::minimumPointCount
+
+ The minimum number of touchpoints required to activate this handler.
+
+ If a smaller number of touchpoints are in contact with the
+ \l {PointerHandler::parent}{parent}, they will be ignored.
+
+ Any ignored points are eligible to activate other Pointer Handlers that
+ have different constraints, on the same Item or on other Items.
+
+ The default value is 2.
+*/
+void QQuickMultiPointHandler::setMinimumPointCount(int c)
+{
+ if (m_minimumPointCount == c)
+ return;
+
+ m_minimumPointCount = c;
+ emit minimumPointCountChanged();
+ if (m_maximumPointCount < 0)
+ emit maximumPointCountChanged();
+}
+
+/*!
+ \qmlproperty int MultiPointHandler::maximumPointCount
+
+ The maximum number of touchpoints this handler can utilize.
+
+ If a larger number of touchpoints are in contact with the
+ \l {PointerHandler::parent}{parent}, the required number of points will be
+ chosen in the order that they are pressed, and the remaining points will
+ be ignored.
+
+ Any ignored points are eligible to activate other Pointer Handlers that
+ have different constraints, on the same Item or on other Items.
+
+ The default value is the same as \l minimumPointCount.
+*/
+void QQuickMultiPointHandler::setMaximumPointCount(int maximumPointCount)
+{
+ if (m_maximumPointCount == maximumPointCount)
+ return;
+
+ m_maximumPointCount = maximumPointCount;
+ emit maximumPointCountChanged();
+}
+
+/*!
+ \qmlproperty real MultiPointHandler::pointDistanceThreshold
+
+ The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
+ item within which a touch point can activate this handler. For example, on
+ a PinchHandler where the \l {PointerHandler::target}{target} is also the
+ \c parent, it's useful to set this to a distance at least half the width
+ of a typical user's finger, so that if the \c parent has been scaled down
+ to a very small size, the pinch gesture is still possible.
+
+ The default value is 0.
+
+ \image pointDistanceThreshold.png
+*/
+void QQuickMultiPointHandler::setPointDistanceThreshold(qreal pointDistanceThreshold)
+{
+ if (m_pointDistanceThreshold == pointDistanceThreshold)
+ return;
+
+ m_pointDistanceThreshold = pointDistanceThreshold;
+ emit pointDistanceThresholdChanged();
+}
+
+bool QQuickMultiPointHandler::sameAsCurrentPoints(QQuickPointerEvent *event)
+{
+ bool ret = true;
+ int c = event->pointCount();
+ if (c != m_currentPoints.size())
+ return false;
+ // TODO optimize: either ensure the points are sorted,
+ // or use std::equal with a predicate
+ for (int i = 0; ret && i < c; ++i) {
+ if (event->point(i)->state() == QQuickEventPoint::Released)
+ return false;
+ bool found = false;
+ int pointId = event->point(i)->pointId();
+ for (QQuickEventPoint *o : qAsConst(m_currentPoints))
+ if (o && pointId == o->pointId())
+ found = true;
+ if (!found)
+ ret = false;
+ }
+ return ret;
+}
+
+// TODO make templates for these functions somehow?
+QPointF QQuickMultiPointHandler::touchPointCentroid()
+{
+ QPointF ret;
+ if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ return ret;
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ ret += point->scenePosition();
+ return ret / m_currentPoints.size();
+}
+
+QVector2D QQuickMultiPointHandler::touchPointCentroidVelocity()
+{
+ QVector2D ret;
+ if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ return ret;
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ ret += point->velocity();
+ return ret / m_currentPoints.size();
+}
+
+qreal QQuickMultiPointHandler::averageTouchPointDistance(const QPointF &ref)
+{
+ qreal ret = 0;
+ if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ return ret;
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ ret += QVector2D(point->scenePosition() - ref).length();
+ return ret / m_currentPoints.size();
+}
+
+qreal QQuickMultiPointHandler::averageStartingDistance(const QPointF &ref)
+{
+ // TODO cache it in setActive()?
+ qreal ret = 0;
+ if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ return ret;
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ ret += QVector2D(point->sceneGrabPosition() - ref).length();
+ return ret / m_currentPoints.size();
+}
+
+QVector<QQuickMultiPointHandler::PointData> QQuickMultiPointHandler::angles(const QPointF &ref) const
+{
+ QVector<PointData> angles;
+ angles.reserve(m_currentPoints.count());
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints)) {
+ qreal angle = QLineF(ref, point->scenePosition()).angle();
+ angles.append(PointData(point->pointId(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation
+ }
+ return angles;
+}
+
+qreal QQuickMultiPointHandler::averageAngleDelta(const QVector<PointData> &old, const QVector<PointData> &newAngles)
+{
+ qreal avgAngleDelta = 0;
+ int numSamples = 0;
+
+ auto oldBegin = old.constBegin();
+
+ for (PointData newData : newAngles) {
+ quint64 id = newData.id;
+ auto it = std::find_if(oldBegin, old.constEnd(), [id] (PointData pd) { return pd.id == id; });
+ qreal angleD = 0;
+ if (it != old.constEnd()) {
+ PointData oldData = *it;
+ // We might rotate from 359 degrees to 1 degree. However, this
+ // should be interpreted as a rotation of +2 degrees instead of
+ // -358 degrees. Therefore, we call remainder() to translate the angle
+ // to be in the range [-180, 180] (-350 to +10 etc)
+ angleD = remainder(newData.angle - oldData.angle, qreal(360));
+ // optimization: narrow down the O(n^2) search to optimally O(n)
+ // if both vectors have the same points and they are in the same order
+ if (it == oldBegin)
+ ++oldBegin;
+ numSamples++;
+ }
+ avgAngleDelta += angleD;
+ }
+ if (numSamples > 1)
+ avgAngleDelta /= numSamples;
+
+ return avgAngleDelta;
+}
+
+void QQuickMultiPointHandler::acceptPoints(const QVector<QQuickEventPoint *> &points)
+{
+ for (QQuickEventPoint* point : points)
+ point->setAccepted();
+}
+
+bool QQuickMultiPointHandler::grabPoints(QVector<QQuickEventPoint *> points)
+{
+ bool canGrab = true;
+ for (QQuickEventPoint* point : points) {
+ auto grabber = point->grabberItem();
+ if (grabber && (grabber->keepMouseGrab() || grabber->keepTouchGrab()))
+ canGrab = false;
+ }
+ if (canGrab) {
+ for (QQuickEventPoint* point : points)
+ setExclusiveGrab(point);
+ }
+ return canGrab;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickmultipointhandler_p.h b/src/quick/handlers/qquickmultipointhandler_p.h
new file mode 100644
index 0000000000..05c3876246
--- /dev/null
+++ b/src/quick/handlers/qquickmultipointhandler_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** 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 QQUICKPOINTERMULTIHANDLER_H
+#define QQUICKPOINTERMULTIHANDLER_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 "qquickpointerdevicehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(int minimumPointCount READ minimumPointCount WRITE setMinimumPointCount NOTIFY minimumPointCountChanged)
+ Q_PROPERTY(int maximumPointCount READ maximumPointCount WRITE setMaximumPointCount NOTIFY maximumPointCountChanged)
+ Q_PROPERTY(qreal pointDistanceThreshold READ pointDistanceThreshold WRITE setPointDistanceThreshold NOTIFY pointDistanceThresholdChanged)
+
+public:
+ explicit QQuickMultiPointHandler(QObject *parent = 0, int minimumPointCount = 2);
+ ~QQuickMultiPointHandler();
+
+ int minimumPointCount() const { return m_minimumPointCount; }
+ void setMinimumPointCount(int c);
+
+ int maximumPointCount() const { return m_maximumPointCount >= 0 ? m_maximumPointCount : m_minimumPointCount; }
+ void setMaximumPointCount(int maximumPointCount);
+
+ qreal pointDistanceThreshold() const { return m_pointDistanceThreshold; }
+ void setPointDistanceThreshold(qreal pointDistanceThreshold);
+
+signals:
+ void minimumPointCountChanged();
+ void maximumPointCountChanged();
+ void pointDistanceThresholdChanged();
+
+protected:
+ struct PointData {
+ PointData() : id(0), angle(0) {}
+ PointData(quint64 id, qreal angle) : id(id), angle(angle) {}
+ quint64 id;
+ qreal angle;
+ };
+
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ bool sameAsCurrentPoints(QQuickPointerEvent *event);
+ QVector<QQuickEventPoint *> eligiblePoints(QQuickPointerEvent *event);
+ QPointF touchPointCentroid();
+ QVector2D touchPointCentroidVelocity();
+ qreal averageTouchPointDistance(const QPointF &ref);
+ qreal averageStartingDistance(const QPointF &ref);
+ qreal averageTouchPointAngle(const QPointF &ref);
+ qreal averageStartingAngle(const QPointF &ref);
+ QVector<PointData> angles(const QPointF &ref) const;
+ static qreal averageAngleDelta(const QVector<PointData> &old, const QVector<PointData> &newAngles);
+ void acceptPoints(const QVector<QQuickEventPoint *> &points);
+ bool grabPoints(QVector<QQuickEventPoint *> points);
+
+protected:
+ QVector<QQuickEventPoint *> m_currentPoints;
+ int m_minimumPointCount;
+ int m_maximumPointCount;
+ qreal m_pointDistanceThreshold;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickMultiPointHandler)
+
+#endif // QQUICKPOINTERMULTIHANDLER_H
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
new file mode 100644
index 0000000000..84c4e912d1
--- /dev/null
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -0,0 +1,483 @@
+/****************************************************************************
+**
+** 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
+ \inherits MultiPointHandler
+ \inqmlmodule Qt.labs.handlers
+ \ingroup qtquick-handlers
+ \brief Handler for pinch gestures
+
+ PinchHandler is a handler that interprets a multi-finger gesture to
+ interactively rotate, zoom, and drag an Item. Like other Pointer Handlers,
+ by default it is fully functional, and manipulates its \l target,
+ which is the Item within which it is declared.
+
+ \snippet pointerHandlers/pinchHandler.qml 0
+
+ It has properties to restrict the range of dragging, rotation, and zoom.
+
+ If it is declared within one Item but is assigned a different \l target, it
+ handles events within the bounds of the outer Item but manipulates the
+ \c target Item instead:
+
+ \snippet pointerHandlers/pinchHandlerDifferentTarget.qml 0
+
+ A third way to use it is to set \l target to \c null and react to property
+ changes in some other way:
+
+ \snippet pointerHandlers/pinchHandlerNullTarget.qml 0
+
+ \image touchpoints-pinchhandler.png
+
+ \sa PinchArea
+*/
+
+QQuickPinchHandler::QQuickPinchHandler(QObject *parent)
+ : QQuickMultiPointHandler(parent, 2)
+ , m_activeScale(1)
+ , m_activeRotation(0)
+ , m_activeTranslation(0,0)
+ , m_minimumScale(-qInf())
+ , m_maximumScale(qInf())
+ , m_minimumRotation(-qInf())
+ , m_maximumRotation(qInf())
+ , m_minimumX(-qInf())
+ , m_maximumX(qInf())
+ , m_minimumY(-qInf())
+ , m_maximumY(qInf())
+ , m_pinchOrigin(PinchCenter)
+ , m_startScale(1)
+ , m_startRotation(0)
+{
+}
+
+QQuickPinchHandler::~QQuickPinchHandler()
+{
+}
+
+/*!
+ \qmlproperty real QtQuick::PinchHandler::minimumScale
+
+ The minimum acceptable \l {Item::scale}{scale} to be applied
+ to the \l target.
+*/
+void QQuickPinchHandler::setMinimumScale(qreal minimumScale)
+{
+ if (m_minimumScale == minimumScale)
+ return;
+
+ m_minimumScale = minimumScale;
+ emit minimumScaleChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::PinchHandler::maximumScale
+
+ The maximum acceptable \l {Item::scale}{scale} to be applied
+ to the \l target.
+*/
+void QQuickPinchHandler::setMaximumScale(qreal maximumScale)
+{
+ if (m_maximumScale == maximumScale)
+ return;
+
+ m_maximumScale = maximumScale;
+ emit maximumScaleChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::PinchHandler::minimumRotation
+
+ The minimum acceptable \l {Item::rotation}{rotation} to be applied
+ to the \l target.
+*/
+void QQuickPinchHandler::setMinimumRotation(qreal minimumRotation)
+{
+ if (m_minimumRotation == minimumRotation)
+ return;
+
+ m_minimumRotation = minimumRotation;
+ emit minimumRotationChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::PinchHandler::maximumRotation
+
+ The maximum acceptable \l {Item::rotation}{rotation} to be applied
+ to the \l target.
+*/
+void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation)
+{
+ if (m_maximumRotation == maximumRotation)
+ return;
+
+ m_maximumRotation = maximumRotation;
+ emit maximumRotationChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::PinchHandler::pinchOrigin
+
+ The point to be held in place, around which the \l target is scaled and
+ rotated.
+
+ \value FirstPoint
+ the first touch point, wherever the first finger is pressed
+ \value PinchCenter
+ the centroid between all the touch points at the time when the
+ PinchHandler becomes \l active
+ \value TargetCenter
+ the center of the \l target
+*/
+void QQuickPinchHandler::setPinchOrigin(QQuickPinchHandler::PinchOrigin pinchOrigin)
+{
+ if (m_pinchOrigin == pinchOrigin)
+ return;
+
+ m_pinchOrigin = pinchOrigin;
+ emit pinchOriginChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::PinchHandler::minimumX
+
+ The minimum acceptable x coordinate of the centroid
+*/
+void QQuickPinchHandler::setMinimumX(qreal minX)
+{
+ if (m_minimumX == minX)
+ return;
+ m_minimumX = minX;
+ emit minimumXChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::PinchHandler::maximumX
+
+ The maximum acceptable x coordinate of the centroid
+*/
+void QQuickPinchHandler::setMaximumX(qreal maxX)
+{
+ if (m_maximumX == maxX)
+ return;
+ m_maximumX = maxX;
+ emit maximumXChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::PinchHandler::minimumY
+
+ The minimum acceptable y coordinate of the centroid
+*/
+void QQuickPinchHandler::setMinimumY(qreal minY)
+{
+ if (m_minimumY == minY)
+ return;
+ m_minimumY = minY;
+ emit minimumYChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::PinchHandler::maximumY
+
+ The maximum acceptable y coordinate of the centroid
+*/
+void QQuickPinchHandler::setMaximumY(qreal maxY)
+{
+ if (m_maximumY == maxY)
+ return;
+ m_maximumY = maxY;
+ emit maximumYChanged();
+}
+
+bool QQuickPinchHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ if (!QQuickMultiPointHandler::wantsPointerEvent(event))
+ return false;
+
+ if (minimumPointCount() == 2) {
+ if (const auto gesture = event->asPointerNativeGestureEvent()) {
+ switch (gesture->type()) {
+ case Qt::BeginNativeGesture:
+ case Qt::EndNativeGesture:
+ case Qt::ZoomNativeGesture:
+ case Qt::RotateNativeGesture:
+ return parentContains(event->point(0));
+ default:
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/*!
+ \qmlproperty int QtQuick::PinchHandler::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 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.
+*/
+
+void QQuickPinchHandler::onActiveChanged()
+{
+ if (active()) {
+ m_startMatrix = QMatrix4x4();
+ m_startCentroid = touchPointCentroid();
+ m_startAngles = angles(m_startCentroid);
+ m_startDistance = averageTouchPointDistance(m_startCentroid);
+ 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();
+ QVector3D xformOrigin(t->transformOriginPoint());
+ m_startMatrix.translate(t->x(), t->y());
+ m_startMatrix.translate(xformOrigin);
+ m_startMatrix.scale(m_startScale);
+ m_startMatrix.rotate(m_startRotation, 0, 0, -1);
+ m_startMatrix.translate(-xformOrigin);
+ } else {
+ m_startScale = 1;
+ m_startRotation = 0;
+ }
+ qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation;
+ } else {
+ qCInfo(lcPinchHandler) << "deactivated with scale" << m_activeScale << "rotation" << m_activeRotation;
+ }
+}
+
+void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) {
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPosition() << "->" << point->scenePosition();
+ }
+
+ qreal dist = 0;
+ if (const auto gesture = event->asPointerNativeGestureEvent()) {
+ switch (gesture->type()) {
+ case Qt::EndNativeGesture:
+ m_activeScale = 1;
+ m_activeRotation = 0;
+ m_activeTranslation = QVector2D();
+ m_centroid = QPointF();
+ m_centroidVelocity = QVector2D();
+ setActive(false);
+ emit updated();
+ return;
+ case Qt::ZoomNativeGesture:
+ m_activeScale *= 1 + gesture->value();
+ break;
+ case Qt::RotateNativeGesture:
+ m_activeRotation += gesture->value();
+ break;
+ default:
+ // Nothing of interest (which is unexpected, because wantsPointerEvent() should have returned false)
+ return;
+ }
+ if (!active()) {
+ m_centroid = gesture->point(0)->scenePosition();
+ setActive(true);
+ m_startCentroid = m_centroid;
+ // 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_centroidVelocity = QVector2D();
+ m_activeTranslation = QVector2D();
+ }
+ } else {
+ bool containsReleasedPoints = event->isReleaseEvent();
+ if (!active() && !containsReleasedPoints) {
+ // Verify that at least one of the points has moved beyond threshold needed to activate the handler
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints)) {
+ if (QQuickWindowPrivate::dragOverThreshold(point)) {
+ if (grabPoints(m_currentPoints))
+ setActive(true);
+ break;
+ }
+ }
+ if (!active())
+ return;
+ }
+ // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter
+ m_centroid = touchPointCentroid();
+ m_centroidVelocity = touchPointCentroidVelocity();
+ // 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(m_centroid);
+ m_activeScale = dist / m_startDistance;
+ m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale);
+
+ // 2. rotate
+ QVector<PointData> newAngles = angles(m_centroid);
+ const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
+ m_activeRotation += angleDelta;
+ m_startAngles = std::move(newAngles);
+
+ if (!containsReleasedPoints)
+ acceptPoints(m_currentPoints);
+ }
+
+ QPointF centroidParentPos;
+ QRectF bounds(m_minimumX, m_minimumY, m_maximumX - m_minimumX, m_maximumY - m_minimumY);
+ if (target() && target()->parentItem()) {
+ centroidParentPos = target()->parentItem()->mapFromScene(m_centroid);
+ centroidParentPos = QPointF(qBound(bounds.left(), centroidParentPos.x(), bounds.right()),
+ qBound(bounds.top(), centroidParentPos.y(), bounds.bottom()));
+ }
+ 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
+ const qreal scale = m_startScale * m_activeScale;
+
+ if (target() && target()->parentItem()) {
+ // 3. Drag/translate
+ const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_startCentroid);
+ m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos);
+
+ // apply rotation + scaling around the centroid - then apply translation.
+ QMatrix4x4 mat;
+
+ const QVector3D centroidParentVector(centroidParentPos);
+ mat.translate(centroidParentVector);
+ mat.rotate(m_activeRotation, 0, 0, 1);
+ mat.scale(m_activeScale);
+ mat.translate(-centroidParentVector);
+ mat.translate(QVector3D(m_activeTranslation));
+
+ mat = mat * m_startMatrix;
+
+ QPointF xformOriginPoint = target()->transformOriginPoint();
+ QPointF pos = mat * xformOriginPoint;
+ pos -= xformOriginPoint;
+
+ target()->setPosition(pos);
+ target()->setRotation(rotation);
+ target()->setScale(scale);
+
+ // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place
+ } else {
+ m_activeTranslation = QVector2D(m_centroid - m_startCentroid);
+ }
+
+ qCDebug(lcPinchHandler) << "centroid" << m_startCentroid << "->" << m_centroid
+ << ", distance" << m_startDistance << "->" << dist
+ << ", startScale" << m_startScale << "->" << scale
+ << ", activeRotation" << m_activeRotation
+ << ", rotation" << rotation
+ << " from " << event->device()->type();
+
+ emit updated();
+}
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::PinchHandler::centroid
+
+ A point exactly in the middle of the currently-pressed touch points.
+ If \l pinchOrigin is set to \c PinchCenter, the \l target will be rotated
+ around this point.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QVector2D QtQuick::PinchHandler::centroidVelocity
+
+ The average velocity of the \l centroid: a vector representing the speed
+ and direction of movement of the whole group of touchpoints, in logical
+ pixels per second.
+*/
+
+/*!
+ \readonly
+ \qmlproperty real QtQuick::PinchHandler::scale
+
+ The scale factor. 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, this will be automatically applied to its
+ \l {Item::scale}{scale}. 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.
+*/
+
+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..7d6b7d9509
--- /dev/null
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** 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 "qquickmultipointhandler_p.h"
+#include <private/qquicktranslate_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
+{
+ 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 centroid READ centroid NOTIFY updated)
+ Q_PROPERTY(QVector2D centroidVelocity READ centroidVelocity NOTIFY updated)
+ Q_PROPERTY(qreal scale READ scale NOTIFY updated)
+ Q_PROPERTY(qreal rotation READ rotation NOTIFY updated)
+ Q_PROPERTY(QVector2D translation READ translation NOTIFY updated)
+ Q_PROPERTY(qreal minimumX READ minimumX WRITE setMinimumX NOTIFY minimumXChanged)
+ Q_PROPERTY(qreal maximumX READ maximumX WRITE setMaximumX NOTIFY maximumXChanged)
+ Q_PROPERTY(qreal minimumY READ minimumY WRITE setMinimumY NOTIFY minimumYChanged)
+ Q_PROPERTY(qreal maximumY READ maximumY WRITE setMaximumY NOTIFY maximumYChanged)
+
+public:
+ enum PinchOrigin {
+ FirstPoint, PinchCenter, TargetCenter
+ };
+ Q_ENUM(PinchOrigin)
+
+ explicit 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);
+
+ QVector2D translation() const { return m_activeTranslation; }
+ qreal scale() const { return m_activeScale; }
+ qreal rotation() const { return m_activeRotation; }
+ QPointF centroid() const { return m_centroid; }
+ QVector2D centroidVelocity() const { return m_centroidVelocity; }
+
+ qreal minimumX() const { return m_minimumX; }
+ void setMinimumX(qreal minX);
+ qreal maximumX() const { return m_maximumX; }
+ void setMaximumX(qreal maxX);
+ qreal minimumY() const { return m_minimumY; }
+ void setMinimumY(qreal minY);
+ qreal maximumY() const { return m_maximumY; }
+ void setMaximumY(qreal maxY);
+
+signals:
+ void minimumScaleChanged();
+ void maximumScaleChanged();
+ void minimumRotationChanged();
+ void maximumRotationChanged();
+ void minimumXChanged();
+ void maximumXChanged();
+ void minimumYChanged();
+ void maximumYChanged();
+ void pinchOriginChanged();
+ void updated();
+
+protected:
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ void onActiveChanged() override;
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+
+private:
+ // properties
+ qreal m_activeScale;
+ qreal m_activeRotation;
+ QVector2D m_activeTranslation;
+ QPointF m_centroid;
+ QVector2D m_centroidVelocity;
+
+ qreal m_minimumScale;
+ qreal m_maximumScale;
+
+ qreal m_minimumRotation;
+ qreal m_maximumRotation;
+
+ qreal m_minimumX;
+ qreal m_maximumX;
+ qreal m_minimumY;
+ qreal m_maximumY;
+
+ PinchOrigin m_pinchOrigin;
+
+ // internal
+ qreal m_startScale;
+ qreal m_startRotation;
+ QPointF m_startCentroid;
+ qreal m_startDistance;
+ QPointF m_startPos;
+
+ QVector<PointData> m_startAngles;
+ QMatrix4x4 m_startMatrix;
+ QQuickMatrix4x4 m_transform;
+
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPinchHandler)
+
+#endif // QQUICKPINCHHANDLER_H
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
new file mode 100644
index 0000000000..1521b58d57
--- /dev/null
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** 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 "qquickpointerdevicehandler_p.h"
+#include <private/qquickitem_p.h>
+#include <QMouseEvent>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype PointerDeviceHandler
+ \qmlabstract
+ \since 5.10
+ \preliminary
+ \instantiates QQuickPointerDeviceHandler
+ \inherits PointerHandler
+ \inqmlmodule Qt.labs.handlers
+ \ingroup qtquick-handlers
+ \brief Abstract handler for pointer events with device-specific constraints.
+
+ An intermediate class (not registered as a QML type) for handlers which
+ allow filtering based on device type, pointer type, or keyboard modifiers.
+*/
+QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QObject *parent)
+ : QQuickPointerHandler(parent)
+ , m_acceptedDevices(QQuickPointerDevice::AllDevices)
+ , m_acceptedPointerTypes(QQuickPointerDevice::AllPointerTypes)
+ , m_acceptedModifiers(Qt::KeyboardModifierMask)
+{
+}
+
+QQuickPointerDeviceHandler::~QQuickPointerDeviceHandler()
+{
+}
+
+/*!
+ \qmlproperty int PointerDeviceHandler::acceptedDevices
+
+ The types of pointing devices that can activate this Pointer Handler.
+
+ By default, this property is set to \l PointerDevice.AllDevices.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching devices.
+
+ For example, a control could be made to respond to mouse and stylus clicks
+ in one way, and touchscreen taps in another way, with two handlers:
+
+ \qml
+ Item {
+ TapHandler {
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ onTapped: console.log("clicked")
+ }
+ TapHandler {
+ acceptedDevices: PointerDevice.TouchScreen
+ onTapped: console.log("tapped")
+ }
+ }
+ \endqml
+*/
+void QQuickPointerDeviceHandler::setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices)
+{
+ if (m_acceptedDevices == acceptedDevices)
+ return;
+
+ m_acceptedDevices = acceptedDevices;
+ emit acceptedDevicesChanged();
+}
+
+/*!
+ \qmlproperty int PointerDeviceHandler::acceptedPointerTypes
+
+ The types of pointing instruments (finger, stylus, eraser, etc.)
+ that can activate this Pointer Handler.
+
+ By default, this property is set to \l PointerDevice.AllPointerTypes.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching events.
+
+ For example, a control could be made to respond to mouse, touch, and stylus clicks
+ in some way, but delete itself if tapped with an eraser tool on a graphics tablet,
+ with two handlers:
+
+ \qml
+ Rectangle {
+ id: rect
+ TapHandler {
+ acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Finger | PointerDevice.Pen
+ onTapped: console.log("clicked")
+ }
+ TapHandler {
+ acceptedPointerTypes: PointerDevice.Eraser
+ onTapped: rect.destroy()
+ }
+ }
+ \endqml
+*/
+void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes)
+{
+ if (m_acceptedPointerTypes == acceptedPointerTypes)
+ return;
+
+ m_acceptedPointerTypes = acceptedPointerTypes;
+ emit acceptedPointerTypesChanged();
+}
+
+/*!
+ \qmlproperty int PointerDeviceHandler::acceptedModifiers
+
+ If this property is set, it will require the given keyboard modifiers to
+ be pressed in order to react to pointer events, and otherwise ignore them.
+
+ If this property is set to \c Qt.KeyboardModifierMask (the default value),
+ then the PointerHandler ignores the modifier keys.
+
+ For example, an \l [QML] Item could have two handlers of the same type,
+ one of which is enabled only if the required keyboard modifiers are
+ pressed:
+
+ \qml
+ Item {
+ TapHandler {
+ acceptedModifiers: Qt.ControlModifier
+ onTapped: console.log("control-tapped")
+ }
+ TapHandler {
+ acceptedModifiers: Qt.NoModifier
+ onTapped: console.log("tapped")
+ }
+ }
+ \endqml
+*/
+void QQuickPointerDeviceHandler::setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers)
+{
+ if (m_acceptedModifiers == acceptedModifiers)
+ return;
+
+ m_acceptedModifiers = acceptedModifiers;
+ emit acceptedModifiersChanged();
+}
+
+bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ if (!QQuickPointerHandler::wantsPointerEvent(event))
+ return false;
+ qCDebug(lcPointerHandlerDispatch) << objectName()
+ << "checking device type" << m_acceptedDevices
+ << "pointer type" << m_acceptedPointerTypes
+ << "modifiers" << m_acceptedModifiers;
+ if ((event->device()->type() & m_acceptedDevices) == 0)
+ return false;
+ if ((event->device()->pointerType() & m_acceptedPointerTypes) == 0)
+ return false;
+ if (m_acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != m_acceptedModifiers)
+ return false;
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h
new file mode 100644
index 0000000000..9e30fa0be4
--- /dev/null
+++ b/src/quick/handlers/qquickpointerdevicehandler_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** 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 QQUICKPOINTERDEVICEHANDLER_H
+#define QQUICKPOINTERDEVICEHANDLER_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 "qquickpointerhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged)
+ Q_PROPERTY(QQuickPointerDevice::PointerTypes acceptedPointerTypes READ acceptedPointerTypes WRITE setAcceptedPointerTypes NOTIFY acceptedPointerTypesChanged)
+ Q_PROPERTY(Qt::KeyboardModifiers acceptedModifiers READ acceptedModifiers WRITE setAcceptedModifiers NOTIFY acceptedModifiersChanged)
+
+public:
+ explicit QQuickPointerDeviceHandler(QObject *parent = 0);
+ ~QQuickPointerDeviceHandler();
+
+ QQuickPointerDevice::DeviceTypes acceptedDevices() const { return m_acceptedDevices; }
+ QQuickPointerDevice::PointerTypes acceptedPointerTypes() const { return m_acceptedPointerTypes; }
+ Qt::KeyboardModifiers acceptedModifiers() const { return m_acceptedModifiers; }
+
+public slots:
+ void setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices);
+ void setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes);
+ void setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers);
+
+Q_SIGNALS:
+ void acceptedDevicesChanged();
+ void acceptedPointerTypesChanged();
+ void acceptedModifiersChanged();
+
+protected:
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+
+protected:
+ QQuickPointerDevice::DeviceTypes m_acceptedDevices;
+ QQuickPointerDevice::PointerTypes m_acceptedPointerTypes;
+ Qt::KeyboardModifiers m_acceptedModifiers;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPointerDeviceHandler)
+
+#endif // QQUICKPOINTERDEVICEHANDLER_H
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
new file mode 100644
index 0000000000..c32bec665c
--- /dev/null
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 "qquickpointerhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch")
+Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active")
+
+/*!
+ \qmltype PointerHandler
+ \qmlabstract
+ \since 5.10
+ \preliminary
+ \instantiates QQuickPointerHandler
+ \inqmlmodule Qt.labs.handlers
+ \ingroup qtquick-handlers
+ \brief Abstract handler for pointer events.
+
+ PointerHandler is the base class handler (not registered as a QML type) for
+ pointer events without regard to source (touch, mouse or graphics tablet).
+*/
+
+QQuickPointerHandler::QQuickPointerHandler(QObject *parent)
+ : QObject(parent)
+ , m_currentEvent(nullptr)
+ , m_target(nullptr)
+ , m_enabled(true)
+ , m_active(false)
+ , m_targetExplicitlySet(false)
+ , m_hadKeepMouseGrab(false)
+ , m_hadKeepTouchGrab(false)
+{
+}
+
+QQuickPointerHandler::~QQuickPointerHandler()
+{
+ QQuickItem *parItem = parentItem();
+ if (parItem) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(parItem);
+ p->extra.value().pointerHandlers.removeOne(this);
+ }
+}
+
+/*!
+ Notification that the grab has changed in some way which is relevant to this handler.
+ The \a grabber (subject) will be the PointerHandler whose state is changing,
+ or null if the state change regards an Item.
+ The \a stateChange (verb) tells what happened.
+ The \a point (object) is the point that was grabbed or ungrabbed.
+ EventPoint has the sole responsibility to call this function.
+ The PointerHandler must react in whatever way is appropriate, and must
+ emit the relevant signals (for the benefit of QML code).
+ A subclass is allowed to override this virtual function, but must always
+ call its parent class's implementation in addition to (usually after)
+ whatever custom behavior it implements.
+*/
+void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+{
+ qCDebug(lcPointerHandlerDispatch) << point << stateChange << grabber;
+ Q_ASSERT(point);
+ if (grabber == this) {
+ bool wasCanceled = false;
+ emit grabChanged(point);
+ switch (stateChange) {
+ case QQuickEventPoint::GrabPassive:
+ case QQuickEventPoint::GrabExclusive:
+ break;
+ case QQuickEventPoint::CancelGrabPassive:
+ case QQuickEventPoint::CancelGrabExclusive:
+ wasCanceled = true; // the grab was stolen by something else
+ Q_FALLTHROUGH();
+ case QQuickEventPoint::UngrabPassive:
+ case QQuickEventPoint::UngrabExclusive:
+ setActive(false);
+ point->setAccepted(false);
+ if (auto par = parentItem()) {
+ par->setKeepMouseGrab(m_hadKeepMouseGrab);
+ par->setKeepTouchGrab(m_hadKeepTouchGrab);
+ }
+ case QQuickEventPoint::OverrideGrabPassive:
+ // Passive grab is still there, but we won't receive point updates right now.
+ // No need to notify about this.
+ return;
+ }
+ if (wasCanceled)
+ emit canceled(point);
+ else
+ emit grabChanged(point);
+ }
+}
+
+/*!
+ \internal
+ Acquire or give up a passive grab of the given \a point, according to the \a grab state.
+
+ Unlike the exclusive grab, multiple PointerHandlers can have passive grabs
+ simultaneously. This means that each of them will receive further events
+ when the \a point moves, and when it is finally released. Typically a
+ PointerHandler should acquire a passive grab as soon as a point is pressed,
+ if the handler's constraints do not clearly rule out any interest in that
+ point. For example, DragHandler needs a passive grab in order to watch the
+ movement of a point to see whether it will be dragged past the drag
+ threshold. When a handler is actively manipulating its \l target (that is,
+ when \l active is true), it may be able to do its work with only a passive
+ grab, or it may acquire an exclusive grab if the gesture clearly must not
+ be interpreted in another way by another handler.
+*/
+void QQuickPointerHandler::setPassiveGrab(QQuickEventPoint *point, bool grab)
+{
+ qCDebug(lcPointerHandlerDispatch) << point << grab;
+ if (grab) {
+ point->setGrabberPointerHandler(this, false);
+ } else {
+ point->removePassiveGrabber(this);
+ }
+}
+
+void QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab)
+{
+ // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab
+ qCDebug(lcPointerHandlerDispatch) << point << grab;
+ // Don't allow one handler to cancel another's grab, unless it is stealing it for itself
+ if (!grab && point->grabberPointerHandler() != this)
+ return;
+ point->setGrabberPointerHandler(grab ? this : nullptr, true);
+}
+
+/*!
+ \internal
+ Cancel any existing grab of the given \a point.
+*/
+void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point)
+{
+ qCDebug(lcPointerHandlerDispatch) << point;
+ point->cancelAllGrabs(this);
+}
+
+QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const
+{
+ return (target() ? target()->mapFromScene(point->scenePosition()) : point->scenePosition());
+}
+
+bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const
+{
+ if (point) {
+ if (QQuickItem *par = parentItem())
+ return par->contains(par->mapFromScene(point->scenePosition()));
+ }
+ return false;
+}
+
+/*!
+ \qmlproperty bool QtQuick::PointerHandler::enabled
+
+ If a PointerHandler is disabled, it will reject all events
+ and no signals will be emitted.
+*/
+void QQuickPointerHandler::setEnabled(bool enabled)
+{
+ if (m_enabled == enabled)
+ return;
+
+ m_enabled = enabled;
+ emit enabledChanged();
+}
+
+/*!
+ \qmlproperty Item QtQuick::PointerHandler::target
+
+ The Item which this handler will manipulate.
+
+ By default, it is the same as the \l parent: the Item within which
+ the handler is declared. However, it can sometimes be useful to set the
+ target to a different Item, in order to handle events within one item
+ but manipulate another; or to \c null, to disable the default behavior
+ and do something else instead.
+*/
+void QQuickPointerHandler::setTarget(QQuickItem *target)
+{
+ m_targetExplicitlySet = true;
+ if (m_target == target)
+ return;
+
+ m_target = target;
+ emit targetChanged();
+}
+
+QQuickItem *QQuickPointerHandler::target() const
+{
+ if (!m_targetExplicitlySet)
+ return parentItem();
+ return m_target;
+}
+
+void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event)
+{
+ bool wants = wantsPointerEvent(event);
+ qCDebug(lcPointerHandlerDispatch) << metaObject()->className() << objectName()
+ << "on" << parentItem()->metaObject()->className() << parentItem()->objectName()
+ << (wants ? "WANTS" : "DECLINES") << event;
+ if (wants) {
+ handlePointerEventImpl(event);
+ } else {
+ setActive(false);
+ int pCount = event->pointCount();
+ for (int i = 0; i < pCount; ++i) {
+ QQuickEventPoint *pt = event->point(i);
+ if (pt->grabberPointerHandler() == this && pt->state() != QQuickEventPoint::Stationary)
+ pt->cancelExclusiveGrab();
+ }
+ }
+ event->device()->eventDeliveryTargets().append(this);
+}
+
+bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ Q_UNUSED(event)
+ return m_enabled;
+}
+
+/*!
+ \readonly
+ \qmlproperty bool QtQuick::PointerHandler::active
+
+ This holds true whenever this PointerHandler has taken sole responsibility
+ for handing one or more EventPoints, by successfully taking an exclusive
+ grab of those points. This means that it is keeping its properties
+ up-to-date according to the movements of those Event Points and actively
+ manipulating its \l target (if any).
+*/
+void QQuickPointerHandler::setActive(bool active)
+{
+ if (m_active != active) {
+ qCDebug(lcPointerHandlerActive) << this << m_active << "->" << active;
+ m_active = active;
+ onActiveChanged();
+ emit activeChanged();
+ }
+}
+
+void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ m_currentEvent = event;
+}
+
+/*!
+ \readonly
+ \qmlproperty Item QtQuick::PointerHandler::parent
+
+ The \l Item which is the scope of the handler; the Item in which it was declared.
+ The handler will handle events on behalf of this Item, which means a
+ pointer event is relevant if at least one of its event points occurs within
+ the Item's interior. Initially \l target() is the same, but target()
+ can be reassigned.
+
+ \sa QQuick::PointerHandler::target(), QObject::parent()
+*/
+
+/*!
+ \qmlsignal QtQuick::PointerHandler::grabChanged(EventPoint point)
+
+ This signal is emitted when this handler has acquired or relinquished a
+ passive or exclusive grab of the given \a point.
+*/
+
+/*!
+ \qmlsignal QtQuick::PointerHandler::canceled(EventPoint point)
+
+ If this handler has already grabbed the given \a point, this signal is
+ emitted when the grab is stolen by a different Pointer Handler or Item.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h
new file mode 100644
index 0000000000..24a058275d
--- /dev/null
+++ b/src/quick/handlers/qquickpointerhandler_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 QQUICKPOINTERHANDLER_H
+#define QQUICKPOINTERHANDLER_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 "qevent.h"
+
+#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch)
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(bool active READ active NOTIFY activeChanged)
+ Q_PROPERTY(QQuickItem * target READ target WRITE setTarget NOTIFY targetChanged)
+ Q_PROPERTY(QQuickItem * parent READ parentItem CONSTANT)
+
+public:
+ explicit QQuickPointerHandler(QObject *parent = 0);
+ virtual ~QQuickPointerHandler();
+
+public:
+ bool enabled() const { return m_enabled; }
+ void setEnabled(bool enabled);
+
+ bool active() const { return m_active; }
+
+ QQuickItem *target() const;
+ void setTarget(QQuickItem *target);
+
+ QQuickItem * parentItem() const { return static_cast<QQuickItem *>(QObject::parent()); }
+
+ void handlePointerEvent(QQuickPointerEvent *event);
+
+Q_SIGNALS:
+ void enabledChanged();
+ void activeChanged();
+ void targetChanged();
+ void grabChanged(QQuickEventPoint *point);
+ void canceled(QQuickEventPoint *point);
+
+protected:
+ QQuickPointerEvent *currentEvent() { return m_currentEvent; }
+ virtual bool wantsPointerEvent(QQuickPointerEvent *event);
+ virtual void handlePointerEventImpl(QQuickPointerEvent *event);
+ void setActive(bool active);
+ virtual void onActiveChanged() { }
+ virtual void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point);
+ void setPassiveGrab(QQuickEventPoint *point, bool grab = true);
+ void setExclusiveGrab(QQuickEventPoint *point, bool grab = true);
+ void cancelAllGrabs(QQuickEventPoint *point);
+ QPointF eventPos(const QQuickEventPoint *point) const;
+ bool parentContains(const QQuickEventPoint *point) const;
+
+private:
+ QQuickPointerEvent *m_currentEvent;
+ QQuickItem *m_target;
+ bool m_enabled : 1;
+ bool m_active : 1;
+ bool m_targetExplicitlySet : 1;
+ bool m_hadKeepMouseGrab : 1; // some handlers override target()->setKeepMouseGrab(); this remembers previous state
+ bool m_hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state
+
+ friend class QQuickEventPoint;
+ friend class QQuickWindowPrivate;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPointerHandler)
+
+#endif // QQUICKPOINTERHANDLER_H
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
new file mode 100644
index 0000000000..dee168a8e4
--- /dev/null
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -0,0 +1,475 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 "qquicksinglepointhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
+
+/*!
+ \qmltype SinglePointHandler
+ \qmlabstract
+ \preliminary
+ \instantiates QQuickSinglePointHandler
+ \inherits PointerDeviceHandler
+ \inqmlmodule Qt.labs.handlers
+ \ingroup qtquick-handlers
+ \brief Abstract handler for single-point Pointer Events.
+
+ An intermediate class (not registered as a QML type)
+ for the most common handlers: those which expect only a single point.
+ wantsPointerEvent() will choose the first point which is inside the
+ \l target item, and return true as long as the event contains that point.
+ Override handleEventPoint() to implement a single-point handler.
+*/
+
+QQuickSinglePointHandler::QQuickSinglePointHandler(QObject *parent)
+ : QQuickPointerDeviceHandler(parent)
+ , m_acceptedButtons(Qt::LeftButton)
+ , m_ignoreAdditionalPoints(false)
+{
+}
+
+bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
+ return false;
+ if (event->device()->pointerType() != QQuickPointerDevice::Finger &&
+ (event->buttons() & m_acceptedButtons) == 0 && (event->button() & m_acceptedButtons) == 0)
+ return false;
+
+ if (m_pointInfo.m_id) {
+ // We already know which one we want, so check whether it's there.
+ // It's expected to be an update or a release.
+ // If we no longer want it, cancel the grab.
+ int candidatePointCount = 0;
+ QQuickEventPoint *point = nullptr;
+ int c = event->pointCount();
+ for (int i = 0; i < c; ++i) {
+ QQuickEventPoint *p = event->point(i);
+ if (wantsEventPoint(p)) {
+ ++candidatePointCount;
+ if (p->pointId() == m_pointInfo.m_id)
+ point = p;
+ }
+ }
+ if (point) {
+ if (candidatePointCount == 1 || (candidatePointCount > 1 && m_ignoreAdditionalPoints)) {
+ point->setAccepted();
+ return true;
+ } else {
+ point->cancelAllGrabs(this);
+ }
+ } else {
+ qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << hex << m_pointInfo.m_id
+ << "is missing from current event, but was neither canceled nor released";
+ return false;
+ }
+ } else {
+ // We have not yet chosen a point; choose the first one for which wantsEventPoint() returns true.
+ int candidatePointCount = 0;
+ int c = event->pointCount();
+ QQuickEventPoint *chosen = nullptr;
+ for (int i = 0; i < c; ++i) {
+ QQuickEventPoint *p = event->point(i);
+ if (!p->exclusiveGrabber() && wantsEventPoint(p)) {
+ if (!chosen)
+ chosen = p;
+ ++candidatePointCount;
+ }
+ }
+ if (chosen && candidatePointCount == 1) {
+ m_pointInfo.m_id = chosen->pointId();
+ chosen->setAccepted();
+ }
+ }
+ return m_pointInfo.m_id;
+}
+
+void QQuickSinglePointHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ QQuickPointerDeviceHandler::handlePointerEventImpl(event);
+ QQuickEventPoint *currentPoint = event->pointById(m_pointInfo.m_id);
+ Q_ASSERT(currentPoint);
+ if (!m_pointInfo.m_id || !currentPoint->isAccepted()) {
+ reset();
+ } else {
+ if (event->asPointerTouchEvent()) {
+ QQuickEventTouchPoint *tp = static_cast<QQuickEventTouchPoint *>(currentPoint);
+ m_pointInfo.m_uniqueId = tp->uniqueId();
+ m_pointInfo.m_rotation = tp->rotation();
+ m_pointInfo.m_pressure = tp->pressure();
+ m_pointInfo.m_ellipseDiameters = tp->ellipseDiameters();
+ } else if (event->asPointerTabletEvent()) {
+ // TODO
+ } else {
+ m_pointInfo.m_uniqueId = event->device()->uniqueId();
+ m_pointInfo.m_rotation = 0;
+ m_pointInfo.m_pressure = event->buttons() ? 1 : 0;
+ m_pointInfo.m_ellipseDiameters = QSizeF();
+ }
+ m_pointInfo.m_position = currentPoint->position();
+ m_pointInfo.m_scenePosition = currentPoint->scenePosition();
+ if (currentPoint->state() == QQuickEventPoint::Updated)
+ m_pointInfo.m_velocity = currentPoint->velocity();
+ handleEventPoint(currentPoint);
+ switch (currentPoint->state()) {
+ case QQuickEventPoint::Pressed:
+ m_pointInfo.m_pressPosition = currentPoint->position();
+ m_pointInfo.m_scenePressPosition = currentPoint->scenePosition();
+ m_pointInfo.m_pressedButtons = event->buttons();
+ break;
+ case QQuickEventPoint::Released:
+ setExclusiveGrab(currentPoint, false);
+ reset();
+ break;
+ default:
+ m_pointInfo.m_pressedButtons = event->buttons();
+ break;
+ }
+ emit pointChanged();
+ }
+}
+
+bool QQuickSinglePointHandler::wantsEventPoint(QQuickEventPoint *point)
+{
+ return parentContains(point);
+}
+
+void QQuickSinglePointHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+{
+ if (grabber != this)
+ return;
+ switch (stateChange) {
+ case QQuickEventPoint::GrabExclusive:
+ m_pointInfo.m_sceneGrabPosition = point->sceneGrabPosition();
+ setActive(true);
+ QQuickPointerHandler::onGrabChanged(grabber, stateChange, point);
+ break;
+ case QQuickEventPoint::GrabPassive:
+ m_pointInfo.m_sceneGrabPosition = point->sceneGrabPosition();
+ QQuickPointerHandler::onGrabChanged(grabber, stateChange, point);
+ break;
+ case QQuickEventPoint::OverrideGrabPassive:
+ return; // don't emit
+ case QQuickEventPoint::UngrabPassive:
+ case QQuickEventPoint::UngrabExclusive:
+ case QQuickEventPoint::CancelGrabPassive:
+ case QQuickEventPoint::CancelGrabExclusive:
+ // the grab is lost or relinquished, so the point is no longer relevant
+ QQuickPointerHandler::onGrabChanged(grabber, stateChange, point);
+ reset();
+ break;
+ }
+ emit singlePointGrabChanged();
+}
+
+void QQuickSinglePointHandler::setIgnoreAdditionalPoints(bool v)
+{
+ m_ignoreAdditionalPoints = v;
+}
+
+void QQuickSinglePointHandler::moveTarget(QPointF pos, QQuickEventPoint *point)
+{
+ target()->setPosition(pos);
+ m_pointInfo.m_scenePosition = point->scenePosition();
+ m_pointInfo.m_position = target()->mapFromScene(m_pointInfo.m_scenePosition);
+}
+
+/*!
+ \qmlproperty int QtQuick::SinglePointHandler::acceptedButtons
+
+ The mouse buttons which can activate this Pointer Handler.
+
+ By default, this property is set to \l Qt.LeftButton.
+ It can be set to an OR combination of mouse buttons, and will ignore events
+ from other buttons.
+
+ For example, a control could be made to respond to left and right clicks
+ in different ways, with two handlers:
+
+ \qml
+ Item {
+ TapHandler {
+ onTapped: console.log("left clicked")
+ }
+ TapHandler {
+ acceptedButtons: Qt.RightButton
+ onTapped: console.log("right clicked")
+ }
+ }
+ \endqml
+
+ \note Tapping on a touchscreen or tapping the stylus on a graphics tablet
+ emulates clicking the left mouse button. This behavior can be altered via
+ \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices} or
+ \l {PointerDeviceHandler::acceptedPointerTypes}{acceptedPointerTypes}.
+*/
+void QQuickSinglePointHandler::setAcceptedButtons(Qt::MouseButtons buttons)
+{
+ if (m_acceptedButtons == buttons)
+ return;
+
+ m_acceptedButtons = buttons;
+ emit acceptedButtonsChanged();
+}
+
+void QQuickSinglePointHandler::reset()
+{
+ setActive(false);
+ m_pointInfo.reset();
+}
+
+/*!
+ \readonly
+ \qmlproperty HandlerPoint QtQuick::SinglePointHandler::point
+
+ The event point currently being handled. When no point is currently being
+ handled, this object is reset to default values (all coordinates are 0).
+*/
+
+/*!
+ \qmltype HandlerPoint
+ \instantiates QQuickHandlerPoint
+ \inqmlmodule Qt.labs.handlers
+ \ingroup qtquick-handlers
+ \brief An event point
+
+ A QML representation of a QQuickEventPoint.
+
+ It's possible to make bindings to properties of a \l SinglePointHandler's
+ current point. For example:
+
+ \snippet pointerHandlers/dragHandlerNullTarget.qml 0
+
+ The point is kept up-to-date when the DragHandler is actively responding to
+ an EventPoint; but when the point is released, or the current point is
+ being handled by a different handler, \c position.x and \c position.y are 0.
+
+ \note This is practically identical to QtQuick::EventPoint; however an
+ EventPoint is a long-lived QObject which is invalidated between gestures
+ and reused for subsequent event deliveries. Continuous bindings to its
+ properties are not possible, and an individual handler cannot rely on it
+ outside the period when that point is part of an active gesture which that
+ handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
+ This allows you to make lifetime bindings to its properties.
+
+ \sa SinglePointHandler::point
+*/
+
+QQuickHandlerPoint::QQuickHandlerPoint()
+ : m_id(0)
+ , m_rotation(0)
+ , m_pressure(0)
+{}
+
+void QQuickHandlerPoint::reset()
+{
+ m_id = 0;
+ m_uniqueId = QPointingDeviceUniqueId();
+ m_position = QPointF();
+ m_scenePosition = QPointF();
+ m_pressPosition = QPointF();
+ m_scenePressPosition = QPointF();
+ m_sceneGrabPosition = QPointF();
+ m_velocity = QVector2D();
+ m_rotation = 0;
+ m_pressure = 0;
+ m_ellipseDiameters = QSizeF();
+ m_pressedButtons = Qt::NoButton;
+}
+
+/*!
+ \readonly
+ \qmlproperty int QtQuick::HandlerPoint::id
+ \brief The ID number of the point
+
+ During a touch gesture, from the time that the first finger is pressed
+ until the last finger is released, each touchpoint will have a unique ID
+ number. Likewise, if input from multiple devices occurs (for example
+ simultaneous mouse and touch presses), all the current event points from
+ all the devices will have unique IDs.
+
+ \note Do not assume that id numbers start at zero or that they are
+ sequential. Such an assumption is often false due to the way the underlying
+ drivers work.
+
+ \sa QTouchEvent::TouchPoint::id
+*/
+
+/*!
+ \readonly
+ \qmlproperty PointingDeviceUniqueId QtQuick::HandlerPoint::uniqueId
+ \brief The unique ID of the point, if any
+
+ This is normally empty, because touchscreens cannot uniquely identify fingers.
+
+ On some types of touchscreens, especially those using TUIO drivers,
+ it's possible to use recognizable physical tokens (fiducial objects)
+ in addition to fingers. So if this point is a touch point, and
+ uniqueId is set, it is the identifier for such an object.
+
+ On a graphics tablet, each type of stylus or other tool often has a unique
+ ID or serial number, which can be useful to respond in different ways to
+ different tools.
+
+ Interpreting the contents of this ID requires knowledge of the hardware and
+ drivers in use.
+
+ \sa QTabletEvent::uniqueId, QtQuick::TouchPoint::uniqueId, QtQuick::EventTouchPoint::uniqueId
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::position
+ \brief The position within the \c parent Item
+
+ This is the position of the event point relative to the bounds of the \l parent.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::scenePosition
+ \brief The position within the scene
+
+ This is the position of the event point relative to the bounds of the Qt
+ Quick scene (typically the whole window).
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::pressPosition
+ \brief The pressed position within the \c parent Item
+
+ This is the position at which this point was pressed, relative to the
+ bounds of the \l parent.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::scenePressPosition
+ \brief The pressed position within the scene
+
+ This is the position at which this point was pressed, in the coordinate
+ system of the \l {Qt Quick Scene Graph}{scene graph}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::sceneGrabPosition
+ \brief The grabbed position within the scene
+
+ If this point has been grabbed by a Pointer Handler or an Item, it means
+ that object has taken sole responsibility for handling the movement and the
+ release if this point. In that case, this is the position at which the grab
+ occurred, in the coordinate system of the \l {Qt Quick Scene Graph}{scene graph}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty enum QtQuick::HandlerPoint::pressedButtons
+ \brief Which mouse or stylus buttons are currently pressed
+
+ \sa MouseArea::pressedButtons
+*/
+
+/*!
+ \readonly
+ \qmlproperty QVector2D QtQuick::HandlerPoint::velocity
+ \brief A vector representing the average speed and direction of movement
+
+ This is a velocity vector pointing in the direction of movement, in logical
+ pixels per second. It has x and y components, at least one of which will be
+ nonzero when this point is in motion. It holds the average recent velocity:
+ how fast and in which direction the event point has been moving recently.
+
+ \sa QtQuick::EventPoint::velocity, QtQuick::TouchPoint::velocity, QTouchEvent::TouchPoint::velocity
+*/
+
+/*!
+ \readonly
+ \qmlproperty qreal QtQuick::HandlerPoint::rotation
+
+ This property holds the rotation angle of the stylus on a graphics tablet
+ or the contact patch of a touchpoint on a touchscreen.
+
+ It is valid only with certain tablet stylus devices and touchscreens that
+ can measure the rotation angle. Otherwise, it will be zero.
+*/
+
+/*!
+ \readonly
+ \qmlproperty qreal QtQuick::HandlerPoint::pressure
+
+ This property tells how hard the user is pressing the stylus on a graphics
+ tablet or the finger against a touchscreen, in the range from \c 0 (no
+ measurable pressure) to \c 1.0 (maximum pressure which the device can
+ measure).
+
+ It is valid only with certain tablets and touchscreens that can measure
+ pressure. Otherwise, it will be zero.
+*/
+
+/*!
+ \readonly
+ \qmlproperty size QtQuick::HandlerPoint::ellipseDiameters
+
+ This property holds the diameters of the contact patch, if the event
+ comes from a touchpoint and the device provides this information.
+
+ A touchpoint is modeled as an elliptical area where the finger is pressed
+ against the touchscreen. (In fact, it could also be modeled as a bitmap;
+ but in that case we expect an elliptical bounding estimate to be fitted to
+ the contact patch before the event is sent.) The harder the user presses,
+ the larger the contact patch; so, these diameters provide an alternate way
+ of detecting pressure, in case the device does not include a separate
+ pressure sensor. The ellipse is centered on \l scenePosition (\l position
+ in the PointerHandler's Item's local coordinates). The \l rotation property
+ provides the rotation of the ellipse, if known. It is expected that if the
+ \l rotation is zero, the \l {QSize::height}{height} is the larger dimension
+ (the major axis), because of the usual hand position, reaching upward or
+ outward across the surface.
+
+ If the contact patch is unknown, or the device is not a touchscreen,
+ these values will be zero.
+
+ \sa QtQuick::EventPoint::ellipseDiameters, QtQuick::TouchPoint::ellipseDiameters, QTouchEvent::TouchPoint::ellipseDiameters
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquicksinglepointhandler_p.h b/src/quick/handlers/qquicksinglepointhandler_p.h
new file mode 100644
index 0000000000..386cea253a
--- /dev/null
+++ b/src/quick/handlers/qquicksinglepointhandler_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** 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 QQUICKPOINTERSINGLEHANDLER_H
+#define QQUICKPOINTERSINGLEHANDLER_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 "qquickpointerdevicehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSinglePointHandler;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickHandlerPoint {
+ Q_GADGET
+ Q_PROPERTY(int id READ id)
+ Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId)
+ Q_PROPERTY(QPointF position READ position)
+ Q_PROPERTY(QPointF scenePosition READ scenePosition)
+ Q_PROPERTY(QPointF pressPosition READ pressPosition)
+ Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition)
+ Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition)
+ Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons)
+ Q_PROPERTY(QVector2D velocity READ velocity)
+ Q_PROPERTY(qreal rotation READ rotation)
+ Q_PROPERTY(qreal pressure READ pressure)
+ Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters)
+
+public:
+ QQuickHandlerPoint();
+
+ int id() const { return m_id; }
+ Qt::MouseButtons pressedButtons() const { return m_pressedButtons; }
+ QPointF pressPosition() const { return m_pressPosition; }
+ QPointF scenePressPosition() const { return m_scenePressPosition; }
+ QPointF sceneGrabPosition() const { return m_sceneGrabPosition; }
+ QPointF position() const { return m_position; }
+ QPointF scenePosition() const { return m_scenePosition; }
+ QVector2D velocity() const { return m_velocity; }
+ qreal rotation() const { return m_rotation; }
+ qreal pressure() const { return m_pressure; }
+ QSizeF ellipseDiameters() const { return m_ellipseDiameters; }
+ QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; }
+
+private:
+ void reset();
+ int m_id;
+ QPointingDeviceUniqueId m_uniqueId;
+ Qt::MouseButtons m_pressedButtons;
+ QPointF m_position;
+ QPointF m_scenePosition;
+ QPointF m_pressPosition;
+ QPointF m_scenePressPosition;
+ QPointF m_sceneGrabPosition;
+ QVector2D m_velocity;
+ qreal m_rotation;
+ qreal m_pressure;
+ QSizeF m_ellipseDiameters;
+ friend class QQuickSinglePointHandler;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointHandler : public QQuickPointerDeviceHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
+ Q_PROPERTY(QQuickHandlerPoint point READ point NOTIFY pointChanged)
+public:
+ explicit QQuickSinglePointHandler(QObject *parent = 0);
+ virtual ~QQuickSinglePointHandler() { }
+
+ Qt::MouseButtons acceptedButtons() const { return m_acceptedButtons; }
+ void setAcceptedButtons(Qt::MouseButtons buttons);
+
+ QQuickHandlerPoint point() const { return m_pointInfo; }
+
+Q_SIGNALS:
+ void pointChanged();
+ void singlePointGrabChanged(); // QQuickPointerHandler::grabChanged signal can't be a property notifier here
+ void acceptedButtonsChanged();
+
+protected:
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ virtual bool wantsEventPoint(QQuickEventPoint *point);
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+ virtual void handleEventPoint(QQuickEventPoint *point) = 0;
+
+ QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointInfo.m_id); }
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override;
+
+ void setIgnoreAdditionalPoints(bool v = true);
+
+ void moveTarget(QPointF pos, QQuickEventPoint *point);
+
+private:
+ void reset();
+
+private:
+ QQuickHandlerPoint m_pointInfo;
+ Qt::MouseButtons m_acceptedButtons;
+ bool m_ignoreAdditionalPoints : 1;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickHandlerPoint)
+QML_DECLARE_TYPE(QQuickSinglePointHandler)
+
+#endif // QQUICKPOINTERSINGLEHANDLER_H
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
new file mode 100644
index 0000000000..e5b728db0f
--- /dev/null
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -0,0 +1,363 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 "qquicktaphandler_p.h"
+#include <qpa/qplatformtheme.h>
+#include <private/qguiapplication_p.h>
+#include <QtGui/qstylehints.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcTapHandler, "qt.quick.handler.tap")
+
+qreal QQuickTapHandler::m_multiTapInterval(0.0);
+// single tap distance is the same as the drag threshold
+int QQuickTapHandler::m_mouseMultiClickDistanceSquared(-1);
+int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
+
+/*!
+ \qmltype TapHandler
+ \instantiates QQuickTapHandler
+ \inherits SinglePointHandler
+ \inqmlmodule Qt.labs.handlers
+ \ingroup qtquick-handlers
+ \brief Handler for taps and clicks.
+
+ TapHandler is a handler for taps on a touchscreen or clicks on a mouse.
+
+ Detection of a valid tap gesture depends on \l gesturePolicy.
+ Note that buttons (such as QPushButton) are often implemented not to care
+ whether the press and release occur close together: if you press the button
+ and then change your mind, you need to drag all the way off the edge of the
+ button in order to cancel the click. Therefore the default
+ \l gesturePolicy is \c TapHandler.ReleaseWithinBounds. If you want to require
+ that the press and release are close together in both space and time,
+ set it to \c TapHandler.DragThreshold.
+
+ For multi-tap gestures (double-tap, triple-tap etc.), the distance moved
+ must not exceed QPlatformTheme::MouseDoubleClickDistance with mouse and
+ QPlatformTheme::TouchDoubleTapDistance with touch, and the time between
+ taps must not exceed QStyleHints::mouseDoubleClickInterval().
+
+ \sa MouseArea
+*/
+
+QQuickTapHandler::QQuickTapHandler(QObject *parent)
+ : QQuickSinglePointHandler(parent)
+ , m_pressed(false)
+ , m_gesturePolicy(ReleaseWithinBounds)
+ , m_tapCount(0)
+ , m_longPressThreshold(-1)
+ , m_lastTapTimestamp(0.0)
+{
+ if (m_mouseMultiClickDistanceSquared < 0) {
+ m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval() / 1000.0;
+ m_mouseMultiClickDistanceSquared = QGuiApplicationPrivate::platformTheme()->
+ themeHint(QPlatformTheme::MouseDoubleClickDistance).toInt();
+ m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared;
+ m_touchMultiTapDistanceSquared = QGuiApplicationPrivate::platformTheme()->
+ themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt();
+ m_touchMultiTapDistanceSquared *= m_touchMultiTapDistanceSquared;
+ }
+}
+
+QQuickTapHandler::~QQuickTapHandler()
+{
+}
+
+static bool dragOverThreshold(QQuickEventPoint *point)
+{
+ QPointF delta = point->scenePosition() - point->scenePressPosition();
+ return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) ||
+ QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point));
+}
+
+bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point)
+{
+ // If the user has not violated any constraint, it could be a tap.
+ // Otherwise we want to give up the grab so that a competing handler
+ // (e.g. DragHandler) gets a chance to take over.
+ // Don't forget to emit released in case of a cancel.
+ bool ret = false;
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ case QQuickEventPoint::Released:
+ ret = parentContains(point);
+ break;
+ case QQuickEventPoint::Updated:
+ switch (m_gesturePolicy) {
+ case DragThreshold:
+ ret = !dragOverThreshold(point);
+ break;
+ case WithinBounds:
+ ret = parentContains(point);
+ break;
+ case ReleaseWithinBounds:
+ ret = point->pointId() == this->point().id();
+ break;
+ }
+ break;
+ case QQuickEventPoint::Stationary:
+ // Never react in any way when the point hasn't moved.
+ // In autotests, the point's position may not even be correct, because
+ // QTest::touchEvent(window, touchDevice).stationary(1)
+ // provides no opportunity to give a position, so it ends up being random.
+ break;
+ }
+ // If this is the grabber, returning false from this function will cancel the grab,
+ // so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called.
+ // But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but
+ // we still don't want to be pressed anymore.
+ if (!ret && point->pointId() == this->point().id() && point->state() != QQuickEventPoint::Stationary)
+ setPressed(false, true, point);
+ return ret;
+}
+
+void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ setPressed(true, false, point);
+ break;
+ case QQuickEventPoint::Released:
+ if ((point->pointerEvent()->buttons() & acceptedButtons()) == Qt::NoButton)
+ setPressed(false, false, point);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ \qmlproperty real TapHandler::longPressThreshold
+
+ The time in seconds that an event point must be pressed in order to
+ trigger a long press gesture and emit the \l longPressed() signal.
+ If the point is released before this time limit, a tap can be detected
+ if the \l gesturePolicy constraint is satisfied. The default value is
+ QStyleHints::mousePressAndHoldInterval() converted to seconds.
+*/
+qreal QQuickTapHandler::longPressThreshold() const
+{
+ return longPressThresholdMilliseconds() / 1000.0;
+}
+
+void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold)
+{
+ int ms = qRound(longPressThreshold * 1000);
+ if (m_longPressThreshold == ms)
+ return;
+
+ m_longPressThreshold = ms;
+ emit longPressThresholdChanged();
+}
+
+int QQuickTapHandler::longPressThresholdMilliseconds() const
+{
+ return (m_longPressThreshold < 0 ? QGuiApplication::styleHints()->mousePressAndHoldInterval() : m_longPressThreshold);
+}
+
+void QQuickTapHandler::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_longPressTimer.timerId()) {
+ m_longPressTimer.stop();
+ qCDebug(lcTapHandler) << objectName() << "longPressed";
+ emit longPressed();
+ }
+}
+
+/*!
+ \qmlproperty enumeration TapHandler::gesturePolicy
+
+ The spatial constraint for a tap or long press gesture to be recognized,
+ in addition to the constraint that the release must occur before
+ \l longPressThreshold has elapsed. If these constraints are not satisfied,
+ the \l tapped signal is not emitted, and \l tapCount is not incremented.
+ If the spatial constraint is violated, \l isPressed transitions immediately
+ from true to false, regardless of the time held.
+
+ \value TapHandler.DragThreshold
+ The event point must not move significantly. If the mouse, finger
+ or stylus moves past the system-wide drag threshold
+ (QStyleHints::startDragDistance), the tap gesture is canceled, even
+ if the button or finger is still pressed. This policy can be useful
+ whenever TapHandler needs to cooperate with other pointer handlers
+ (for example \l DragHandler), because in this case TapHandler will
+ never grab.
+
+ \value TapHandler.WithinBounds
+ If the event point leaves the bounds of the \l target item, the tap
+ gesture is canceled. The TapHandler will grab on press, but release
+ the grab as soon as the boundary constraint is no longer satisfied.
+
+ \value TapHandler.ReleaseWithinBounds
+ (the default value) At the time of release (the mouse button is
+ released or the finger is lifted), if the event point is outside
+ the bounds of the \l target item, a tap gesture is not recognized.
+ This is the default value, because it corresponds to typical button
+ behavior: you can cancel a click by dragging outside the button,
+ and you can also change your mind by dragging back inside the button
+ before release. Note that it's necessary for TapHandler to grab on
+ press and retain it until release (greedy grab) in order to detect
+ this gesture.
+*/
+void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
+{
+ if (m_gesturePolicy == gesturePolicy)
+ return;
+
+ m_gesturePolicy = gesturePolicy;
+ emit gesturePolicyChanged();
+}
+
+/*!
+ \qmlproperty bool TapHandler::pressed
+ \readonly
+
+ Holds true whenever the mouse or touch point is pressed,
+ and any movement since the press is compliant with the current
+ \l gesturePolicy. When the event point is released or the policy is
+ violated, \e pressed will change to false.
+*/
+void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *point)
+{
+ if (m_pressed != press) {
+ qCDebug(lcTapHandler) << objectName() << "pressed" << m_pressed << "->" << press << (cancel ? "CANCEL" : "") << point;
+ m_pressed = press;
+ connectPreRenderSignal(press);
+ updateTimeHeld();
+ if (press) {
+ m_longPressTimer.start(longPressThresholdMilliseconds(), this);
+ m_holdTimer.start();
+ } else {
+ m_longPressTimer.stop();
+ m_holdTimer.invalidate();
+ }
+ if (press) {
+ // on press, grab before emitting changed signals
+ if (m_gesturePolicy == DragThreshold)
+ setPassiveGrab(point, press);
+ else
+ setExclusiveGrab(point, press);
+ }
+ if (!cancel && !press && parentContains(point)) {
+ if (point->timeHeld() < longPressThreshold()) {
+ // Assuming here that pointerEvent()->timestamp() is in ms.
+ qreal ts = point->pointerEvent()->timestamp() / 1000.0;
+ if (ts - m_lastTapTimestamp < m_multiTapInterval &&
+ QVector2D(point->scenePosition() - m_lastTapPos).lengthSquared() <
+ (point->pointerEvent()->device()->type() == QQuickPointerDevice::Mouse ?
+ m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
+ ++m_tapCount;
+ else
+ m_tapCount = 1;
+ qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times";
+ emit tapped();
+ emit tapCountChanged();
+ m_lastTapTimestamp = ts;
+ m_lastTapPos = point->scenePosition();
+ } else {
+ qCDebug(lcTapHandler) << objectName() << "tap threshold" << longPressThreshold() << "exceeded:" << point->timeHeld();
+ }
+ }
+ emit pressedChanged();
+ if (!press && m_gesturePolicy != DragThreshold) {
+ // on release, ungrab after emitting changed signals
+ setExclusiveGrab(point, press);
+ }
+ }
+}
+
+void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+{
+ QQuickSinglePointHandler::onGrabChanged(grabber, stateChange, point);
+ bool isCanceled = stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive;
+ if (grabber == this && (isCanceled || point->state() == QQuickEventPoint::Released))
+ setPressed(false, isCanceled, point);
+}
+
+void QQuickTapHandler::connectPreRenderSignal(bool conn)
+{
+ if (conn)
+ connect(parentItem()->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
+ else
+ disconnect(parentItem()->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
+}
+
+void QQuickTapHandler::updateTimeHeld()
+{
+ emit timeHeldChanged();
+}
+
+/*!
+ \qmlproperty int TapHandler::tapCount
+ \readonly
+
+ The number of taps which have occurred within the time and space
+ constraints to be considered a single gesture. For example, to detect
+ a double-tap, you can write:
+
+ \qml
+ Rectangle {
+ width: 100; height: 30
+ signal doubleTap
+ TapHandler {
+ acceptedButtons: Qt.AllButtons
+ onTapped: if (tapCount == 2) doubleTap()
+ }
+ }
+ \endqml
+*/
+
+/*!
+ \qmlproperty real TapHandler::timeHeld
+ \readonly
+
+ The amount of time in seconds that a pressed point has been held, without
+ moving beyond the drag threshold. It will be updated at least once per
+ frame rendered, which enables rendering an animation showing the progress
+ towards an action which will be triggered by a long-press. It is also
+ possible to trigger one of a series of actions depending on how long the
+ press is held.
+
+ A value of less than zero means no point is being held within this
+ handler's \l [QML] Item.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h
new file mode 100644
index 0000000000..6504ec87f0
--- /dev/null
+++ b/src/quick/handlers/qquicktaphandler_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 QQUICKTAPHANDLER_H
+#define QQUICKTAPHANDLER_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 "qquicksinglepointhandler_p.h"
+#include <QtCore/qbasictimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickSinglePointHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
+ Q_PROPERTY(int tapCount READ tapCount NOTIFY tapCountChanged)
+ Q_PROPERTY(qreal timeHeld READ timeHeld NOTIFY timeHeldChanged)
+ Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged)
+ Q_PROPERTY(GesturePolicy gesturePolicy READ gesturePolicy WRITE setGesturePolicy NOTIFY gesturePolicyChanged)
+
+public:
+ enum GesturePolicy {
+ DragThreshold,
+ WithinBounds,
+ ReleaseWithinBounds
+ };
+ Q_ENUM(GesturePolicy)
+
+ explicit QQuickTapHandler(QObject *parent = 0);
+ ~QQuickTapHandler();
+
+ bool isPressed() const { return m_pressed; }
+
+ int tapCount() const { return m_tapCount; }
+ qreal timeHeld() const { return (m_holdTimer.isValid() ? m_holdTimer.elapsed() / 1000.0 : -1.0); }
+
+ qreal longPressThreshold() const;
+ void setLongPressThreshold(qreal longPressThreshold);
+
+ GesturePolicy gesturePolicy() const { return m_gesturePolicy; }
+ void setGesturePolicy(GesturePolicy gesturePolicy);
+
+Q_SIGNALS:
+ void pressedChanged();
+ void tapCountChanged();
+ void timeHeldChanged();
+ void longPressThresholdChanged();
+ void gesturePolicyChanged();
+ void tapped();
+ void longPressed();
+
+protected:
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override;
+ void timerEvent(QTimerEvent *event) override;
+ bool wantsEventPoint(QQuickEventPoint *point) override;
+ void handleEventPoint(QQuickEventPoint *point) override;
+
+private:
+ void setPressed(bool press, bool cancel, QQuickEventPoint *point);
+ int longPressThresholdMilliseconds() const;
+ void connectPreRenderSignal(bool conn = true);
+ void updateTimeHeld();
+
+private:
+ bool m_pressed;
+ GesturePolicy m_gesturePolicy;
+ int m_tapCount;
+ int m_longPressThreshold;
+ QBasicTimer m_longPressTimer;
+ QElapsedTimer m_holdTimer;
+ QPointF m_lastTapPos;
+ qreal m_lastTapTimestamp;
+
+ static qreal m_multiTapInterval;
+ static int m_mouseMultiClickDistanceSquared;
+ static int m_touchMultiTapDistanceSquared;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTapHandler)
+
+#endif // QQUICKTAPHANDLER_H