aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/handlers')
-rw-r--r--src/quick/handlers/handlers.pri28
-rw-r--r--src/quick/handlers/qquickdragaxis.cpp75
-rw-r--r--src/quick/handlers/qquickdragaxis_p.h87
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp312
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h110
-rw-r--r--src/quick/handlers/qquickhandlerpoint.cpp350
-rw-r--r--src/quick/handlers/qquickhandlerpoint_p.h121
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp106
-rw-r--r--src/quick/handlers/qquickhoverhandler_p.h91
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp347
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p.h120
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp563
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h168
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp262
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p.h99
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p_p.h75
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp550
-rw-r--r--src/quick/handlers/qquickpointerhandler_p.h157
-rw-r--r--src/quick/handlers/qquickpointerhandler_p_p.h84
-rw-r--r--src/quick/handlers/qquickpointhandler.cpp165
-rw-r--r--src/quick/handlers/qquickpointhandler_p.h80
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp223
-rw-r--r--src/quick/handlers/qquicksinglepointhandler_p.h97
-rw-r--r--src/quick/handlers/qquicksinglepointhandler_p_p.h79
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp432
-rw-r--r--src/quick/handlers/qquicktaphandler_p.h133
26 files changed, 4914 insertions, 0 deletions
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
new file mode 100644
index 0000000000..226cca22cb
--- /dev/null
+++ b/src/quick/handlers/handlers.pri
@@ -0,0 +1,28 @@
+HEADERS += \
+ $$PWD/qquickdraghandler_p.h \
+ $$PWD/qquickhandlerpoint_p.h \
+ $$PWD/qquickhoverhandler_p.h \
+ $$PWD/qquickmultipointhandler_p.h \
+ $$PWD/qquickpinchhandler_p.h \
+ $$PWD/qquickpointerdevicehandler_p.h \
+ $$PWD/qquickpointerdevicehandler_p_p.h \
+ $$PWD/qquickpointerhandler_p.h \
+ $$PWD/qquickpointerhandler_p_p.h \
+ $$PWD/qquickpointhandler_p.h \
+ $$PWD/qquicksinglepointhandler_p.h \
+ $$PWD/qquicksinglepointhandler_p_p.h \
+ $$PWD/qquicktaphandler_p.h \
+ $$PWD/qquickdragaxis_p.h
+
+SOURCES += \
+ $$PWD/qquickdraghandler.cpp \
+ $$PWD/qquickhandlerpoint.cpp \
+ $$PWD/qquickhoverhandler.cpp \
+ $$PWD/qquickmultipointhandler.cpp \
+ $$PWD/qquickpinchhandler.cpp \
+ $$PWD/qquickpointerdevicehandler.cpp \
+ $$PWD/qquickpointerhandler.cpp \
+ $$PWD/qquickpointhandler.cpp \
+ $$PWD/qquicksinglepointhandler.cpp \
+ $$PWD/qquicktaphandler.cpp \
+ $$PWD/qquickdragaxis.cpp
diff --git a/src/quick/handlers/qquickdragaxis.cpp b/src/quick/handlers/qquickdragaxis.cpp
new file mode 100644
index 0000000000..5efe19b2fe
--- /dev/null
+++ b/src/quick/handlers/qquickdragaxis.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** 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 "qquickdragaxis_p.h"
+#include <limits>
+
+QQuickDragAxis::QQuickDragAxis()
+ : m_minimum(-std::numeric_limits<qreal>::max())
+ , m_maximum(std::numeric_limits<qreal>::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();
+}
+
diff --git a/src/quick/handlers/qquickdragaxis_p.h b/src/quick/handlers/qquickdragaxis_p.h
new file mode 100644
index 0000000000..2c2e0a426d
--- /dev/null
+++ b/src/quick/handlers/qquickdragaxis_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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 QQUICKDRAGAXIS_P_H
+#define QQUICKDRAGAXIS_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 <QtCore/qobject.h>
+#include <QtCore/qglobal.h>
+
+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;
+};
+
+#endif // QQUICKDRAGAXIS_P_H
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
new file mode 100644
index 0000000000..48f0599284
--- /dev/null
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -0,0 +1,312 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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
+
+static const qreal DragAngleToleranceDegrees = 10;
+
+Q_LOGGING_CATEGORY(lcDragHandler, "qt.quick.handler.drag")
+
+/*!
+ \qmltype DragHandler
+ \instantiates QQuickDragHandler
+ \inherits MultiPointHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
+ \brief Handler for dragging.
+
+ DragHandler is a handler that is used to interactively move an Item.
+ Like other Input Handlers, by default it is fully functional, and
+ manipulates its \l {PointerHandler::target} {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 {PointerHandler::target} {target}, then it handles events within the
+ bounds of the \l {PointerHandler::parent} {parent} Item but
+ manipulates the \c target Item instead:
+
+ \snippet pointerHandlers/dragHandlerDifferentTarget.qml 0
+
+ A third way to use it is to set \l {PointerHandler::target} {target} to
+ \c null and react to property changes in some other way:
+
+ \snippet pointerHandlers/dragHandlerNullTarget.qml 0
+
+ If minimumPointCount and maximumPointCount are set to values larger than 1,
+ the user will need to drag that many fingers in the same direction to start
+ dragging. A multi-finger drag gesture can be detected independently of both
+ a (default) single-finger DragHandler and a PinchHandler on the same Item,
+ and thus can be used to adjust some other feature independently of the
+ usual pinch behavior: for example adjust a tilt transformation, or adjust
+ some other numeric value, if the \c target is set to null. But if the
+ \c target is an Item, \c centroid is the point at which the drag begins and
+ to which the \c target will be moved (subject to constraints).
+
+ At this time, drag-and-drop is not yet supported.
+
+ \sa Drag, MouseArea
+*/
+
+QQuickDragHandler::QQuickDragHandler(QQuickItem *parent)
+ : QQuickMultiPointHandler(parent, 1, 1)
+{
+}
+
+bool QQuickDragHandler::targetContainsCentroid()
+{
+ Q_ASSERT(parentItem() && target());
+ return target()->contains(targetCentroidPosition());
+}
+
+QPointF QQuickDragHandler::targetCentroidPosition()
+{
+ QPointF pos = m_centroid.position();
+ if (target() != parentItem())
+ pos = parentItem()->mapToItem(target(), pos);
+ return pos;
+}
+
+void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
+{
+ QQuickMultiPointHandler::onGrabChanged(grabber, transition, point);
+ if (grabber == this && transition == QQuickEventPoint::GrabExclusive) {
+ // In case the grab got handed over from another grabber, we might not get the Press.
+ if (!m_pressedInsideTarget) {
+ if (target())
+ m_pressTargetPos = QPointF(target()->width(), target()->height()) / 2;
+ } else if (m_pressTargetPos.isNull()) {
+ if (target())
+ m_pressTargetPos = targetCentroidPosition();
+ }
+ }
+}
+
+void QQuickDragHandler::onActiveChanged()
+{
+ QQuickMultiPointHandler::onActiveChanged();
+ if (active()) {
+ if (auto parent = parentItem()) {
+ if (currentEvent()->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);
+ }
+ } else {
+ m_pressTargetPos = QPointF();
+ m_pressedInsideTarget = false;
+ if (auto parent = parentItem()) {
+ parent->setKeepTouchGrab(false);
+ parent->setKeepMouseGrab(false);
+ }
+ }
+}
+
+void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ QQuickMultiPointHandler::handlePointerEventImpl(event);
+ event->setAccepted(true);
+
+ if (active()) {
+ // Calculate drag delta, taking into account the axis enabled constraint
+ // i.e. if xAxis is not enabled, then ignore the horizontal component of the actual movement
+ QVector2D accumulatedDragDelta = QVector2D(m_centroid.scenePosition() - m_centroid.scenePressPosition());
+ if (!m_xAxis.enabled())
+ accumulatedDragDelta.setX(0);
+ if (!m_yAxis.enabled())
+ accumulatedDragDelta.setY(0);
+ setTranslation(accumulatedDragDelta);
+ } else {
+ // Check that all points have been dragged past the drag threshold,
+ // to the extent that the constraints allow,
+ // and in approximately the same direction
+ qreal minAngle = 361;
+ qreal maxAngle = -361;
+ bool allOverThreshold = !event->isReleaseEvent();
+ QVector <QQuickEventPoint *> chosenPoints;
+
+ if (event->isPressEvent())
+ m_pressedInsideTarget = target() && m_currentPoints.count() > 0;
+
+ for (const QQuickHandlerPoint &p : m_currentPoints) {
+ if (!allOverThreshold)
+ break;
+ QQuickEventPoint *point = event->pointById(p.id());
+ chosenPoints << point;
+ setPassiveGrab(point);
+ // Calculate drag delta, taking into account the axis enabled constraint
+ // i.e. if xAxis is not enabled, then ignore the horizontal component of the actual movement
+ QVector2D accumulatedDragDelta = QVector2D(point->scenePosition() - point->scenePressPosition());
+ if (!m_xAxis.enabled()) {
+ // If horizontal dragging is disallowed, but the user is dragging
+ // mostly horizontally, then don't activate.
+ if (qAbs(accumulatedDragDelta.x()) > qAbs(accumulatedDragDelta.y()))
+ accumulatedDragDelta.setY(0);
+ accumulatedDragDelta.setX(0);
+ }
+ if (!m_yAxis.enabled()) {
+ // If vertical dragging is disallowed, but the user is dragging
+ // mostly vertically, then don't activate.
+ if (qAbs(accumulatedDragDelta.y()) > qAbs(accumulatedDragDelta.x()))
+ accumulatedDragDelta.setX(0);
+ accumulatedDragDelta.setY(0);
+ }
+ qreal angle = std::atan2(accumulatedDragDelta.y(), accumulatedDragDelta.x()) * 180 / M_PI;
+ bool overThreshold = QQuickWindowPrivate::dragOverThreshold(accumulatedDragDelta);
+ qCDebug(lcDragHandler) << "movement" << accumulatedDragDelta << "angle" << angle << "of point" << point
+ << "pressed @" << point->scenePressPosition() << "over threshold?" << overThreshold;
+ minAngle = qMin(angle, minAngle);
+ maxAngle = qMax(angle, maxAngle);
+ if (allOverThreshold && !overThreshold)
+ allOverThreshold = false;
+
+ if (event->isPressEvent()) {
+ // m_pressedInsideTarget should stay true iff ALL points in which DragHandler is interested
+ // have been pressed inside the target() Item. (E.g. in a Slider the parent might be the
+ // whole control while the target is just the knob.)
+ if (target()) {
+ const QPointF localPressPos = target()->mapFromScene(point->scenePressPosition());
+ m_pressedInsideTarget &= target()->contains(localPressPos);
+ m_pressTargetPos = targetCentroidPosition();
+ }
+ // QQuickWindowPrivate::deliverToPassiveGrabbers() skips subsequent delivery if the event is filtered.
+ // (That affects behavior for mouse but not for touch, because Flickable only handles mouse.)
+ // So we have to compensate by accepting the event here to avoid any parent Flickable from
+ // getting the event via direct delivery and grabbing too soon.
+ point->setAccepted(event->asPointerMouseEvent()); // stop propagation iff it's a mouse event
+ }
+ }
+ if (allOverThreshold) {
+ qreal angleDiff = maxAngle - minAngle;
+ if (angleDiff > 180)
+ angleDiff = 360 - angleDiff;
+ qCDebug(lcDragHandler) << "angle min" << minAngle << "max" << maxAngle << "range" << angleDiff;
+ if (angleDiff < DragAngleToleranceDegrees && grabPoints(chosenPoints))
+ setActive(true);
+ }
+ }
+ if (active() && target() && target()->parentItem()) {
+ const QPointF newTargetTopLeft = targetCentroidPosition() - m_pressTargetPos;
+ const QPointF xformOrigin = target()->transformOriginPoint();
+ const QPointF targetXformOrigin = newTargetTopLeft + xformOrigin;
+ QPointF pos = target()->parentItem()->mapFromItem(target(), targetXformOrigin);
+ pos -= xformOrigin;
+ QPointF targetItemPos = target()->position();
+ if (!m_xAxis.enabled())
+ pos.setX(targetItemPos.x());
+ if (!m_yAxis.enabled())
+ pos.setY(targetItemPos.y());
+ enforceAxisConstraints(&pos);
+ moveTarget(pos);
+ }
+}
+
+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::setTranslation(const QVector2D &trans)
+{
+ if (trans == m_translation) // fuzzy compare?
+ return;
+ m_translation = trans;
+ emit translationChanged();
+}
+
+/*!
+ \qmlpropertygroup QtQuick::DragHandler::xAxis
+ \qmlproperty real QtQuick::DragHandler::xAxis.minimum
+ \qmlproperty real QtQuick::DragHandler::xAxis.maximum
+ \qmlproperty bool QtQuick::DragHandler::xAxis.enabled
+
+ \c xAxis controls the constraints for horizontal dragging.
+
+ \c minimum is the minimum acceptable value of \l {Item::x}{x} to be
+ applied to the \l {PointerHandler::target} {target}.
+ \c maximum is the maximum acceptable value of \l {Item::x}{x} to be
+ applied to the \l {PointerHandler::target} {target}.
+ If \c enabled is true, horizontal dragging is allowed.
+ */
+
+/*!
+ \qmlpropertygroup QtQuick::DragHandler::yAxis
+ \qmlproperty real QtQuick::DragHandler::yAxis.minimum
+ \qmlproperty real QtQuick::DragHandler::yAxis.maximum
+ \qmlproperty bool QtQuick::DragHandler::yAxis.enabled
+
+ \c yAxis controls the constraints for vertical dragging.
+
+ \c minimum is the minimum acceptable value of \l {Item::y}{y} to be
+ applied to the \l {PointerHandler::target} {target}.
+ \c maximum is the maximum acceptable value of \l {Item::y}{y} to be
+ applied to the \l {PointerHandler::target} {target}.
+ If \c enabled is true, vertical dragging is allowed.
+ */
+
+/*!
+ \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..387a81eb43
--- /dev/null
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "qquickmultipointhandler_p.h"
+#include "qquickdragaxis_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
+{
+ 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(QQuickItem *parent = nullptr);
+
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+
+ QQuickDragAxis *xAxis() { return &m_xAxis; }
+ QQuickDragAxis *yAxis() { return &m_yAxis; }
+
+ QVector2D translation() const { return m_translation; }
+ void setTranslation(const QVector2D &trans);
+
+ void enforceConstraints();
+
+Q_SIGNALS:
+ void translationChanged();
+
+protected:
+ void onActiveChanged() override;
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
+
+private:
+ void ungrab();
+ void enforceAxisConstraints(QPointF *localPos);
+ bool targetContainsCentroid();
+ QPointF targetCentroidPosition();
+
+private:
+ QPointF m_pressTargetPos; // We must also store the local targetPos, because we cannot deduce
+ // the press target pos from the scene pos in case there was e.g a
+ // flick in one of the ancestors during the drag.
+ QVector2D m_translation;
+
+ QQuickDragAxis m_xAxis;
+ QQuickDragAxis m_yAxis;
+ bool m_pressedInsideTarget = false;
+
+ friend class QQuickDragAxis;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickDragHandler)
+QML_DECLARE_TYPE(QQuickDragAxis)
+
+#endif // QQUICKDRAGHANDLER_H
diff --git a/src/quick/handlers/qquickhandlerpoint.cpp b/src/quick/handlers/qquickhandlerpoint.cpp
new file mode 100644
index 0000000000..de21537f27
--- /dev/null
+++ b/src/quick/handlers/qquickhandlerpoint.cpp
@@ -0,0 +1,350 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "qquickhandlerpoint_p.h"
+#include "private/qquickevents_p_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
+
+/*!
+ \qmltype HandlerPoint
+ \instantiates QQuickHandlerPoint
+ \inqmlmodule QtQuick
+ \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()
+{}
+
+void QQuickHandlerPoint::localize(QQuickItem *item)
+{
+ m_pressPosition = item->mapFromScene(m_scenePressPosition);
+}
+
+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;
+ m_pressedModifiers = Qt::NoModifier;
+}
+
+void QQuickHandlerPoint::reset(const QQuickEventPoint *point)
+{
+ m_id = point->pointId();
+ const QQuickPointerEvent *event = point->pointerEvent();
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ m_pressPosition = point->position();
+ m_scenePressPosition = point->scenePosition();
+ m_pressedButtons = event->buttons();
+ break;
+ case QQuickEventPoint::Released:
+ if (event->buttons() == Qt::NoButton) {
+ reset();
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ m_scenePressPosition = point->scenePressPosition();
+ m_pressedButtons = event->buttons();
+ m_pressedModifiers = event->modifiers();
+ if (event->asPointerTouchEvent()) {
+ const QQuickEventTouchPoint *tp = static_cast<const QQuickEventTouchPoint *>(point);
+ m_uniqueId = tp->uniqueId();
+ m_rotation = tp->rotation();
+ m_pressure = tp->pressure();
+ m_ellipseDiameters = tp->ellipseDiameters();
+ } else if (event->asPointerTabletEvent()) {
+ // TODO
+ } else {
+ m_uniqueId = event->device()->uniqueId();
+ m_rotation = 0;
+ m_pressure = event->buttons() ? 1 : 0;
+ m_ellipseDiameters = QSizeF();
+ }
+ m_position = point->position();
+ m_scenePosition = point->scenePosition();
+ if (point->state() == QQuickEventPoint::Updated)
+ m_velocity = point->velocity();
+}
+
+void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
+{
+ if (points.isEmpty()) {
+ qWarning("reset: no points");
+ return;
+ }
+ if (points.count() == 1) {
+ *this = points.first(); // copy all values
+ return;
+ }
+ // all points are required to be from the same event
+ QPointF posSum;
+ QPointF scenePosSum;
+ QPointF pressPosSum;
+ QPointF scenePressPosSum;
+ QVector2D velocitySum;
+ qreal pressureSum = 0;
+ QSizeF ellipseDiameterSum;
+ for (const QQuickHandlerPoint &point : points) {
+ posSum += point.position();
+ scenePosSum += point.scenePosition();
+ pressPosSum += point.pressPosition();
+ scenePressPosSum += point.scenePressPosition();
+ velocitySum += point.velocity();
+ pressureSum += point.pressure();
+ ellipseDiameterSum += point.ellipseDiameters();
+ }
+ m_id = 0;
+ m_uniqueId = QPointingDeviceUniqueId();
+ // all points are required to be from the same event, so pressed buttons and modifiers should be the same
+ m_pressedButtons = points.first().pressedButtons();
+ m_pressedModifiers = points.first().modifiers();
+ m_position = posSum / points.size();
+ m_scenePosition = scenePosSum / points.size();
+ m_pressPosition = pressPosSum / points.size();
+ m_scenePressPosition = scenePressPosSum / points.size();
+ m_velocity = velocitySum / points.size();
+ m_rotation = 0; // averaging the rotations of all the points isn't very sensible
+ m_pressure = pressureSum / points.size();
+ m_ellipseDiameters = ellipseDiameterSum / points.size();
+}
+
+/*!
+ \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 {PointerHandler::parent} {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 {PointerHandler::parent} {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 enumeration QtQuick::HandlerPoint::pressedButtons
+ \brief Which mouse or stylus buttons are currently pressed
+
+ \sa MouseArea::pressedButtons
+*/
+
+/*!
+ \readonly
+ \qmlproperty enumeration QtQuick::HandlerPoint::modifiers
+ \brief Which modifier keys are currently pressed
+
+ This property holds the keyboard modifiers that were pressed at the time
+ the event occurred.
+*/
+
+/*!
+ \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::EventTouchPoint::ellipseDiameters, QtQuick::TouchPoint::ellipseDiameters, QTouchEvent::TouchPoint::ellipseDiameters
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickhandlerpoint_p.h b/src/quick/handlers/qquickhandlerpoint_p.h
new file mode 100644
index 0000000000..44fd830af2
--- /dev/null
+++ b/src/quick/handlers/qquickhandlerpoint_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 QQUICKHANDLERPOINT_H
+#define QQUICKHANDLERPOINT_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 QQuickMultiPointHandler;
+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(Qt::KeyboardModifiers modifiers READ modifiers)
+ 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; }
+ Qt::KeyboardModifiers modifiers() const { return m_pressedModifiers; }
+ 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; }
+ void localize(QQuickItem *item);
+
+ void reset();
+ void reset(const QQuickEventPoint *point);
+ void reset(const QVector<QQuickHandlerPoint> &points);
+
+private:
+ int m_id = 0;
+ QPointingDeviceUniqueId m_uniqueId;
+ Qt::MouseButtons m_pressedButtons = Qt::NoButton;
+ Qt::KeyboardModifiers m_pressedModifiers = Qt::NoModifier;
+ QPointF m_position;
+ QPointF m_scenePosition;
+ QPointF m_pressPosition;
+ QPointF m_scenePressPosition;
+ QPointF m_sceneGrabPosition;
+ QVector2D m_velocity;
+ qreal m_rotation = 0;
+ qreal m_pressure = 0;
+ QSizeF m_ellipseDiameters;
+ friend class QQuickMultiPointHandler;
+ friend class QQuickSinglePointHandler;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickHandlerPoint)
+
+#endif // QQUICKHANDLERPOINT_H
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
new file mode 100644
index 0000000000..61955cad03
--- /dev/null
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "qquickhoverhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcHoverHandler, "qt.quick.handler.hover")
+
+/*!
+ \qmltype HoverHandler
+ \instantiates QQuickHoverHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
+ \brief Handler for mouse and tablet hover.
+
+ HoverHandler detects a hovering cursor. Since touchscreens don't generally
+ offer hover events, in practice it detects a hovering mouse or tablet stylus.
+
+ \sa MouseArea
+*/
+
+QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
+ : QQuickSinglePointHandler(parent)
+{
+ // Rule out the touchscreen for now (can be overridden in QML in case a hover-detecting touchscreen exists)
+ setAcceptedDevices(static_cast<QQuickPointerDevice::DeviceType>(
+ static_cast<int>(QQuickPointerDevice::AllDevices) ^ static_cast<int>(QQuickPointerDevice::TouchScreen)));
+}
+
+QQuickHoverHandler::~QQuickHoverHandler()
+{
+ if (auto parent = parentItem())
+ QQuickItemPrivate::get(parent)->setHasHoverInChild(false);
+}
+
+void QQuickHoverHandler::componentComplete()
+{
+ parentItem()->setAcceptHoverEvents(true);
+ QQuickItemPrivate::get(parentItem())->setHasHoverInChild(true);
+}
+
+bool QQuickHoverHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ QQuickEventPoint *point = event->point(0);
+ if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(point) && parentContains(point)) {
+ // assume this is a mouse event, so there's only one point
+ setPointId(point->pointId());
+ return true;
+ }
+ setHovered(false);
+ return false;
+}
+
+void QQuickHoverHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ setHovered(true);
+ setPassiveGrab(point);
+}
+
+void QQuickHoverHandler::setHovered(bool hovered)
+{
+ if (m_hovered != hovered) {
+ qCDebug(lcHoverHandler) << objectName() << "hovered" << m_hovered << "->" << hovered;
+ m_hovered = hovered;
+ emit hoveredChanged();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickhoverhandler_p.h b/src/quick/handlers/qquickhoverhandler_p.h
new file mode 100644
index 0000000000..1ee2aeb7e6
--- /dev/null
+++ b/src/quick/handlers/qquickhoverhandler_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 QQUICKHOVERHANDLER_H
+#define QQUICKHOVERHANDLER_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 QQuickHoverHandler : public QQuickSinglePointHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged)
+
+public:
+ explicit QQuickHoverHandler(QQuickItem *parent = nullptr);
+ ~QQuickHoverHandler();
+
+ bool isHovered() const { return m_hovered; }
+
+Q_SIGNALS:
+ void hoveredChanged();
+
+protected:
+ void componentComplete() override;
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ void handleEventPoint(QQuickEventPoint *point) override;
+
+private:
+ void setHovered(bool hovered);
+
+private:
+ bool m_hovered = false;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickHoverHandler)
+
+#endif // QQUICKHOVERHANDLER_H
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
new file mode 100644
index 0000000000..baa68e5e53
--- /dev/null
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -0,0 +1,347 @@
+/****************************************************************************
+**
+** 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 QtQuick
+ \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(QQuickItem *parent, int minimumPointCount, int maximumPointCount)
+ : QQuickPointerDeviceHandler(parent)
+ , m_minimumPointCount(minimumPointCount)
+ , m_maximumPointCount(maximumPointCount)
+{
+}
+
+bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
+ return false;
+
+#if QT_CONFIG(gestures)
+ if (event->asPointerNativeGestureEvent())
+ return true;
+#endif
+
+ if (event->asPointerScrollEvent())
+ return false;
+
+ // If points were pressed or released within parentItem, reset stored state
+ // and check eligible points again. This class of handlers is intended to
+ // handle a specific number of points, so a differing number of points will
+ // usually result in different behavior. But otherwise if the currentPoints
+ // are all still there in the event, we're good to go (do not reset
+ // m_currentPoints, because we don't want to lose the pressPosition, and do
+ // not want to reshuffle the order either).
+ const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event);
+ if (candidatePoints.count() != m_currentPoints.count()) {
+ m_currentPoints.clear();
+ if (active()) {
+ setActive(false);
+ m_centroid.reset();
+ emit centroidChanged();
+ }
+ } else if (hasCurrentPoints(event)) {
+ return true;
+ }
+
+ const bool ret = (candidatePoints.size() >= minimumPointCount() && candidatePoints.size() <= maximumPointCount());
+ if (ret) {
+ const int c = candidatePoints.count();
+ m_currentPoints.resize(c);
+ for (int i = 0; i < c; ++i) {
+ m_currentPoints[i].reset(candidatePoints[i]);
+ m_currentPoints[i].localize(parentItem());
+ }
+ } else {
+ m_currentPoints.clear();
+ }
+ return ret;
+}
+
+void QQuickMultiPointHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ QQuickPointerHandler::handlePointerEventImpl(event);
+ // event's points can be reordered since the previous event, which is why m_currentPoints
+ // is _not_ a shallow copy of the QQuickPointerTouchEvent::m_touchPoints vector.
+ // So we have to update our m_currentPoints instances based on the given event.
+ for (QQuickHandlerPoint &p : m_currentPoints) {
+ const QQuickEventPoint *ep = event->pointById(p.id());
+ if (ep)
+ p.reset(ep);
+ }
+ QPointF sceneGrabPos = m_centroid.sceneGrabPosition();
+ m_centroid.reset(m_currentPoints);
+ m_centroid.m_sceneGrabPosition = sceneGrabPos; // preserve as it was
+ emit centroidChanged();
+}
+
+void QQuickMultiPointHandler::onActiveChanged()
+{
+ if (active()) {
+ m_centroid.m_sceneGrabPosition = m_centroid.m_scenePosition;
+ } else {
+ // Don't call m_centroid.reset() here, because in a QML onActiveChanged
+ // callback, we'd like to see what the position _was_, what the velocity _was_, etc.
+ // (having them undefined is not useful)
+ // But pressedButtons and pressedModifiers are meant to be more real-time than those
+ // (which seems a bit inconsistent, from one side).
+ m_centroid.m_pressedButtons = Qt::NoButton;
+ m_centroid.m_pressedModifiers = Qt::NoModifier;
+ }
+}
+
+void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *)
+{
+ // If another handler or item takes over this set of points, assume it has
+ // decided that it's the better fit for them. Don't immediately re-grab
+ // at the next opportunity. This should help to avoid grab cycles
+ // (e.g. between DragHandler and PinchHandler).
+ if (transition == QQuickEventPoint::UngrabExclusive || transition == QQuickEventPoint::CancelGrabExclusive)
+ m_currentPoints.clear();
+}
+
+QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointerEvent *event)
+{
+ QVector<QQuickEventPoint *> ret;
+ int c = event->pointCount();
+ // If one or more points are newly pressed or released, all non-released points are candidates for this handler.
+ // In other cases however, check whether it would be OK to steal the grab if the handler chooses to do that.
+ bool stealingAllowed = event->isPressEvent() || event->isReleaseEvent();
+ for (int i = 0; i < c; ++i) {
+ QQuickEventPoint *p = event->point(i);
+ if (QQuickPointerMouseEvent *me = event->asPointerMouseEvent()) {
+ if (me->buttons() == Qt::NoButton)
+ continue;
+ }
+ if (!stealingAllowed) {
+ QObject *exclusiveGrabber = p->exclusiveGrabber();
+ if (exclusiveGrabber && exclusiveGrabber != this && !canGrab(p))
+ continue;
+ }
+ if (p->state() != QQuickEventPoint::Released && wantsEventPoint(p))
+ 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 Input 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 Input 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();
+}
+
+bool QQuickMultiPointHandler::hasCurrentPoints(QQuickPointerEvent *event)
+{
+ if (event->pointCount() < m_currentPoints.size() || m_currentPoints.size() == 0)
+ return false;
+ // TODO optimize: either ensure the points are sorted,
+ // or use std::equal with a predicate
+ for (const QQuickHandlerPoint &p : qAsConst(m_currentPoints)) {
+ const QQuickEventPoint *ep = event->pointById(p.id());
+ if (!ep)
+ return false;
+ if (ep->state() == QQuickEventPoint::Released)
+ return false;
+ }
+ return true;
+}
+
+qreal QQuickMultiPointHandler::averageTouchPointDistance(const QPointF &ref)
+{
+ qreal ret = 0;
+ if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ return ret;
+ for (const QQuickHandlerPoint &p : m_currentPoints)
+ ret += QVector2D(p.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 (const QQuickHandlerPoint &p : m_currentPoints)
+ ret += QVector2D(p.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 (const QQuickHandlerPoint &p : m_currentPoints) {
+ qreal angle = QLineF(ref, p.scenePosition()).angle();
+ angles.append(PointData(p.id(), -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)
+{
+ if (points.isEmpty())
+ return false;
+ bool allowed = true;
+ for (QQuickEventPoint* point : points) {
+ if (point->exclusiveGrabber() != this && !canGrab(point)) {
+ allowed = false;
+ break;
+ }
+ }
+ if (allowed) {
+ for (QQuickEventPoint* point : points)
+ setExclusiveGrab(point);
+ }
+ return allowed;
+}
+
+void QQuickMultiPointHandler::moveTarget(QPointF pos)
+{
+ target()->setPosition(pos);
+ m_centroid.m_position = target()->mapFromScene(m_centroid.m_scenePosition);
+}
+
+/*!
+ \readonly
+ \qmlproperty QtQuick::HandlerPoint QtQuick::MultiPointHandler::centroid
+
+ A point exactly in the middle of the currently-pressed touch points.
+ If only one point is pressed, it's the same as that point.
+ A handler that has a \l target will normally transform it relative to this point.
+*/
+
+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..94142013cc
--- /dev/null
+++ b/src/quick/handlers/qquickmultipointhandler_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** 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 "qquickhandlerpoint_p.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(QQuickHandlerPoint centroid READ centroid NOTIFY centroidChanged)
+
+public:
+ explicit QQuickMultiPointHandler(QQuickItem *parent = nullptr, int minimumPointCount = 2, int maximumPointCount = -1);
+
+ 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);
+
+ QQuickHandlerPoint centroid() const { return m_centroid; }
+
+signals:
+ void minimumPointCountChanged();
+ void maximumPointCountChanged();
+ void marginChanged();
+ void centroidChanged();
+
+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;
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+ void onActiveChanged() override;
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
+ bool hasCurrentPoints(QQuickPointerEvent *event);
+ QVector<QQuickEventPoint *> eligiblePoints(QQuickPointerEvent *event);
+ 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);
+ void moveTarget(QPointF pos);
+
+protected:
+ QVector<QQuickHandlerPoint> m_currentPoints;
+ QQuickHandlerPoint m_centroid;
+ int m_minimumPointCount;
+ int m_maximumPointCount;
+};
+
+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..9ae2116d39
--- /dev/null
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -0,0 +1,563 @@
+/****************************************************************************
+**
+** 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 <QtQml/qqmlinfo.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>
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
+
+/*!
+ \qmltype PinchHandler
+ \instantiates QQuickPinchHandler
+ \inherits MultiPointHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-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 Input 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(QQuickItem *parent)
+ : QQuickMultiPointHandler(parent, 2)
+{
+}
+
+/*!
+ \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 (qFuzzyCompare(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 (qFuzzyCompare(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 (qFuzzyCompare(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 (qFuzzyCompare(m_maximumRotation, maximumRotation))
+ return;
+
+ m_maximumRotation = maximumRotation;
+ emit maximumRotationChanged();
+}
+
+#if QT_DEPRECATED_SINCE(5, 12)
+void QQuickPinchHandler::warnAboutMinMaxDeprecated() const
+{
+ qmlWarning(this) << "min and max constraints are now part of the xAxis and yAxis properties";
+}
+
+void QQuickPinchHandler::setMinimumX(qreal minX)
+{
+ warnAboutMinMaxDeprecated();
+ if (qFuzzyCompare(m_minimumX, minX))
+ return;
+ m_minimumX = minX;
+ emit minimumXChanged();
+}
+
+void QQuickPinchHandler::setMaximumX(qreal maxX)
+{
+ warnAboutMinMaxDeprecated();
+ if (qFuzzyCompare(m_maximumX, maxX))
+ return;
+ m_maximumX = maxX;
+ emit maximumXChanged();
+}
+
+void QQuickPinchHandler::setMinimumY(qreal minY)
+{
+ warnAboutMinMaxDeprecated();
+ if (qFuzzyCompare(m_minimumY, minY))
+ return;
+ m_minimumY = minY;
+ emit minimumYChanged();
+}
+
+void QQuickPinchHandler::setMaximumY(qreal maxY)
+{
+ warnAboutMinMaxDeprecated();
+ if (qFuzzyCompare(m_maximumY, maxY))
+ return;
+ m_maximumY = maxY;
+ emit maximumYChanged();
+}
+#endif
+
+bool QQuickPinchHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ if (!QQuickMultiPointHandler::wantsPointerEvent(event))
+ return false;
+
+#if QT_CONFIG(gestures)
+ if (const auto gesture = event->asPointerNativeGestureEvent()) {
+ if (minimumPointCount() == 2) {
+ switch (gesture->type()) {
+ case Qt::BeginNativeGesture:
+ case Qt::EndNativeGesture:
+ case Qt::ZoomNativeGesture:
+ case Qt::RotateNativeGesture:
+ return parentContains(event->point(0));
+ default:
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+#endif
+
+ return true;
+}
+
+/*!
+ \qmlpropertygroup QtQuick::PinchHandler::xAxis
+ \qmlproperty real QtQuick::PinchHandler::xAxis.minimum
+ \qmlproperty real QtQuick::PinchHandler::xAxis.maximum
+ \qmlproperty bool QtQuick::PinchHandler::xAxis.enabled
+
+ \c xAxis controls the constraints for horizontal translation of the \l target item.
+
+ \c minimum is the minimum acceptable x coordinate of the translation.
+ \c maximum is the maximum acceptable x coordinate of the translation.
+ If \c enabled is true, horizontal dragging is allowed.
+ */
+
+/*!
+ \qmlpropertygroup QtQuick::PinchHandler::yAxis
+ \qmlproperty real QtQuick::PinchHandler::yAxis.minimum
+ \qmlproperty real QtQuick::PinchHandler::yAxis.maximum
+ \qmlproperty bool QtQuick::PinchHandler::yAxis.enabled
+
+ \c yAxis controls the constraints for vertical translation of the \l target item.
+
+ \c minimum is the minimum acceptable y coordinate of the translation.
+ \c maximum is the maximum acceptable y coordinate of the translation.
+ If \c enabled is true, vertical dragging is allowed.
+ */
+
+/*!
+ \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()
+{
+ QQuickMultiPointHandler::onActiveChanged();
+ if (active()) {
+ m_startMatrix = QMatrix4x4();
+ m_startAngles = angles(m_centroid.sceneGrabPosition());
+ m_startDistance = averageTouchPointDistance(m_centroid.sceneGrabPosition());
+ m_activeRotation = 0;
+ m_activeTranslation = QVector2D();
+ if (const QQuickItem *t = target()) {
+ m_startScale = t->scale(); // TODO incompatible with independent x/y scaling
+ m_startRotation = t->rotation();
+ QVector3D xformOrigin(t->transformOriginPoint());
+ m_startMatrix.translate(float(t->x()), float(t->y()));
+ m_startMatrix.translate(xformOrigin);
+ m_startMatrix.scale(float(m_startScale));
+ m_startMatrix.rotate(float(m_startRotation), 0, 0, -1);
+ m_startMatrix.translate(-xformOrigin);
+ } else {
+ m_startScale = m_accumulatedScale;
+ 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 (const QQuickHandlerPoint &p : m_currentPoints)
+ qCDebug(lcPinchHandler) << hex << p.id() << p.sceneGrabPosition() << "->" << p.scenePosition();
+ }
+ QQuickMultiPointHandler::handlePointerEventImpl(event);
+
+ qreal dist = 0;
+#if QT_CONFIG(gestures)
+ if (const auto gesture = event->asPointerNativeGestureEvent()) {
+ m_centroid.reset(event->point(0));
+ switch (gesture->type()) {
+ case Qt::EndNativeGesture:
+ m_activeScale = 1;
+ m_activeRotation = 0;
+ m_activeTranslation = QVector2D();
+ m_centroid.reset();
+ 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()) {
+ setActive(true);
+ // Native gestures for 2-finger pinch do not allow dragging, so
+ // the centroid won't move during the gesture, and translation stays at zero
+ m_activeTranslation = QVector2D();
+ }
+ } else
+#endif // QT_CONFIG(gestures)
+ {
+ const bool containsReleasedPoints = event->isReleaseEvent();
+ QVector<QQuickEventPoint *> chosenPoints;
+ for (const QQuickHandlerPoint &p : m_currentPoints) {
+ QQuickEventPoint *ep = event->pointById(p.id());
+ chosenPoints << ep;
+ }
+ if (!active()) {
+ // Verify that at least one of the points has moved beyond threshold needed to activate the handler
+ int numberOfPointsDraggedOverThreshold = 0;
+ QVector2D accumulatedDrag;
+ const QVector2D currentCentroid(m_centroid.scenePosition());
+ const QVector2D pressCentroid(m_centroid.scenePressPosition());
+
+ QStyleHints *styleHints = QGuiApplication::styleHints();
+ const int dragThreshold = styleHints->startDragDistance();
+ const int dragThresholdSquared = dragThreshold * dragThreshold;
+
+ double accumulatedCentroidDistance = 0; // Used to detect scale
+ if (event->isPressEvent())
+ m_accumulatedStartCentroidDistance = 0; // Used to detect scale
+
+ float accumulatedMovementMagnitude = 0;
+
+ for (QQuickEventPoint *point : qAsConst(chosenPoints)) {
+ if (!containsReleasedPoints) {
+ accumulatedDrag += QVector2D(point->scenePressPosition() - point->scenePosition());
+ /*
+ In order to detect a drag, we want to check if all points have moved more or
+ less in the same direction.
+
+ We then take each point, and convert the point to a local coordinate system where
+ the centroid is the origin. This is done both for the press positions and the
+ current positions. We will then have two positions:
+
+ - pressCentroidRelativePosition
+ is the start point relative to the press centroid
+ - currentCentroidRelativePosition
+ is the current point relative to the current centroid
+
+ If those two points are far enough apart, it might not be considered as a drag
+ anymore. (Note that the threshold will matched to the average of the relative
+ movement of all the points). Therefore, a big relative movement will make a big
+ contribution to the average relative movement.
+
+ The algorithm then can be described as:
+ For each point:
+ - Calculate vector pressCentroidRelativePosition (from the press centroid to the press position)
+ - Calculate vector currentCentroidRelativePosition (from the current centroid to the current position)
+ - Calculate the relative movement vector:
+
+ centroidRelativeMovement = currentCentroidRelativePosition - pressCentroidRelativePosition
+
+ and measure its magnitude. Add the magnitude to the accumulatedMovementMagnitude.
+
+ Finally, if the accumulatedMovementMagnitude is below some threshold, it means
+ that the points were stationary or they were moved in parallel (e.g. the hand
+ was moved, but the relative position between each finger remained very much
+ the same). This is then used to rule out if there is a rotation or scale.
+ */
+ QVector2D pressCentroidRelativePosition = QVector2D(point->scenePosition()) - currentCentroid;
+ QVector2D currentCentroidRelativePosition = QVector2D(point->scenePressPosition()) - pressCentroid;
+ QVector2D centroidRelativeMovement = currentCentroidRelativePosition - pressCentroidRelativePosition;
+ accumulatedMovementMagnitude += centroidRelativeMovement.length();
+
+ accumulatedCentroidDistance += qreal(pressCentroidRelativePosition.length());
+ if (event->isPressEvent())
+ m_accumulatedStartCentroidDistance += qreal((QVector2D(point->scenePressPosition()) - pressCentroid).length());
+ } else {
+ setPassiveGrab(point);
+ }
+ if (point->state() == QQuickEventPoint::Pressed) {
+ point->setAccepted(false); // don't stop propagation
+ setPassiveGrab(point);
+ }
+ if (QQuickWindowPrivate::dragOverThreshold(point))
+ ++numberOfPointsDraggedOverThreshold;
+ }
+
+ const bool requiredNumberOfPointsDraggedOverThreshold = numberOfPointsDraggedOverThreshold >= minimumPointCount() && numberOfPointsDraggedOverThreshold <= maximumPointCount();
+ accumulatedMovementMagnitude /= m_currentPoints.count();
+
+ QVector2D avgDrag = accumulatedDrag / m_currentPoints.count();
+ if (!xAxis()->enabled())
+ avgDrag.setX(0);
+ if (!yAxis()->enabled())
+ avgDrag.setY(0);
+
+ const qreal centroidMovementDelta = qreal((currentCentroid - pressCentroid).length());
+
+ qreal distanceToCentroidDelta = qAbs(accumulatedCentroidDistance - m_accumulatedStartCentroidDistance); // Used to detect scale
+ if (numberOfPointsDraggedOverThreshold >= 1) {
+ if (requiredNumberOfPointsDraggedOverThreshold && avgDrag.lengthSquared() >= dragThresholdSquared && accumulatedMovementMagnitude < dragThreshold) {
+ // Drag
+ if (grabPoints(chosenPoints))
+ setActive(true);
+ } else if (distanceToCentroidDelta > dragThreshold) { // all points should in accumulation have been moved beyond threshold (?)
+ // Scale
+ if (grabPoints(chosenPoints))
+ setActive(true);
+ } else if (distanceToCentroidDelta < dragThreshold && (centroidMovementDelta < dragThreshold)) {
+ // Rotate
+ // Since it wasn't a scale and if we exceeded the dragthreshold, and the
+ // centroid didn't moved much, the points must have been moved around the centroid.
+ if (grabPoints(chosenPoints))
+ setActive(true);
+ }
+ }
+ if (!active())
+ return;
+ }
+
+ // 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.scenePosition());
+ 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.scenePosition());
+ const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
+ m_activeRotation += angleDelta;
+ m_startAngles = std::move(newAngles);
+
+ if (!containsReleasedPoints)
+ acceptPoints(chosenPoints);
+ }
+
+ const qreal totalRotation = m_startRotation + m_activeRotation;
+ const qreal rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation);
+ m_activeRotation += (rotation - totalRotation); //adjust for the potential bounding above
+ m_accumulatedScale = m_startScale * m_activeScale;
+
+ if (target() && target()->parentItem()) {
+ const QPointF centroidParentPos = target()->parentItem()->mapFromScene(m_centroid.scenePosition());
+ // 3. Drag/translate
+ const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_centroid.sceneGrabPosition());
+ 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(float(m_activeRotation), 0, 0, 1);
+ mat.scale(float(m_activeScale));
+ mat.translate(-centroidParentVector);
+ mat.translate(QVector3D(m_activeTranslation));
+
+ mat = mat * m_startMatrix;
+
+ QPointF xformOriginPoint = target()->transformOriginPoint();
+ QPointF pos = mat * xformOriginPoint;
+ pos -= xformOriginPoint;
+
+ if (xAxis()->enabled())
+ pos.setX(qBound(xAxis()->minimum(), pos.x(), xAxis()->maximum()));
+ else
+ pos.rx() -= qreal(m_activeTranslation.x());
+ if (yAxis()->enabled())
+ pos.setY(qBound(yAxis()->minimum(), pos.y(), yAxis()->maximum()));
+ else
+ pos.ry() -= qreal(m_activeTranslation.y());
+
+ target()->setPosition(pos);
+ target()->setRotation(rotation);
+ target()->setScale(m_accumulatedScale);
+ } else {
+ m_activeTranslation = QVector2D(m_centroid.scenePosition() - m_centroid.scenePressPosition());
+ }
+
+ qCDebug(lcPinchHandler) << "centroid" << m_centroid.scenePressPosition() << "->" << m_centroid.scenePosition()
+ << ", distance" << m_startDistance << "->" << dist
+ << ", startScale" << m_startScale << "->" << m_accumulatedScale
+ << ", activeRotation" << m_activeRotation
+ << ", rotation" << rotation
+ << " from " << event->device()->type();
+
+ emit updated();
+}
+
+/*!
+ \readonly
+ \qmlproperty QtQuick::HandlerPoint QtQuick::PinchHandler::centroid
+
+ A point exactly in the middle of the currently-pressed touch points.
+ The \l target will be rotated around this point.
+*/
+
+/*!
+ \readonly
+ \qmlproperty real QtQuick::PinchHandler::scale
+
+ The scale factor that will automatically be set on the \l target if it is not null.
+ Otherwise, bindings can be used to do arbitrary things with this value.
+ While the pinch gesture is being performed, it is continuously multiplied by
+ \l activeScale; after the gesture ends, it stays the same; and when the next
+ pinch gesture begins, it begins to be multiplied by activeScale again.
+*/
+
+/*!
+ \readonly
+ \qmlproperty real QtQuick::PinchHandler::activeScale
+
+ The scale factor while the pinch gesture is being performed.
+ It is 1.0 when the gesture begins, increases as the touchpoints are spread
+ apart, and decreases as the touchpoints are brought together.
+ If \l target is not null, its \l {Item::scale}{scale} will be automatically
+ multiplied by this value.
+ Otherwise, bindings can be used to do arbitrary things with this value.
+*/
+
+/*!
+ \readonly
+ \qmlproperty real QtQuick::PinchHandler::rotation
+
+ The rotation of the pinch gesture in degrees, with positive values clockwise.
+ It is 0 when the gesture begins. If \l target is not null, this will be
+ automatically applied to its \l {Item::rotation}{rotation}. Otherwise,
+ bindings can be used to do arbitrary things with this value.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QVector2D QtQuick::PinchHandler::translation
+
+ The translation of the gesture \l centroid. It is \c (0, 0) when the
+ gesture begins.
+*/
+
+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..1afc028758
--- /dev/null
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** 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>
+#include "qquickdragaxis_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(qreal scale READ scale NOTIFY updated)
+ Q_PROPERTY(qreal activeScale READ activeScale NOTIFY updated)
+ Q_PROPERTY(qreal rotation READ rotation NOTIFY updated)
+ Q_PROPERTY(QVector2D translation READ translation NOTIFY updated)
+#if QT_DEPRECATED_SINCE(5, 12)
+ Q_PROPERTY(qreal minimumX READ minimumX WRITE setMinimumX NOTIFY minimumXChanged) // ### Qt 6: remove
+ Q_PROPERTY(qreal maximumX READ maximumX WRITE setMaximumX NOTIFY maximumXChanged) // ### Qt 6: remove
+ Q_PROPERTY(qreal minimumY READ minimumY WRITE setMinimumY NOTIFY minimumYChanged) // ### Qt 6: remove
+ Q_PROPERTY(qreal maximumY READ maximumY WRITE setMaximumY NOTIFY maximumYChanged) // ### Qt 6: remove
+#endif
+ Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
+ Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT)
+
+public:
+ explicit QQuickPinchHandler(QQuickItem *parent = nullptr);
+
+ 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);
+
+ QVector2D translation() const { return m_activeTranslation; }
+ qreal scale() const { return m_accumulatedScale; }
+ qreal activeScale() const { return m_activeScale; }
+ qreal rotation() const { return m_activeRotation; }
+
+#if QT_DEPRECATED_SINCE(5, 12)
+ void warnAboutMinMaxDeprecated() const;
+ qreal minimumX() const { warnAboutMinMaxDeprecated(); return m_minimumX; }
+ void setMinimumX(qreal minX);
+ qreal maximumX() const { warnAboutMinMaxDeprecated(); return m_maximumX; }
+ void setMaximumX(qreal maxX);
+ qreal minimumY() const { warnAboutMinMaxDeprecated(); return m_minimumY; }
+ void setMinimumY(qreal minY);
+ qreal maximumY() const { warnAboutMinMaxDeprecated(); return m_maximumY; }
+ void setMaximumY(qreal maxY);
+#endif
+
+ QQuickDragAxis *xAxis() { return &m_xAxis; }
+ QQuickDragAxis *yAxis() { return &m_yAxis; }
+
+signals:
+ void minimumScaleChanged();
+ void maximumScaleChanged();
+ void minimumRotationChanged();
+ void maximumRotationChanged();
+ void minimumXChanged();
+ void maximumXChanged();
+ void minimumYChanged();
+ void maximumYChanged();
+ void updated();
+
+protected:
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ void onActiveChanged() override;
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+
+private:
+ // properties
+ qreal m_activeScale = 1;
+ qreal m_accumulatedScale = 1;
+ qreal m_activeRotation = 0;
+ QVector2D m_activeTranslation = QVector2D(0, 0);
+
+ qreal m_minimumScale = -qInf();
+ qreal m_maximumScale = qInf();
+
+ qreal m_minimumRotation = -qInf();
+ qreal m_maximumRotation = qInf();
+
+ qreal m_minimumX = -qInf();
+ qreal m_maximumX = qInf();
+ qreal m_minimumY = -qInf();
+ qreal m_maximumY = qInf();
+ QQuickDragAxis m_xAxis;
+ QQuickDragAxis m_yAxis;
+
+ // internal
+ qreal m_startScale = 1;
+ qreal m_startRotation = 0;
+ qreal m_startDistance = 0;
+ QPointF m_startPos;
+ qreal m_accumulatedStartCentroidDistance = 0;
+ QVector<PointData> m_startAngles;
+ QQuickMatrix4x4 m_transform;
+ QMatrix4x4 m_startMatrix;
+};
+
+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..096fad2071
--- /dev/null
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** 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_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 QtQuick
+ \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(QQuickItem *parent)
+ : QQuickPointerHandler(*(new QQuickPointerDeviceHandlerPrivate), parent)
+{
+}
+
+QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QQuickPointerDeviceHandlerPrivate &dd, QQuickItem *parent)
+ : QQuickPointerHandler(dd, parent)
+{
+}
+
+QQuickPointerDevice::DeviceTypes QQuickPointerDeviceHandler::acceptedDevices() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedDevices;
+}
+
+QQuickPointerDevice::PointerTypes QQuickPointerDeviceHandler::acceptedPointerTypes() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedPointerTypes;
+}
+
+/*!
+ \qmlproperty flags QtQuick::PointerDeviceHandler::acceptedButtons
+
+ The mouse buttons which can activate this Pointer Handler.
+
+ By default, this property is set to \l {QtQuick::MouseEvent::button} {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}.
+*/
+Qt::MouseButtons QQuickPointerDeviceHandler::acceptedButtons() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedButtons;
+}
+
+void QQuickPointerDeviceHandler::setAcceptedButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedButtons == buttons)
+ return;
+
+ d->acceptedButtons = buttons;
+ emit acceptedButtonsChanged();
+}
+
+Qt::KeyboardModifiers QQuickPointerDeviceHandler::acceptedModifiers() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedModifiers;
+}
+
+/*!
+ \qmlproperty flags PointerDeviceHandler::acceptedDevices
+
+ The types of pointing devices that can activate this Pointer Handler.
+
+ By default, this property is set to
+ \l{QtQuick::PointerDevice::type} {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)
+{
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedDevices == acceptedDevices)
+ return;
+
+ d->acceptedDevices = acceptedDevices;
+ emit acceptedDevicesChanged();
+}
+
+/*!
+ \qmlproperty flags 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 {QtQuick::PointerDevice::pointerType} {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)
+{
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedPointerTypes == acceptedPointerTypes)
+ return;
+
+ d->acceptedPointerTypes = acceptedPointerTypes;
+ emit acceptedPointerTypesChanged();
+}
+
+/*!
+ \qmlproperty flags 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)
+{
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedModifiers == acceptedModifiers)
+ return;
+
+ d->acceptedModifiers = acceptedModifiers;
+ emit acceptedModifiersChanged();
+}
+
+bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ Q_D(QQuickPointerDeviceHandler);
+ if (!QQuickPointerHandler::wantsPointerEvent(event))
+ return false;
+ qCDebug(lcPointerHandlerDispatch) << objectName()
+ << "checking device type" << d->acceptedDevices
+ << "pointer type" << d->acceptedPointerTypes
+ << "modifiers" << d->acceptedModifiers;
+ if ((event->device()->type() & d->acceptedDevices) == 0)
+ return false;
+ if ((event->device()->pointerType() & d->acceptedPointerTypes) == 0)
+ return false;
+ if (d->acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != d->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..82b24369d3
--- /dev/null
+++ b/src/quick/handlers/qquickpointerdevicehandler_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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"
+
+#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.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPointerDeviceHandlerPrivate;
+
+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::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
+ Q_PROPERTY(Qt::KeyboardModifiers acceptedModifiers READ acceptedModifiers WRITE setAcceptedModifiers NOTIFY acceptedModifiersChanged)
+
+public:
+ explicit QQuickPointerDeviceHandler(QQuickItem *parent = nullptr);
+
+ QQuickPointerDevice::DeviceTypes acceptedDevices() const;
+ QQuickPointerDevice::PointerTypes acceptedPointerTypes() const;
+ Qt::MouseButtons acceptedButtons() const;
+ Qt::KeyboardModifiers acceptedModifiers() const;
+
+public Q_SLOTS:
+ void setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices);
+ void setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes);
+ void setAcceptedButtons(Qt::MouseButtons buttons);
+ void setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers);
+
+Q_SIGNALS:
+ void acceptedDevicesChanged();
+ void acceptedPointerTypesChanged();
+ void acceptedButtonsChanged();
+ void acceptedModifiersChanged();
+
+protected:
+ QQuickPointerDeviceHandler(QQuickPointerDeviceHandlerPrivate &dd, QQuickItem *parent = nullptr);
+
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+
+ Q_DECLARE_PRIVATE(QQuickPointerDeviceHandler)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPointerDeviceHandler)
+
+#endif // QQUICKPOINTERDEVICEHANDLER_H
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p_p.h b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
new file mode 100644
index 0000000000..6a950590f3
--- /dev/null
+++ b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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_P_H
+#define QQUICKPOINTERDEVICEHANDLER_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 "qquickpointerdevicehandler_p.h"
+#include "qquickpointerhandler_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPointerDeviceHandler)
+
+public:
+ static QQuickPointerDeviceHandlerPrivate* get(QQuickPointerDeviceHandler *q) { return q->d_func(); }
+ static const QQuickPointerDeviceHandlerPrivate* get(const QQuickPointerDeviceHandler *q) { return q->d_func(); }
+
+ QQuickPointerDevice::DeviceTypes acceptedDevices = QQuickPointerDevice::AllDevices;
+ QQuickPointerDevice::PointerTypes acceptedPointerTypes = QQuickPointerDevice::AllPointerTypes;
+ Qt::MouseButtons acceptedButtons = Qt::LeftButton;
+ Qt::KeyboardModifiers acceptedModifiers = Qt::KeyboardModifierMask;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOINTERDEVICEHANDLER_P_H
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
new file mode 100644
index 0000000000..12c06aa179
--- /dev/null
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -0,0 +1,550 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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"
+#include "qquickpointerhandler_p_p.h"
+#include <QtQuick/private/qquickitem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch")
+Q_LOGGING_CATEGORY(lcPointerHandlerGrab, "qt.quick.handler.grab")
+Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active")
+
+/*!
+ \qmltype PointerHandler
+ \qmlabstract
+ \since 5.10
+ \preliminary
+ \instantiates QQuickPointerHandler
+ \inqmlmodule QtQuick
+ \brief Abstract handler for pointer events.
+
+ PointerHandler is the base class Input Handler (not registered as a QML type) for
+ events from any kind of pointing device (touch, mouse or graphics tablet).
+*/
+
+QQuickPointerHandler::QQuickPointerHandler(QQuickItem *parent)
+ : QObject(*(new QQuickPointerHandlerPrivate), parent)
+{
+}
+
+QQuickPointerHandler::QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QQuickItem *parent)
+ : QObject(dd, parent)
+{
+}
+
+QQuickPointerHandler::~QQuickPointerHandler()
+{
+ QQuickItem *parItem = parentItem();
+ if (parItem) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(parItem);
+ p->extra.value().pointerHandlers.removeOne(this);
+ }
+}
+
+/*!
+ \qmlproperty real PointerHandler::margin
+
+ The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
+ item within which an event 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. Or, if a
+ TapHandler-based button is placed near the screen edge, it can be used
+ to comply with Fitts's Law: react to mouse clicks at the screen edge
+ even though the button is visually spaced away from the edge by a few pixels.
+
+ The default value is 0.
+
+ \image pointerHandlerMargin.png
+*/
+qreal QQuickPointerHandler::margin() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->m_margin;
+}
+
+void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
+{
+ Q_D(QQuickPointerHandler);
+ if (d->m_margin == pointDistanceThreshold)
+ return;
+
+ d->m_margin = pointDistanceThreshold;
+ emit marginChanged();
+}
+
+/*!
+ Notification that the grab has changed in some way which is relevant to this handler.
+ The \a grabber (subject) will be the Input Handler whose state is changing,
+ or null if the state change regards an Item.
+ The \a transition (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 Input Handler 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::GrabTransition transition, QQuickEventPoint *point)
+{
+ qCDebug(lcPointerHandlerGrab) << point << transition << grabber;
+ Q_ASSERT(point);
+ if (grabber == this) {
+ bool wasCanceled = false;
+ switch (transition) {
+ 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()) {
+ Q_D(const QQuickPointerHandler);
+ par->setKeepMouseGrab(d->hadKeepMouseGrab);
+ par->setKeepTouchGrab(d->hadKeepTouchGrab);
+ }
+ break;
+ 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);
+ emit grabChanged(transition, point);
+ }
+}
+
+/*!
+ Acquire or give up a passive grab of the given \a point, according to the \a grab state.
+
+ Unlike the exclusive grab, multiple Input Handlers 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 an
+ Input Handler 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(lcPointerHandlerGrab) << point << grab;
+ if (grab) {
+ point->setGrabberPointerHandler(this, false);
+ } else {
+ point->removePassiveGrabber(this);
+ }
+}
+
+/*!
+ Check whether it's OK to take an exclusive grab of the \a point.
+
+ The default implementation will call approveGrabTransition() to check this
+ handler's \l grabPermissions. If grabbing can be done only by taking over
+ the exclusive grab from an Item, approveGrabTransition() checks the Item's
+ \l keepMouseGrab or \l keepTouchGrab flags appropriately. If grabbing can
+ be done only by taking over another handler's exclusive grab, canGrab()
+ also calls approveGrabTransition() on the handler which is about to lose
+ its grab. Either one can deny the takeover.
+*/
+bool QQuickPointerHandler::canGrab(QQuickEventPoint *point)
+{
+ QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler();
+ return approveGrabTransition(point, this) &&
+ (existingPhGrabber ? existingPhGrabber->approveGrabTransition(point, this) : true);
+}
+
+/*!
+ Check this handler's rules to see if \l proposedGrabber will be allowed to take
+ the exclusive grab. This function may be called twice: once on the instance which
+ will take the grab, and once on the instance which would thereby lose its grab,
+ in case of a takeover scenario.
+*/
+bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObject *proposedGrabber)
+{
+ Q_D(const QQuickPointerHandler);
+ bool allowed = false;
+ if (proposedGrabber == this) {
+ QObject* existingGrabber = point->exclusiveGrabber();
+ allowed = (existingGrabber == nullptr) || ((d->grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything);
+ if (existingGrabber) {
+ if (QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler()) {
+ if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfDifferentType) &&
+ existingPhGrabber->metaObject()->className() != metaObject()->className())
+ allowed = true;
+ if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfSameType) &&
+ existingPhGrabber->metaObject()->className() == metaObject()->className())
+ allowed = true;
+ } else if ((d->grabPermissions & CanTakeOverFromItems)) {
+ QQuickItem * existingItemGrabber = point->grabberItem();
+ if (existingItemGrabber && !((existingItemGrabber->keepMouseGrab() && point->pointerEvent()->asPointerMouseEvent()) ||
+ (existingItemGrabber->keepTouchGrab() && point->pointerEvent()->asPointerTouchEvent())))
+ allowed = true;
+ }
+ }
+ } else {
+ // proposedGrabber is different: that means this instance will lose its grab
+ if (proposedGrabber) {
+ if ((d->grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything)
+ allowed = true;
+ if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) &&
+ proposedGrabber->metaObject()->className() != metaObject()->className())
+ allowed = true;
+ if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfSameType) &&
+ proposedGrabber->metaObject()->className() == metaObject()->className())
+ allowed = true;
+ if (!allowed && (d->grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits("QQuickItem"))
+ allowed = true;
+ } else {
+ if (!allowed && (d->grabPermissions & ApprovesCancellation))
+ allowed = true;
+ }
+ }
+ qCDebug(lcPointerHandlerGrab) << "point" << hex << point->pointId() << "permission" <<
+ QMetaEnum::fromType<GrabPermissions>().valueToKeys(grabPermissions()) <<
+ ':' << this << (allowed ? "approved to" : "denied to") << proposedGrabber;
+ return allowed;
+}
+
+/*!
+ \qmlproperty flags QtQuick::PointerHandler::grabPermissions
+
+ This property specifies the permissions when this handler's logic decides
+ to take over the exclusive grab, or when it is asked to approve grab
+ takeover or cancellation by another handler.
+
+ \value PointerHandler.TakeOverForbidden
+ This handler neither takes from nor gives grab permission to any type of Item or Handler.
+ \value PointerHandler.CanTakeOverFromHandlersOfSameType
+ This handler can take the exclusive grab from another handler of the same class.
+ \value PointerHandler.CanTakeOverFromHandlersOfDifferentType
+ This handler can take the exclusive grab from any kind of handler.
+ \value PointerHandler.CanTakeOverFromAnything
+ This handler can take the exclusive grab from any type of Item or Handler.
+ \value PointerHandler.ApprovesTakeOverByHandlersOfSameType
+ This handler gives permission for another handler of the same class to take the grab.
+ \value PointerHandler.ApprovesTakeOverByHandlersOfDifferentType
+ This handler gives permission for any kind of handler to take the grab.
+ \value PointerHandler.ApprovesTakeOverByItems
+ This handler gives permission for any kind of Item to take the grab.
+ \value PointerHandler.ApprovesCancellation
+ This handler will allow its grab to be set to null.
+ \value PointerHandler.ApprovesTakeOverByAnything
+ This handler gives permission for any any type of Item or Handler to take the grab.
+
+ The default is
+ \c {PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything}
+ which allows most takeover scenarios but avoids e.g. two PinchHandlers fighting
+ over the same touchpoints.
+*/
+QQuickPointerHandler::GrabPermissions QQuickPointerHandler::grabPermissions() const
+{
+ Q_D(const QQuickPointerHandler);
+ return static_cast<QQuickPointerHandler::GrabPermissions>(d->grabPermissions);
+}
+
+void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission)
+{
+ Q_D(QQuickPointerHandler);
+ if (d->grabPermissions == grabPermission)
+ return;
+
+ d->grabPermissions = grabPermission;
+ emit grabPermissionChanged();
+}
+
+void QQuickPointerHandler::classBegin()
+{
+}
+
+void QQuickPointerHandler::componentComplete()
+{
+}
+
+QQuickPointerEvent *QQuickPointerHandler::currentEvent()
+{
+ Q_D(const QQuickPointerHandler);
+ return d->currentEvent;
+}
+
+/*!
+ Acquire or give up the exclusive grab of the given \a point, according to
+ the \a grab state, and subject to the rules: canGrab(), and the rule not to
+ relinquish another handler's grab. Returns true if permission is granted,
+ or if the exclusive grab has already been acquired or relinquished as
+ specified. Returns false if permission is denied either by this handler or
+ by the handler or item from which this handler would take over
+*/
+bool QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab)
+{
+ if ((grab && point->exclusiveGrabber() == this) || (!grab && point->exclusiveGrabber() != this))
+ return true;
+ // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab
+ bool allowed = true;
+ if (grab) {
+ allowed = canGrab(point);
+ } else {
+ QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler();
+ // Ask before allowing one handler to cancel another's grab
+ if (existingPhGrabber && existingPhGrabber != this && !existingPhGrabber->approveGrabTransition(point, nullptr))
+ allowed = false;
+ }
+ qCDebug(lcPointerHandlerGrab) << point << (grab ? "grab" : "ungrab") << (allowed ? "allowed" : "forbidden") <<
+ point->exclusiveGrabber() << "->" << (grab ? this : nullptr);
+ if (allowed)
+ point->setGrabberPointerHandler(grab ? this : nullptr, true);
+ return allowed;
+}
+
+/*!
+ Cancel any existing grab of the given \a point.
+*/
+void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point)
+{
+ qCDebug(lcPointerHandlerGrab) << 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)
+ return false;
+ if (QQuickItem *par = parentItem()) {
+ if (par->window()) {
+ QPoint screenPosition = par->window()->mapToGlobal(point->scenePosition().toPoint());
+ if (!par->window()->geometry().contains(screenPosition))
+ return false;
+ }
+ QPointF p = par->mapFromScene(point->scenePosition());
+ qreal m = margin();
+ if (m > 0)
+ return p.x() >= -m && p.y() >= -m && p.x() <= par->width() + m && p.y() <= par->height() + m;
+ return par->contains(p);
+ }
+ return false;
+}
+
+/*!
+ \qmlproperty bool QtQuick::PointerHandler::enabled
+
+ If a PointerHandler is disabled, it will reject all events
+ and no signals will be emitted.
+*/
+bool QQuickPointerHandler::enabled() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->enabled;
+}
+
+void QQuickPointerHandler::setEnabled(bool enabled)
+{
+ Q_D(QQuickPointerHandler);
+ if (d->enabled == enabled)
+ return;
+
+ d->enabled = enabled;
+ emit enabledChanged();
+}
+
+bool QQuickPointerHandler::active() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->active;
+}
+
+/*!
+ \qmlproperty Item QtQuick::PointerHandler::target
+
+ The Item which this handler will manipulate.
+
+ By default, it is the same as the \l [QML] {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)
+{
+ Q_D(QQuickPointerHandler);
+ d->targetExplicitlySet = true;
+ if (d->target == target)
+ return;
+
+ QQuickItem *oldTarget = d->target;
+ d->target = target;
+ onTargetChanged(oldTarget);
+ emit targetChanged();
+}
+
+QQuickItem *QQuickPointerHandler::parentItem() const
+{
+ return static_cast<QQuickItem *>(QObject::parent());
+}
+
+QQuickItem *QQuickPointerHandler::target() const
+{
+ Q_D(const QQuickPointerHandler);
+ if (!d->targetExplicitlySet)
+ return parentItem();
+ return d->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_D(const QQuickPointerHandler);
+ Q_UNUSED(event)
+ return d->enabled;
+}
+
+bool QQuickPointerHandler::wantsEventPoint(QQuickEventPoint *point)
+{
+ bool ret = point->exclusiveGrabber() == this || point->passiveGrabbers().contains(this) || parentContains(point);
+ qCDebug(lcPointerHandlerDispatch) << hex << point->pointId() << "@" << point->scenePosition()
+ << metaObject()->className() << objectName() << ret;
+ return ret;
+}
+
+/*!
+ \readonly
+ \qmlproperty bool QtQuick::PointerHandler::active
+
+ This holds true whenever this Input Handler 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)
+{
+ Q_D(QQuickPointerHandler);
+ if (d->active != active) {
+ qCDebug(lcPointerHandlerActive) << this << d->active << "->" << active;
+ d->active = active;
+ onActiveChanged();
+ emit activeChanged();
+ }
+}
+
+void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ Q_D(QQuickPointerHandler);
+ d->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 [QML] {target} {target()} is the same, but it
+ can be reassigned.
+
+ \sa {target}, QObject::parent()
+*/
+
+/*!
+ \qmlsignal QtQuick::PointerHandler::grabChanged(GrabTransition transition, EventPoint point)
+
+ This signal is emitted when the grab has changed in some way which is
+ relevant to this handler.
+
+ The \a transition (verb) tells what happened.
+ The \a point (object) is the point that was grabbed or ungrabbed.
+*/
+
+/*!
+ \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.
+*/
+
+QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate()
+ : grabPermissions(QQuickPointerHandler::CanTakeOverFromItems |
+ QQuickPointerHandler::CanTakeOverFromHandlersOfDifferentType |
+ QQuickPointerHandler::ApprovesTakeOverByAnything)
+ , enabled(true)
+ , active(false)
+ , targetExplicitlySet(false)
+ , hadKeepMouseGrab(false)
+ , hadKeepTouchGrab(false)
+{
+}
+
+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..c600e42491
--- /dev/null
+++ b/src/quick/handlers/qquickpointerhandler_p.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch)
+
+class QQuickPointerHandlerPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+
+ 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)
+ Q_PROPERTY(GrabPermissions grabPermissions READ grabPermissions WRITE setGrabPermissions NOTIFY grabPermissionChanged)
+ Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged)
+
+public:
+ explicit QQuickPointerHandler(QQuickItem *parent = nullptr);
+ ~QQuickPointerHandler();
+
+ enum GrabPermission {
+ TakeOverForbidden = 0x0,
+ CanTakeOverFromHandlersOfSameType = 0x01,
+ CanTakeOverFromHandlersOfDifferentType= 0x02,
+ CanTakeOverFromItems = 0x04,
+ CanTakeOverFromAnything = 0x0F,
+ ApprovesTakeOverByHandlersOfSameType = 0x10,
+ ApprovesTakeOverByHandlersOfDifferentType= 0x20,
+ ApprovesTakeOverByItems = 0x40,
+ ApprovesCancellation = 0x80,
+ ApprovesTakeOverByAnything = 0xF0
+ };
+ Q_DECLARE_FLAGS(GrabPermissions, GrabPermission)
+ Q_FLAG(GrabPermissions)
+
+public:
+ bool enabled() const;
+ void setEnabled(bool enabled);
+
+ bool active() const;
+
+ QQuickItem *target() const;
+ void setTarget(QQuickItem *target);
+
+ QQuickItem * parentItem() const;
+
+ void handlePointerEvent(QQuickPointerEvent *event);
+
+ GrabPermissions grabPermissions() const;
+ void setGrabPermissions(GrabPermissions grabPermissions);
+
+ qreal margin() const;
+ void setMargin(qreal pointDistanceThreshold);
+
+Q_SIGNALS:
+ void enabledChanged();
+ void activeChanged();
+ void targetChanged();
+ void marginChanged();
+ void grabChanged(QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point);
+ void grabPermissionChanged();
+ void canceled(QQuickEventPoint *point);
+
+protected:
+ QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QQuickItem *parent);
+
+ void classBegin() override;
+ void componentComplete() override;
+
+ QQuickPointerEvent *currentEvent();
+ virtual bool wantsPointerEvent(QQuickPointerEvent *event);
+ virtual bool wantsEventPoint(QQuickEventPoint *point);
+ virtual void handlePointerEventImpl(QQuickPointerEvent *event);
+ void setActive(bool active);
+ virtual void onTargetChanged(QQuickItem *oldTarget) { Q_UNUSED(oldTarget); }
+ virtual void onActiveChanged() { }
+ virtual void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point);
+ virtual bool canGrab(QQuickEventPoint *point);
+ virtual bool approveGrabTransition(QQuickEventPoint *point, QObject *proposedGrabber);
+ void setPassiveGrab(QQuickEventPoint *point, bool grab = true);
+ bool setExclusiveGrab(QQuickEventPoint *point, bool grab = true);
+ void cancelAllGrabs(QQuickEventPoint *point);
+ QPointF eventPos(const QQuickEventPoint *point) const;
+ bool parentContains(const QQuickEventPoint *point) const;
+
+ friend class QQuickEventPoint;
+ friend class QQuickItemPrivate;
+ friend class QQuickWindowPrivate;
+
+ Q_DECLARE_PRIVATE(QQuickPointerHandler)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerHandler::GrabPermissions)
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPointerHandler)
+
+#endif // QQUICKPOINTERHANDLER_H
diff --git a/src/quick/handlers/qquickpointerhandler_p_p.h b/src/quick/handlers/qquickpointerhandler_p_p.h
new file mode 100644
index 0000000000..2ea4905643
--- /dev/null
+++ b/src/quick/handlers/qquickpointerhandler_p_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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_P_H
+#define QQUICKPOINTERHANDLER_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 "qevent.h"
+
+#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquickpointerhandler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandlerPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPointerHandler)
+
+public:
+ static QQuickPointerHandlerPrivate* get(QQuickPointerHandler *q) { return q->d_func(); }
+ static const QQuickPointerHandlerPrivate* get(const QQuickPointerHandler *q) { return q->d_func(); }
+
+ QQuickPointerHandlerPrivate();
+
+ QQuickPointerEvent *currentEvent = nullptr;
+ QQuickItem *target = nullptr;
+ qreal m_margin = 0;
+ uint8_t grabPermissions : 8;
+ bool enabled : 1;
+ bool active : 1;
+ bool targetExplicitlySet : 1;
+ bool hadKeepMouseGrab : 1; // some handlers override target()->setKeepMouseGrab(); this remembers previous state
+ bool hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOINTERHANDLER_P_H
diff --git a/src/quick/handlers/qquickpointhandler.cpp b/src/quick/handlers/qquickpointhandler.cpp
new file mode 100644
index 0000000000..30f62332ba
--- /dev/null
+++ b/src/quick/handlers/qquickpointhandler.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** 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 "qquickpointhandler_p.h"
+#include <private/qquickwindow_p.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype PointHandler
+ \instantiates QQuickPointHandler
+ \inherits SinglePointHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
+ \brief Handler for reacting to a single touchpoint.
+
+ PointHandler can be used to show feedback about a touchpoint or the mouse
+ position, or to otherwise react to pointer events.
+
+ When a press event occurs, each instance of PointHandler chooses a
+ single point which is not yet "taken" at that moment: if the press
+ occurs within the bounds of the \l {PointerHandler::parent}, and
+ no sibling PointHandler within the same \l {PointerHandler::parent}
+ has yet acquired a passive grab on that point, and if the other
+ constraints such as \l {PointerDeviceHandler::acceptedButtons}{acceptedButtons}, \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices} etc.
+ are satisfied, it's
+ eligible, and the PointHandler then acquires a passive grab. In
+ this way, the \l {PointerHandler::parent} acts like an exclusive
+ group: there can be multiple instances of PointHandler, and the
+ set of pressed touchpoints will be distributed among them. Each
+ PointHandler which has chosen a point to track has its \l active
+ property \c true. It then continues to track its chosen point
+ until release: the properties of the \l point will be kept
+ up-to-date. Any Item can bind to these properties, and thereby
+ follow the point's movements.
+
+ By being only a passive grabber, it has the ability to keep independent
+ oversight of all movements. The passive grab cannot be stolen or overridden
+ even when other gestures are detected and exclusive grabs occur.
+
+ If your goal is orthogonal surveillance of eventpoints, an older
+ alternative was QObject::installEventFilter(), but that has never been a
+ built-in QtQuick feature: it requires some C++ code, such as a QQuickItem
+ subclass. PointHandler is more efficient than that, because only pointer
+ events will be delivered to it, during the course of normal event delivery
+ in QQuickWindow; whereas an event filter needs to filter all QEvents of all
+ types, and thus sets itself up as a potential event delivery bottleneck.
+
+ One possible use case is to add this handler to a transparent Item which is
+ on top of the rest of the scene (by having a high \l{Item::z} {z} value),
+ so that when a point is freshly pressed, it will be delivered to that Item
+ and its handlers first, providing the opportunity to take the passive grab
+ as early as possible. Such an item (like a pane of glass over the whole UI)
+ can be a convenient parent for other Items which visualize the kind of reactive
+ feedback which must always be on top; and likewise it can be the parent for
+ popups, popovers, dialogs and so on. If it will be used in that way, it can
+ be helpful for your main.cpp to use QQmlContext::setContextProperty() to
+ make the "glass pane" accessible by ID to the entire UI, so that other
+ Items and PointHandlers can be reparented to it.
+
+ \snippet pointerHandlers/pointHandler.qml 0
+
+ Like all input handlers, a PointHandler has a \l target property, which
+ may be used as a convenient place to put a point-tracking Item; but
+ PointHandler will not automatically manipulate the \c target item in any way.
+ You need to use bindings to make it react to the \l point.
+
+ \note On macOS, PointHandler does not react to the trackpad by default.
+ That is because macOS can provide either native gesture recognition, or raw
+ touchpoints, but not both. We prefer to use the native gesture event in
+ PinchHandler, so we do not want to disable it by enabling touch. However
+ MultiPointTouchArea does enable touch, thus disabling native gesture
+ recognition within the entire window; so it's an alternative if you only
+ want to react to all the touchpoints but do not require the smooth
+ native-gesture experience.
+
+ \sa MultiPointTouchArea
+*/
+
+QQuickPointHandler::QQuickPointHandler(QQuickItem *parent)
+ : QQuickSinglePointHandler(parent)
+{
+ setIgnoreAdditionalPoints();
+}
+
+bool QQuickPointHandler::wantsEventPoint(QQuickEventPoint *pt)
+{
+ // On press, we want it unless a sibling of the same type also does.
+ if (pt->state() == QQuickEventPoint::Pressed && QQuickSinglePointHandler::wantsEventPoint(pt)) {
+ for (const QQuickPointerHandler *grabber : pt->passiveGrabbers()) {
+ if (grabber && grabber->parent() == parent() &&
+ grabber->metaObject()->className() == metaObject()->className())
+ return false;
+ }
+ return true;
+ }
+ // If we've already been interested in a point, stay interested, even if it has strayed outside bounds.
+ return (pt->state() != QQuickEventPoint::Pressed && point().id() == pt->pointId());
+}
+
+void QQuickPointHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ if (point->pointerEvent()->asPointerTouchEvent() ||
+ (point->pointerEvent()->buttons() & acceptedButtons()) != Qt::NoButton) {
+ setPassiveGrab(point);
+ setActive(true);
+ }
+ break;
+ case QQuickEventPoint::Released:
+ if (point->pointerEvent()->asPointerTouchEvent() ||
+ (point->pointerEvent()->buttons() & acceptedButtons()) == Qt::NoButton)
+ setActive(false);
+ break;
+ default:
+ break;
+ }
+ point->setAccepted(false); // Just lurking... don't interfere with propagation
+ emit translationChanged();
+}
+
+QVector2D QQuickPointHandler::translation() const
+{
+ return QVector2D(point().position() - point().pressPosition());
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointhandler_p.h b/src/quick/handlers/qquickpointhandler_p.h
new file mode 100644
index 0000000000..380ce1f90f
--- /dev/null
+++ b/src/quick/handlers/qquickpointhandler_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** 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 QQUICKPONTHANDLER_H
+#define QQUICKPONTHANDLER_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 QQuickPointHandler : public QQuickSinglePointHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)
+
+public:
+ explicit QQuickPointHandler(QQuickItem *parent = nullptr);
+
+ QVector2D translation() const;
+
+Q_SIGNALS:
+ void translationChanged();
+
+protected:
+ bool wantsEventPoint(QQuickEventPoint *pt) override;
+ void handleEventPoint(QQuickEventPoint *point) override;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPointHandler)
+
+#endif // QQUICKPONTHANDLER_H
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
new file mode 100644
index 0000000000..c0fa39fad3
--- /dev/null
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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"
+#include "qquicksinglepointhandler_p_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
+
+/*!
+ \qmltype SinglePointHandler
+ \qmlabstract
+ \preliminary
+ \instantiates QQuickSinglePointHandler
+ \inherits PointerDeviceHandler
+ \inqmlmodule QtQuick
+ \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(QQuickItem *parent)
+ : QQuickPointerDeviceHandler(*(new QQuickSinglePointHandlerPrivate), parent)
+{
+}
+
+QQuickSinglePointHandler::QQuickSinglePointHandler(QQuickSinglePointHandlerPrivate &dd, QQuickItem *parent)
+ : QQuickPointerDeviceHandler(dd, parent)
+{
+}
+
+bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ Q_D(QQuickSinglePointHandler);
+ if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
+ return false;
+ if (event->device()->pointerType() != QQuickPointerDevice::Finger &&
+ (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0)
+ return false;
+
+ if (d->pointInfo.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;
+ bool missing = true;
+ QQuickEventPoint *point = nullptr;
+ int c = event->pointCount();
+ for (int i = 0; i < c; ++i) {
+ QQuickEventPoint *p = event->point(i);
+ const bool found = (p->pointId() == d->pointInfo.id());
+ if (found)
+ missing = false;
+ if (wantsEventPoint(p)) {
+ ++candidatePointCount;
+ if (found)
+ point = p;
+ }
+ }
+ if (missing)
+ qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << hex << d->pointInfo.id()
+ << "is missing from current event, but was neither canceled nor released";
+ if (point) {
+ if (candidatePointCount == 1 || (candidatePointCount > 1 && d->ignoreAdditionalPoints)) {
+ point->setAccepted();
+ return true;
+ } else {
+ point->cancelAllGrabs(this);
+ }
+ } else {
+ 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 && !chosen; ++i) {
+ QQuickEventPoint *p = event->point(i);
+ if (!p->exclusiveGrabber() && wantsEventPoint(p)) {
+ if (!chosen)
+ chosen = p;
+ ++candidatePointCount;
+ }
+ }
+ if (chosen && candidatePointCount == 1) {
+ setPointId(chosen->pointId());
+ chosen->setAccepted();
+ }
+ }
+ return d->pointInfo.id();
+}
+
+void QQuickSinglePointHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ Q_D(QQuickSinglePointHandler);
+ QQuickPointerDeviceHandler::handlePointerEventImpl(event);
+ QQuickEventPoint *currentPoint = event->pointById(d->pointInfo.id());
+ Q_ASSERT(currentPoint);
+ d->pointInfo.reset(currentPoint);
+ handleEventPoint(currentPoint);
+ if (currentPoint->state() == QQuickEventPoint::Released && (event->buttons() & acceptedButtons()) == Qt::NoButton) {
+ setExclusiveGrab(currentPoint, false);
+ d->reset();
+ }
+ emit pointChanged();
+}
+
+void QQuickSinglePointHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
+{
+ Q_D(QQuickSinglePointHandler);
+ if (grabber != this)
+ return;
+ switch (transition) {
+ case QQuickEventPoint::GrabExclusive:
+ d->pointInfo.m_sceneGrabPosition = point->sceneGrabPosition();
+ setActive(true);
+ QQuickPointerHandler::onGrabChanged(grabber, transition, point);
+ break;
+ case QQuickEventPoint::GrabPassive:
+ d->pointInfo.m_sceneGrabPosition = point->sceneGrabPosition();
+ QQuickPointerHandler::onGrabChanged(grabber, transition, 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, transition, point);
+ d->reset();
+ break;
+ }
+}
+
+void QQuickSinglePointHandler::setIgnoreAdditionalPoints(bool v)
+{
+ Q_D(QQuickSinglePointHandler);
+ d->ignoreAdditionalPoints = v;
+}
+
+void QQuickSinglePointHandler::moveTarget(QPointF pos, QQuickEventPoint *point)
+{
+ Q_D(QQuickSinglePointHandler);
+ target()->setPosition(pos);
+ d->pointInfo.m_scenePosition = point->scenePosition();
+ d->pointInfo.m_position = target()->mapFromScene(d->pointInfo.m_scenePosition);
+}
+
+void QQuickSinglePointHandler::setPointId(int id)
+{
+ Q_D(QQuickSinglePointHandler);
+ d->pointInfo.m_id = id;
+}
+
+QQuickHandlerPoint QQuickSinglePointHandler::point() const
+{
+ Q_D(const QQuickSinglePointHandler);
+ return d->pointInfo;
+}
+
+/*!
+ \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).
+*/
+
+QQuickSinglePointHandlerPrivate::QQuickSinglePointHandlerPrivate()
+ : QQuickPointerDeviceHandlerPrivate()
+{
+}
+
+void QQuickSinglePointHandlerPrivate::reset()
+{
+ Q_Q(QQuickSinglePointHandler);
+ q->setActive(false);
+ pointInfo.reset();
+}
+
+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..edc55aaaf6
--- /dev/null
+++ b/src/quick/handlers/qquicksinglepointhandler_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "qquickhandlerpoint_p.h"
+#include "qquickpointerdevicehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSinglePointHandlerPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointHandler : public QQuickPointerDeviceHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickHandlerPoint point READ point NOTIFY pointChanged)
+
+public:
+ explicit QQuickSinglePointHandler(QQuickItem *parent = nullptr);
+
+ QQuickHandlerPoint point() const;
+
+Q_SIGNALS:
+ void pointChanged();
+
+protected:
+ QQuickSinglePointHandler(QQuickSinglePointHandlerPrivate &dd, QQuickItem *parent);
+
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+ virtual void handleEventPoint(QQuickEventPoint *point) = 0;
+
+ QQuickEventPoint *currentPoint(QQuickPointerEvent *ev);
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
+
+ void setIgnoreAdditionalPoints(bool v = true);
+
+ void moveTarget(QPointF pos, QQuickEventPoint *point);
+
+ void setPointId(int id);
+
+ Q_DECLARE_PRIVATE(QQuickSinglePointHandler)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickSinglePointHandler)
+
+#endif // QQUICKPOINTERSINGLEHANDLER_H
diff --git a/src/quick/handlers/qquicksinglepointhandler_p_p.h b/src/quick/handlers/qquicksinglepointhandler_p_p.h
new file mode 100644
index 0000000000..1e66c25e14
--- /dev/null
+++ b/src/quick/handlers/qquicksinglepointhandler_p_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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_P_H
+#define QQUICKPOINTERSINGLEHANDLER_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 "qquickhandlerpoint_p.h"
+#include "qquickpointerdevicehandler_p_p.h"
+#include "qquicksinglepointhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointHandlerPrivate : public QQuickPointerDeviceHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickSinglePointHandler)
+
+public:
+ static QQuickSinglePointHandlerPrivate* get(QQuickSinglePointHandler *q) { return q->d_func(); }
+ static const QQuickSinglePointHandlerPrivate* get(const QQuickSinglePointHandler *q) { return q->d_func(); }
+
+ QQuickSinglePointHandlerPrivate();
+
+ void reset();
+
+ QQuickHandlerPoint pointInfo;
+ bool ignoreAdditionalPoints = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOINTERSINGLEHANDLER_P_H
+
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
new file mode 100644
index 0000000000..081645da71
--- /dev/null
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qquicksinglepointhandler_p_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 QtQuick
+ \ingroup qtquick-input-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. The default
+ value is DragThreshold, which requires the press and release to be close
+ together in both space and time. In this case, DragHandler is able to
+ function using only a passive grab, and therefore does not interfere with
+ event delivery to any other Items or Input Handlers. So the default
+ gesturePolicy is useful when you want to modify behavior of an existing
+ control or Item by adding a TapHandler with bindings and/or JavaScript
+ callbacks.
+
+ 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. For this use case, set the
+ \l gesturePolicy to \c TapHandler.ReleaseWithinBounds.
+
+ 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(QQuickItem *parent)
+ : QQuickSinglePointHandler(parent)
+{
+ 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;
+ }
+}
+
+static bool dragOverThreshold(const 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 (!point->pointerEvent()->asPointerMouseEvent() &&
+ !point->pointerEvent()->asPointerTouchEvent() &&
+ !point->pointerEvent()->asPointerTabletEvent() )
+ return false;
+ // 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;
+ bool overThreshold = dragOverThreshold(point);
+ if (overThreshold) {
+ m_longPressTimer.stop();
+ m_holdTimer.invalidate();
+ }
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ case QQuickEventPoint::Released:
+ ret = parentContains(point);
+ break;
+ case QQuickEventPoint::Updated:
+ switch (m_gesturePolicy) {
+ case DragThreshold:
+ ret = !overThreshold && parentContains(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 QtQuick::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 QtQuick::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 pressed transitions immediately
+ from true to false, regardless of the time held.
+
+ \value TapHandler.DragThreshold
+ (the default value) 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
+ input handlers (for example \l DragHandler) or event-handling Items
+ (for example QtQuick Controls), because in this case TapHandler
+ will not take the exclusive grab, but merely a passive grab.
+
+ \value TapHandler.WithinBounds
+ If the event point leaves the bounds of the \c parent Item, the tap
+ gesture is canceled. The TapHandler will take the exclusive grab on
+ press, but will release the grab as soon as the boundary constraint
+ is no longer satisfied.
+
+ \value TapHandler.ReleaseWithinBounds
+ 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
+ \c parent Item, a tap gesture is not recognized. This corresponds to
+ typical behavior for button widgets: 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 take the exclusive grab on press and retain
+ it until release in order to detect this gesture.
+*/
+void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
+{
+ if (m_gesturePolicy == gesturePolicy)
+ return;
+
+ m_gesturePolicy = gesturePolicy;
+ emit gesturePolicyChanged();
+}
+
+/*!
+ \qmlproperty bool QtQuick::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(point);
+ emit tapCountChanged();
+ if (m_tapCount == 1)
+ emit singleTapped(point);
+ else if (m_tapCount == 2)
+ emit doubleTapped(point);
+ 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);
+ }
+ if (cancel) {
+ emit canceled(point);
+ setExclusiveGrab(point, false);
+ // In case there is a filtering parent (Flickable), we should not give up the passive grab,
+ // so that it can continue to filter future events.
+ d_func()->reset();
+ emit pointChanged();
+ }
+ }
+}
+
+void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
+{
+ QQuickSinglePointHandler::onGrabChanged(grabber, transition, point);
+ bool isCanceled = transition == QQuickEventPoint::CancelGrabExclusive || transition == 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 QtQuick::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 triple-tap, you can write:
+
+ \qml
+ Rectangle {
+ width: 100; height: 30
+ signal tripleTap
+ TapHandler {
+ acceptedButtons: Qt.AllButtons
+ onTapped: if (tapCount == 3) tripleTap()
+ }
+ }
+ \endqml
+*/
+
+/*!
+ \qmlproperty real QtQuick::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.
+*/
+
+/*!
+ \qmlsignal QtQuick::TapHandler::tapped
+
+ This signal is emitted each time the \c parent Item is tapped.
+
+ That is, if you press and release a touchpoint or button within a time
+ period less than \l longPressThreshold, while any movement does not exceed
+ the drag threshold, then the \c tapped signal will be emitted at the time
+ of release.
+*/
+
+/*!
+ \qmlsignal QtQuick::TapHandler::singleTapped
+ \since 5.11
+
+ This signal is emitted when the \c parent Item is tapped once.
+ After an amount of time greater than QStyleHints::mouseDoubleClickInterval,
+ it can be tapped again; but if the time until the next tap is less,
+ \l tapCount will increase.
+*/
+
+/*!
+ \qmlsignal QtQuick::TapHandler::doubleTapped
+ \since 5.11
+
+ This signal is emitted when the \c parent Item is tapped twice within a
+ short span of time (QStyleHints::mouseDoubleClickInterval) and distance
+ (QPlatformTheme::MouseDoubleClickDistance or
+ QPlatformTheme::TouchDoubleTapDistance). This signal always occurs after
+ \l singleTapped, \l tapped, and \l tapCountChanged.
+*/
+
+/*!
+ \qmlsignal QtQuick::TapHandler::longPressed
+
+ This signal is emitted when the \c parent Item is pressed and held for a
+ time period greater than \l longPressThreshold. That is, if you press and
+ hold a touchpoint or button, while any movement does not exceed the drag
+ threshold, then the \c longPressed signal will be emitted at the time that
+ \l timeHeld exceeds \l longPressThreshold.
+*/
+
+/*!
+ \qmlsignal QtQuick::TapHandler::tapCountChanged
+
+ This signal is emitted when the \c parent Item is tapped once or more (within
+ a specified time and distance span) and when the present \c tapCount differs
+ from the previous \c tapCount.
+*/
+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..6ec5d55227
--- /dev/null
+++ b/src/quick/handlers/qquicktaphandler_p.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** 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(QQuickItem *parent = nullptr);
+
+ 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(QQuickEventPoint *eventPoint);
+ void singleTapped(QQuickEventPoint *eventPoint);
+ void doubleTapped(QQuickEventPoint *eventPoint);
+ void longPressed();
+
+protected:
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, 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:
+ QPointF m_lastTapPos;
+ qreal m_lastTapTimestamp = 0;
+ QElapsedTimer m_holdTimer;
+ QBasicTimer m_longPressTimer;
+ int m_tapCount = 0;
+ int m_longPressThreshold = -1;
+ GesturePolicy m_gesturePolicy = GesturePolicy::DragThreshold;
+ bool m_pressed = false;
+
+ static qreal m_multiTapInterval;
+ static int m_mouseMultiClickDistanceSquared;
+ static int m_touchMultiTapDistanceSquared;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTapHandler)
+
+#endif // QQUICKTAPHANDLER_H