diff options
Diffstat (limited to 'src/quick')
23 files changed, 2260 insertions, 81 deletions
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri new file mode 100644 index 0000000000..f32f330d4e --- /dev/null +++ b/src/quick/handlers/handlers.pri @@ -0,0 +1,19 @@ +HEADERS += \ + $$PWD/qquickdraghandler_p.h \ + $$PWD/qquickpointerhandler_p.h \ + $$PWD/qquickpointerdevicehandler_p.h \ + $$PWD/qquickmultipointerhandler_p.h \ + $$PWD/qquickpinchhandler_p.h \ + $$PWD/qquickpointersinglehandler_p.h \ + $$PWD/qquickhandlersmodule_p.h \ + $$PWD/qquickpointerdevicehandler_p.h \ + $$PWD/qquickhandlersmodule_p.h \ + +SOURCES += \ + $$PWD/qquickdraghandler.cpp \ + $$PWD/qquickpointerhandler.cpp \ + $$PWD/qquickpointerdevicehandler.cpp \ + $$PWD/qquickmultipointerhandler.cpp \ + $$PWD/qquickpinchhandler.cpp \ + $$PWD/qquickpointersinglehandler.cpp \ + $$PWD/qquickhandlersmodule.cpp \ diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp new file mode 100644 index 0000000000..dadf8bba04 --- /dev/null +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -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$ +** +****************************************************************************/ + +#include "qquickdraghandler_p.h" +#include <private/qquickwindow_p.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype DragHandler + \instantiates QQuickDragHandler + \inqmlmodule QtQuick + \ingroup qtquick-handlers + \brief Handler for dragging + + DragHandler is a handler that is used to interactively move an Item. + + At this time, drag-and-drop is not yet supported. + + \sa Drag, MouseArea +*/ + +QQuickDragHandler::QQuickDragHandler(QObject *parent) + : QQuickPointerSingleHandler(parent) + , m_dragging(false) +{ +} + +QQuickDragHandler::~QQuickDragHandler() +{ +} + +bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point) +{ + // If we've already been interested in a point, stay interested, even if it has strayed outside bounds. + return ((point->state() != QQuickEventPoint::Pressed && currentPointId() == point->pointId()) + || QQuickPointerSingleHandler::wantsEventPoint(point)); +} + +void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) +{ + // If there's no target or the target has no parent, we shouldn't be dragging + if (!target() || !target()->parentItem()) + return; + point->setAccepted(); + switch (point->state()) { + case QQuickEventPoint::Pressed: + m_startPos = target()->mapToScene(QPointF()); + break; + case QQuickEventPoint::Updated: { + QPointF delta = point->scenePos() - point->scenePressPos(); + if (!m_xAxis.enabled()) + delta.setX(0); + if (!m_yAxis.enabled()) + delta.setY(0); + if (m_dragging) { + QPointF pos = target()->parentItem()->mapFromScene(m_startPos) + delta; + enforceAxisConstraints(&pos); + target()->setPosition(pos); + } else if ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || + (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point))) { + m_dragging = true; + setGrab(point, true); + emit draggingChanged(); + } + } break; + case QQuickEventPoint::Released: + if (m_dragging) { + m_dragging = false; + emit draggingChanged(); + } + break; + default: + break; + } +} + +void QQuickDragHandler::enforceConstraints() +{ + if (!target() || !target()->parentItem()) + return; + QPointF pos = target()->position(); + QPointF copy(pos); + enforceAxisConstraints(&pos); + if (pos != copy) + target()->setPosition(pos); +} + +void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos) +{ + if (m_xAxis.enabled()) + localPos->setX(qBound(m_xAxis.minimum(), localPos->x(), m_xAxis.maximum())); + if (m_yAxis.enabled()) + localPos->setY(qBound(m_yAxis.minimum(), localPos->y(), m_yAxis.maximum())); +} + +QQuickDragAxis::QQuickDragAxis() + : m_minimum(-DBL_MAX) + , m_maximum(DBL_MAX) + , m_enabled(true) +{ +} + +void QQuickDragAxis::setMinimum(qreal minimum) +{ + if (m_minimum == minimum) + return; + + m_minimum = minimum; + emit minimumChanged(); +} + +void QQuickDragAxis::setMaximum(qreal maximum) +{ + if (m_maximum == maximum) + return; + + m_maximum = maximum; + emit maximumChanged(); +} + +void QQuickDragAxis::setEnabled(bool enabled) +{ + if (m_enabled == enabled) + return; + + m_enabled = enabled; + emit enabledChanged(); +} + +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..e7cb675025 --- /dev/null +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** 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 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 "qquickpointersinglehandler_p.h" + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged) + Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + +public: + QQuickDragAxis(); + + qreal minimum() const { return m_minimum; } + void setMinimum(qreal minimum); + + qreal maximum() const { return m_maximum; } + void setMaximum(qreal maximum); + + bool enabled() const { return m_enabled; } + void setEnabled(bool enabled); + +signals: + void minimumChanged(); + void maximumChanged(); + void enabledChanged(); + +private: + qreal m_minimum; + qreal m_maximum; + bool m_enabled; +}; + +class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickPointerSingleHandler +{ + Q_OBJECT + Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT) + Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT) + Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) + +public: + QQuickDragHandler(QObject *parent = 0); + ~QQuickDragHandler(); + + void handleEventPoint(QQuickEventPoint *point) override; + + QQuickDragAxis *xAxis() { return &m_xAxis; } + QQuickDragAxis *yAxis() { return &m_yAxis; } + + bool dragging() const { return m_dragging; } + + Q_INVOKABLE void enforceConstraints(); + +Q_SIGNALS: +// void gestureStarted(QQuickGestureEvent *gesture); + void draggingChanged(); + +protected: + bool wantsEventPoint(QQuickEventPoint *point) override; + +private: + void ungrab(); + void enforceAxisConstraints(QPointF *localPos); + +private: + bool m_dragging; + QPointF m_startPos; + QQuickDragAxis m_xAxis; + QQuickDragAxis m_yAxis; + + friend class QQuickDragAxis; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickDragHandler) +QML_DECLARE_TYPE(QQuickDragAxis) + +#endif // QQUICKDRAGHANDLER_H diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp new file mode 100644 index 0000000000..97d6f1d499 --- /dev/null +++ b/src/quick/handlers/qquickhandlersmodule.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickhandlersmodule_p.h" +#include "qquickpointerhandler_p.h" +#include "qquickdraghandler_p.h" +#include "qquickpinchhandler_p.h" + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_Qt_labs_handlers); +#endif +} + +QT_BEGIN_NAMESPACE + +static QQmlPrivate::AutoParentResult handler_autoParent(QObject *obj, QObject *parent) +{ + if (QQuickItem *parentItem = qmlobject_cast<QQuickItem *>(parent)) { + QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj); + if (handler && !handler->target()) { + handler->setTarget(parentItem); + return QQmlPrivate::Parented; + } + } + return QQmlPrivate::IncompatibleObject; +} + +static void qt_quickhandlers_defineModule(const char *uri, int major, int minor) +{ + QQmlPrivate::RegisterAutoParent autoparent = { 0, &handler_autoParent }; + QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent); + qmlRegisterUncreatableType<QQuickPointerEvent>(uri, major, minor, "PointerEvent", + QQuickPointerHandler::tr("PointerEvent is only available as a parameter of several signals in PointerHandler")); + qmlRegisterUncreatableType<QQuickPointerDevice>(uri, major, minor, "PointerDevice", + QQuickPointerHandler::tr("PointerDevice is only available as a property of PointerEvent")); + qRegisterMetaType<QPointerUniqueId>("QPointerUniqueId"); + qmlRegisterUncreatableType<QPointerUniqueId>(uri, major, minor, "PointerUniqueId", + QQuickPointerHandler::tr("PointerUniqueId is only available as a property of PointerEvent")); + + qmlRegisterType<QQuickPointerHandler>(uri,major,minor,"PointerHandler"); + qmlRegisterType<QQuickDragHandler>(uri,major,minor,"DragHandler"); + qmlRegisterUncreatableType<QQuickDragAxis>(uri, major, minor, "DragAxis", + QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler")); + qmlRegisterType<QQuickPinchHandler>(uri,major,minor,"PinchHandler"); +} + +void QQuickHandlersModule::defineModule() +{ + initResources(); + + const char uri[] = "Qt.labs.handlers"; + int majorVersion = 1; + int minorVersion = 0; + + qt_quickhandlers_defineModule(uri, majorVersion, minorVersion); +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickhandlersmodule_p.h b/src/quick/handlers/qquickhandlersmodule_p.h new file mode 100644 index 0000000000..7eb8d39b98 --- /dev/null +++ b/src/quick/handlers/qquickhandlersmodule_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKHANDLERSMODULE_P_H +#define QQUICKHANDLERSMODULE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qqml.h> +#include <private/qtquickglobal_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QQuickHandlersModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +#endif // QQUICKHANDLERSMODULE_P_H + diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp new file mode 100644 index 0000000000..746d33a12c --- /dev/null +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** 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 "qquickmultipointerhandler_p.h" +#include <private/qquickitem_p.h> +#include <QLineF> +#include <QMouseEvent> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + An intermediate class (not registered as a QML type) + for the type of handler which requires and acts upon a specific number + of multiple touchpoints. +*/ +QQuickMultiPointerHandler::QQuickMultiPointerHandler(QObject *parent, int requiredPointCount) + : QQuickPointerDeviceHandler(parent) + , m_requiredPointCount(requiredPointCount) + , m_pointDistanceThreshold(0) +{ +} + +QQuickMultiPointerHandler::~QQuickMultiPointerHandler() +{ +} + +bool QQuickMultiPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) +{ + if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) + return false; + if (event->pointCount() < m_requiredPointCount) + return false; + if (sameAsCurrentPoints(event)) + return true; + + const QVector<QQuickEventPoint *> candidatePoints = pointsInsideOrNearTarget(event); + const bool ret = (candidatePoints.size() == m_requiredPointCount); + if (ret) + m_currentPoints = candidatePoints; + return ret; +} + +QVector<QQuickEventPoint *> QQuickMultiPointerHandler::pointsInsideOrNearTarget(QQuickPointerEvent *event) +{ + QVector<QQuickEventPoint *> ret; + int c = event->pointCount(); + QRectF targetBounds = target()->mapRectToScene(target()->boundingRect()) + .marginsAdded(QMarginsF(m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold)); + for (int i = 0; i < c; ++i) { + QQuickEventPoint *p = event->point(i); + if (targetBounds.contains(p->scenePos())) + ret << p; + } + return ret; +} + +void QQuickMultiPointerHandler::setRequiredPointCount(int c) +{ + if (m_requiredPointCount == c) + return; + + m_requiredPointCount = c; + emit requiredPointCountChanged(); +} + +void QQuickMultiPointerHandler::setPointDistanceThreshold(qreal pointDistanceThreshold) +{ + if (m_pointDistanceThreshold == pointDistanceThreshold) + return; + + m_pointDistanceThreshold = pointDistanceThreshold; + emit pointDistanceThresholdChanged(); +} + +bool QQuickMultiPointerHandler::sameAsCurrentPoints(QQuickPointerEvent *event) +{ + bool ret = true; + int c = event->pointCount(); + if (c != m_currentPoints.size()) + return false; + // TODO optimize: either ensure the points are sorted, + // or use std::equal with a predicate + for (int i = 0; ret && i < c; ++i) { + bool found = false; + quint64 pointId = event->point(i)->pointId(); + for (QQuickEventPoint *o : qAsConst(m_currentPoints)) + if (o && pointId == o->pointId()) + found = true; + if (!found) + ret = false; + } + return ret; +} + +// TODO make templates for these functions somehow? +QPointF QQuickMultiPointerHandler::touchPointCentroid() +{ + QPointF ret; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += point->scenePos(); + return ret / m_currentPoints.size(); +} + +qreal QQuickMultiPointerHandler::averageTouchPointDistance(const QPointF &ref) +{ + qreal ret = 0; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += QVector2D(point->scenePos() - ref).length(); + return ret / m_currentPoints.size(); +} + +qreal QQuickMultiPointerHandler::averageStartingDistance(const QPointF &ref) +{ + // TODO cache it in setActive()? + qreal ret = 0; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += QVector2D(point->sceneGrabPos() - ref).length(); + return ret / m_currentPoints.size(); +} + +QVector<QQuickMultiPointerHandler::PointData> QQuickMultiPointerHandler::angles(const QPointF &ref) const +{ + QVector<PointData> angles; + angles.reserve(m_currentPoints.count()); + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { + qreal angle = QLineF(ref, point->scenePos()).angle(); + angles.append(PointData(point->pointId(), angle)); + } + return angles; +} + +qreal QQuickMultiPointerHandler::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 QQuickMultiPointerHandler::acceptPoints(const QVector<QQuickEventPoint *> &points) +{ + for (QQuickEventPoint* point : points) + point->setAccepted(); +} + +void QQuickMultiPointerHandler::grabPoints(QVector<QQuickEventPoint *> points) +{ + for (QQuickEventPoint* point : points) + point->setGrabberPointerHandler(this); +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h new file mode 100644 index 0000000000..2a23a14f8a --- /dev/null +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPOINTERMULTIHANDLER_H +#define QQUICKPOINTERMULTIHANDLER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem.h" +#include "qevent.h" +#include "qquickpointerdevicehandler_p.h" +#include "../items/qquickmultipointtoucharea_p.h" + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQuickMultiPointerHandler : public QQuickPointerDeviceHandler +{ + Q_OBJECT + Q_PROPERTY(int requiredPointCount READ requiredPointCount WRITE setRequiredPointCount NOTIFY requiredPointCountChanged) + Q_PROPERTY(qreal pointDistanceThreshold READ pointDistanceThreshold WRITE setPointDistanceThreshold NOTIFY pointDistanceThresholdChanged) + +public: + QQuickMultiPointerHandler(QObject *parent = 0, int requiredPointCount = 2); + ~QQuickMultiPointerHandler(); + + int requiredPointCount() const { return m_requiredPointCount; } + void setRequiredPointCount(int c); + + qreal pointDistanceThreshold() const { return m_pointDistanceThreshold; } + void setPointDistanceThreshold(qreal pointDistanceThreshold); + +signals: + void requiredPointCountChanged(); + void pointDistanceThresholdChanged(); + +protected: + struct PointData { + PointData() : id(0), angle(0) {} + PointData(quint64 id, qreal angle) : id(id), angle(angle) {} + quint64 id; + qreal angle; + }; + + bool wantsPointerEvent(QQuickPointerEvent *event) override; + bool sameAsCurrentPoints(QQuickPointerEvent *event); + QVector<QQuickEventPoint *> pointsInsideOrNearTarget(QQuickPointerEvent *event); + QPointF touchPointCentroid(); + 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); + void grabPoints(QVector<QQuickEventPoint *> points); + +protected: + QVector<QQuickEventPoint *> m_currentPoints; + int m_requiredPointCount; + qreal m_pointDistanceThreshold; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickMultiPointerHandler) + +#endif // QQUICKPOINTERMULTIHANDLER_H diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp new file mode 100644 index 0000000000..8ee8e2c6b6 --- /dev/null +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpinchhandler_p.h" +#include <QtQuick/qquickwindow.h> +#include <private/qsgadaptationlayer_p.h> +#include <private/qquickitem_p.h> +#include <private/qguiapplication_p.h> +#include <private/qquickwindow_p.h> +#include <QEvent> +#include <QMouseEvent> +#include <QDebug> +#include <qpa/qplatformnativeinterface.h> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch") + +/*! + \qmltype PinchHandler + \instantiates QQuickPinchHandler + \inqmlmodule QtQuick + \ingroup qtquick-handlers + \brief Handler for pinch gestures + + PinchHandler is a handler that is used to interactively rotate and zoom an Item. +*/ + +QQuickPinchHandler::QQuickPinchHandler(QObject *parent) + : QQuickMultiPointerHandler(parent, 2) + , m_scale(1) + , m_rotation(0) + , m_translation(0,0) + , m_minimumScale(-qInf()) + , m_maximumScale(qInf()) + , m_minimumRotation(-qInf()) + , m_maximumRotation(qInf()) + , m_minimumX(-qInf()) + , m_maximumX(qInf()) + , m_minimumY(-qInf()) + , m_maximumY(qInf()) + , m_pinchOrigin(PinchCenter) + , m_startScale(1) + , m_startRotation(0) +{ + connect(this, &QQuickPinchHandler::targetChanged, this, &QQuickPinchHandler::onTargetChanged); +} + +QQuickPinchHandler::~QQuickPinchHandler() +{ +} + +void QQuickPinchHandler::setMinimumScale(qreal minimumScale) +{ + if (m_minimumScale == minimumScale) + return; + + m_minimumScale = minimumScale; + emit minimumScaleChanged(); +} + +void QQuickPinchHandler::setMaximumScale(qreal maximumScale) +{ + if (m_maximumScale == maximumScale) + return; + + m_maximumScale = maximumScale; + emit maximumScaleChanged(); +} + +void QQuickPinchHandler::setMinimumRotation(qreal minimumRotation) +{ + if (m_minimumRotation == minimumRotation) + return; + + m_minimumRotation = minimumRotation; + emit minimumRotationChanged(); +} + +void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation) +{ + if (m_maximumRotation == maximumRotation) + return; + + m_maximumRotation = maximumRotation; + emit maximumRotationChanged(); +} + +void QQuickPinchHandler::setPinchOrigin(QQuickPinchHandler::PinchOrigin pinchOrigin) +{ + if (m_pinchOrigin == pinchOrigin) + return; + + m_pinchOrigin = pinchOrigin; + emit pinchOriginChanged(); +} + +/*! + \qmlproperty QQuickPinchHandler::minimumX + + The minimum acceptable x coordinate of the centroid + */ +void QQuickPinchHandler::setMinimumX(qreal minX) +{ + if (m_minimumX == minX) + return; + m_minimumX = minX; + emit minimumXChanged(); +} + +/*! + \qmlproperty QQuickPinchHandler::maximumX + + The maximum acceptable x coordinate of the centroid + */ +void QQuickPinchHandler::setMaximumX(qreal maxX) +{ + if (m_maximumX == maxX) + return; + m_maximumX = maxX; + emit maximumXChanged(); +} + +/*! + \qmlproperty QQuickPinchHandler::minimumY + + The minimum acceptable y coordinate of the centroid + */ +void QQuickPinchHandler::setMinimumY(qreal minY) +{ + if (m_minimumY == minY) + return; + m_minimumY = minY; + emit minimumYChanged(); +} + +/*! + \qmlproperty QQuickPinchHandler::maximumY + + The maximum acceptable y coordinate of the centroid + */ +void QQuickPinchHandler::setMaximumY(qreal maxY) +{ + if (m_maximumY == maxY) + return; + m_maximumY = maxY; + emit maximumYChanged(); +} + +/*! + \qmlproperty QQuickPinchHandler::minimumTouchPoints + + The pinch begins when this number of fingers are pressed. + Until then, PinchHandler tracks the positions of any pressed fingers, + but if it's an insufficient number, it does not scale or rotate + its \l target, and the \l active property will remain false. +*/ + +/*! + \qmlproperty QQuickPinchHandler::active +*/ + +void QQuickPinchHandler::onActiveChanged() +{ + if (active()) { + m_startScale = m_scale; // TODO incompatible with independent x/y scaling + m_startRotation = m_rotation; + m_startCentroid = touchPointCentroid(); + m_startAngles = angles(m_startCentroid); + m_startDistance = averageTouchPointDistance(m_startCentroid); + m_activeRotation = 0; + m_startMatrix = m_transform.matrix(); + qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; + grabPoints(m_currentPoints); + } else { + qCInfo(lcPinchHandler) << "deactivated with scale" << m_scale << "rotation" << m_rotation; + } +} + +void QQuickPinchHandler::onTargetChanged() +{ + if (target()) { + // TODO if m_target was previously set differently, + // does prepending to the new target remove it from the old one? + // If not, should we fix that there, or here? + m_transform.prependToItem(target()); + } +} + +void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) +{ + Q_UNUSED(event) + + if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) { + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPos() << "->" << point->scenePos(); + } + + // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter + m_centroid = touchPointCentroid(); + QRectF bounds(m_minimumX, m_minimumY, m_maximumX, m_maximumY); + // 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. + QPointF centroidLocalPos; + if (target() && target()->parentItem()) { + centroidLocalPos = target()->parentItem()->mapFromScene(m_centroid); + centroidLocalPos = QPointF(qBound(bounds.left(), centroidLocalPos.x(), bounds.right()), + qBound(bounds.top(), centroidLocalPos.y(), bounds.bottom())); + } + // 1. scale + qreal dist = averageTouchPointDistance(m_centroid); + qreal activeScale = dist / m_startDistance; + activeScale = qBound(m_minimumScale/m_startScale, activeScale, m_maximumScale/m_startScale); + m_scale = m_startScale * activeScale; + + // 2. rotate + QVector<PointData> newAngles = angles(m_centroid); + const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles); + m_activeRotation += angleDelta; + const qreal totalRotation = m_startRotation + m_activeRotation; + m_rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation); + m_activeRotation += (m_rotation - totalRotation); //adjust for the potential bounding above + m_startAngles = std::move(newAngles); + + if (target() && target()->parentItem()) { + // 3. Drag/translate + QPointF activeTranslation(centroidLocalPos - target()->parentItem()->mapFromScene(m_startCentroid)); + + // apply rotation + scaling around the centroid - then apply translation. + QMatrix4x4 mat; + QVector3D xlatOrigin(centroidLocalPos - target()->position()); + mat.translate(xlatOrigin); + mat.rotate(m_activeRotation, 0, 0, -1); + mat.scale(activeScale); + mat.translate(-xlatOrigin); + mat.translate(QVector3D(activeTranslation)); + + // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place + + qCDebug(lcPinchHandler) << "centroid" << m_startCentroid << "->" << m_centroid + << ", distance" << m_startDistance << "->" << dist + << ", startScale" << m_startScale << "->" << m_scale + << ", activeRotation" << m_activeRotation + << ", rotation" << m_rotation; + + mat = mat * m_startMatrix; + m_transform.setMatrix(mat); + m_translation = QPointF(mat.constData()[12], mat.constData()[13]); + } + + acceptPoints(m_currentPoints); + emit updated(); +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h new file mode 100644 index 0000000000..83d6447b15 --- /dev/null +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPINCHHANDLER_H +#define QQUICKPINCHHANDLER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem.h" +#include "qevent.h" +#include "qquickmultipointerhandler_p.h" +#include <private/qquicktranslate_p.h> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointerHandler +{ + Q_OBJECT + Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged) + Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged) + Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged) + Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged) + Q_PROPERTY(PinchOrigin pinchOrigin READ pinchOrigin WRITE setPinchOrigin NOTIFY pinchOriginChanged) + Q_PROPERTY(QPointF centroid READ centroid NOTIFY updated) + Q_PROPERTY(qreal scale READ scale NOTIFY updated) + Q_PROPERTY(qreal rotation READ rotation NOTIFY updated) + Q_PROPERTY(QPointF translation READ translation NOTIFY updated) + Q_PROPERTY(qreal minimumX READ minimumX WRITE setMinimumX NOTIFY minimumXChanged) + Q_PROPERTY(qreal maximumX READ maximumX WRITE setMaximumX NOTIFY maximumXChanged) + Q_PROPERTY(qreal minimumY READ minimumY WRITE setMinimumY NOTIFY minimumYChanged) + Q_PROPERTY(qreal maximumY READ maximumY WRITE setMaximumY NOTIFY maximumYChanged) + +public: + enum PinchOrigin { + FirstPoint, PinchCenter, TargetCenter + }; + Q_ENUM(PinchOrigin) + + QQuickPinchHandler(QObject *parent = 0); + ~QQuickPinchHandler(); + + qreal minimumScale() const { return m_minimumScale; } + void setMinimumScale(qreal minimumScale); + + qreal maximumScale() const { return m_maximumScale; } + void setMaximumScale(qreal maximumScale); + + qreal minimumRotation() const { return m_minimumRotation; } + void setMinimumRotation(qreal minimumRotation); + + qreal maximumRotation() const { return m_maximumRotation; } + void setMaximumRotation(qreal maximumRotation); + + PinchOrigin pinchOrigin() const { return m_pinchOrigin; } + void setPinchOrigin(PinchOrigin pinchOrigin); + + QPointF translation() const { return m_translation; } + qreal scale() const { return m_scale; } + qreal rotation() const { return m_rotation; } + QPointF centroid() const { return m_centroid; } + + qreal minimumX() const { return m_minimumX; } + void setMinimumX(qreal minX); + qreal maximumX() const { return m_maximumX; } + void setMaximumX(qreal maxX); + qreal minimumY() const { return m_minimumY; } + void setMinimumY(qreal minY); + qreal maximumY() const { return m_maximumY; } + void setMaximumY(qreal maxY); + +signals: + void requiredPointCountChanged(); + void minimumScaleChanged(); + void maximumScaleChanged(); + void minimumRotationChanged(); + void maximumRotationChanged(); + void minimumXChanged(); + void maximumXChanged(); + void minimumYChanged(); + void maximumYChanged(); + void pinchOriginChanged(); + void updated(); + +protected: + void onActiveChanged() override; + void onTargetChanged(); + void handlePointerEventImpl(QQuickPointerEvent *event) override; + +private: + // properties + qreal m_scale; + qreal m_rotation; + QPointF m_translation; + QPointF m_centroid; + + qreal m_minimumScale; + qreal m_maximumScale; + + qreal m_minimumRotation; + qreal m_maximumRotation; + + qreal m_minimumX; + qreal m_maximumX; + qreal m_minimumY; + qreal m_maximumY; + + PinchOrigin m_pinchOrigin; + + // internal + qreal m_startScale; + qreal m_startRotation; + qreal m_activeRotation; + QPointF m_startCentroid; + qreal m_startDistance; + + QVector<PointData> m_startAngles; + QMatrix4x4 m_startMatrix; + QQuickMatrix4x4 m_transform; + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPinchHandler) + +#endif // QQUICKPINCHHANDLER_H diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp new file mode 100644 index 0000000000..03704ac09e --- /dev/null +++ b/src/quick/handlers/qquickpointerdevicehandler.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpointerdevicehandler_p.h" +#include <private/qquickitem_p.h> +#include <QMouseEvent> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + An intermediate class (not registered as a QML type) + for handlers which allow filtering based on device type, + pointer type, or device-specific buttons (such as mouse or stylus buttons). + */ +QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QObject *parent) + : QQuickPointerHandler(parent) + , m_acceptedDevices(QQuickPointerDevice::AllDevices) + , m_acceptedPointerTypes(QQuickPointerDevice::AllPointerTypes) + , m_acceptedButtons(Qt::AllButtons) +{ +} + +QQuickPointerDeviceHandler::~QQuickPointerDeviceHandler() +{ +} + +void QQuickPointerDeviceHandler::setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices) +{ + if (m_acceptedDevices == acceptedDevices) + return; + + m_acceptedDevices = acceptedDevices; + emit acceptedDevicesChanged(); +} + +void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes) +{ + if (m_acceptedPointerTypes == acceptedPointerTypes) + return; + + m_acceptedPointerTypes = acceptedPointerTypes; + emit acceptedPointerTypesChanged(); +} + +void QQuickPointerDeviceHandler::setAcceptedButtons(Qt::MouseButtons buttons) +{ + if (m_acceptedButtons == buttons) + return; + + m_acceptedButtons = buttons; + emit acceptedButtonsChanged(); +} + +bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event) +{ + if (!QQuickPointerHandler::wantsPointerEvent(event)) + return false; + qCDebug(lcPointerHandlerDispatch) << objectName() + << "checking device type" << m_acceptedDevices + << "pointer type" << m_acceptedPointerTypes + << "buttons" << m_acceptedButtons; + if ((event->device()->type() & m_acceptedDevices) == 0) + return false; + if ((event->device()->pointerType() & m_acceptedPointerTypes) == 0) + return false; + if (event->device()->pointerType() != QQuickPointerDevice::Finger && + (event->buttons() & m_acceptedButtons) == 0 && (event->button() & m_acceptedButtons) == 0) + 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..bb12d3ccdb --- /dev/null +++ b/src/quick/handlers/qquickpointerdevicehandler_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPOINTERDEVICEHANDLER_H +#define QQUICKPOINTERDEVICEHANDLER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpointerhandler_p.h" + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler +{ + Q_OBJECT + Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged) + Q_PROPERTY(QQuickPointerDevice::PointerTypes acceptedPointerTypes READ acceptedPointerTypes WRITE setAcceptedPointerTypes NOTIFY acceptedPointerTypesChanged) + Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) + +public: + QQuickPointerDeviceHandler(QObject *parent = 0); + ~QQuickPointerDeviceHandler(); + + QQuickPointerDevice::DeviceTypes acceptedDevices() const { return m_acceptedDevices; } + QQuickPointerDevice::PointerTypes acceptedPointerTypes() const { return m_acceptedPointerTypes; } + Qt::MouseButtons acceptedButtons() const { return m_acceptedButtons; } + +public slots: + void setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices); + void setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes); + void setAcceptedButtons(Qt::MouseButtons buttons); + +Q_SIGNALS: + void acceptedDevicesChanged(); + void acceptedPointerTypesChanged(); + void acceptedButtonsChanged(); + +protected: + bool wantsPointerEvent(QQuickPointerEvent *event) override; + void setPressed(bool pressed); + +protected: + QQuickPointerDevice::DeviceTypes m_acceptedDevices; + QQuickPointerDevice::PointerTypes m_acceptedPointerTypes; + Qt::MouseButtons m_acceptedButtons; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPointerDeviceHandler) + +#endif // QQUICKPOINTERDEVICEHANDLER_H diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp new file mode 100644 index 0000000000..6225d2a24a --- /dev/null +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** 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 "qquickpointerhandler_p.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch") + +/*! + \qmltype PointerHandler + \instantiates QQuickPointerHandler + \inqmlmodule QtQuick + \ingroup qtquick-handlers + \brief Handler for pointer events + + PointerHandler is a handler for pointer events regardless of source. + They may represent events from a touch, mouse or tablet device. +*/ + +QQuickPointerHandler::QQuickPointerHandler(QObject *parent) + : QObject(parent) + , m_currentEvent(nullptr) + , m_target(nullptr) + , m_enabled(true) + , m_active(false) +{ +} + +void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) +{ + if (grab) { + QQuickPointerHandler *oldGrabber = point->grabberPointerHandler(); + if (oldGrabber && oldGrabber != this) + oldGrabber->handleGrabCancel(point); + point->setGrabberPointerHandler(this); + onGrabChanged(point); + emit grabChanged(point); + } else if (point->grabberPointerHandler() == this) { + point->setGrabberPointerHandler(nullptr); + onGrabChanged(point); + emit grabChanged(point); + } +} + +QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const +{ + return (m_target ? m_target->mapFromScene(point->scenePos()) : point->scenePos()); +} + +bool QQuickPointerHandler::targetContains(const QQuickEventPoint *point) const +{ + if (!m_target || !point) + return false; + return m_target->contains(m_target->mapFromScene(point->scenePos())); +} + +/*! + \qmlproperty QQuickPointerHandler::enabled + + If a PointerHandler is disabled, it will reject all events + and no signals will be emitted. + + TODO is it too extreme not even to emit pressed/updated/released? + or should we disable only the higher-level interpretation, in subclasses? +*/ +void QQuickPointerHandler::setEnabled(bool enabled) +{ + if (m_enabled == enabled) + return; + + m_enabled = enabled; + emit enabledChanged(); +} + +void QQuickPointerHandler::setTarget(QQuickItem *target) +{ + if (m_target == target) + return; + + if (m_target) { + QQuickItemPrivate *p = QQuickItemPrivate::get(m_target); + p->extra.value().pointerHandlers.removeOne(this); + } + + m_target = target; + if (m_target) { + QQuickItemPrivate *p = QQuickItemPrivate::get(m_target); + p->extra.value().pointerHandlers.append(this); + // Accept all buttons, and leave filtering to pointerEvent() and/or user JS, + // because there can be multiple handlers... + m_target->setAcceptedMouseButtons(Qt::AllButtons); + } + emit targetChanged(); +} + +void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) +{ + const bool wants = wantsPointerEvent(event); + setActive(wants); + if (wants) + handlePointerEventImpl(event); + else + setActive(false); +} + +void QQuickPointerHandler::handleGrabCancel(QQuickEventPoint *point) +{ + qCDebug(lcPointerHandlerDispatch) << point; + Q_ASSERT(point); + setActive(false); + point->setAccepted(false); + emit canceled(point); +} + +bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) +{ + Q_UNUSED(event) + return m_enabled; +} + +void QQuickPointerHandler::setActive(bool active) +{ + if (m_active != active) { + m_active = active; + onActiveChanged(); + emit activeChanged(); + } +} + +void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event) +{ + m_currentEvent = event; +} + +/*! + \qmlproperty QQuickPointerHandler::parent + + The \l Item which is the scope of the handler; the Item in which it was declared. + The handler will handle events on behalf of this Item, which means a + pointer event is relevant if at least one of its event points occurs within + the Item's interior. Initially \l target() is the same, but target() + can be reassigned. + + \sa QQuickPointerHandler::target(), QObject::parent() +*/ + +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..9595897d72 --- /dev/null +++ b/src/quick/handlers/qquickpointerhandler_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 QQUICKPOINTERHANDLER_H +#define QQUICKPOINTERHANDLER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qevent.h" + +#include <QtQuick/private/qquickevents_p_p.h> +#include <QtQuick/private/qquickitem_p.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch) + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + Q_PROPERTY(QQuickItem * target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QQuickItem * parent READ parentItem CONSTANT) + +public: + QQuickPointerHandler(QObject *parent = 0); + virtual ~QQuickPointerHandler() { } + +public: + bool enabled() const { return m_enabled; } + void setEnabled(bool enabled); + + bool active() const { return m_active; } + + QQuickItem *target() const { return m_target; } + void setTarget(QQuickItem *target); + + QQuickItem * parentItem() const { return static_cast<QQuickItem *>(QObject::parent()); } + + void handlePointerEvent(QQuickPointerEvent *event); + +Q_SIGNALS: + void enabledChanged(); + void activeChanged(); + void targetChanged(); + void grabChanged(QQuickEventPoint *point); + void canceled(QQuickEventPoint *point); + +protected: + QQuickPointerEvent *currentEvent() { return m_currentEvent; } + virtual bool wantsPointerEvent(QQuickPointerEvent *event); + virtual void handlePointerEventImpl(QQuickPointerEvent *event); + void setActive(bool active); + virtual void onActiveChanged() { } + virtual void onGrabChanged(QQuickEventPoint *) { } + void setGrab(QQuickEventPoint *point, bool grab); + virtual void handleGrabCancel(QQuickEventPoint *point); + QPointF eventPos(const QQuickEventPoint *point) const; + bool targetContains(const QQuickEventPoint *point) const; + +private: + QQuickPointerEvent *m_currentEvent; + QQuickItem *m_target; + bool m_enabled : 1; + bool m_active : 1; + + friend class QQuickEventPoint; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPointerHandler) + +#endif // QQUICKPOINTERHANDLER_H diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp new file mode 100644 index 0000000000..d97348ea22 --- /dev/null +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpointersinglehandler_p.h" + +QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET) + +/*! + 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. +*/ + +QQuickPointerSingleHandler::QQuickPointerSingleHandler(QObject *parent) + : QQuickPointerDeviceHandler(parent) + , m_currentPointId(0) +{ +} + +bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) +{ + if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) + return false; + if (m_currentPointId) { + // 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. + if (auto point = event->pointById(m_currentPointId)) { + if (wantsEventPoint(point)) { + point->setAccepted(); + return true; + } else if (point->grabber() == this) { + point->cancelGrab(); + } + } else { + qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << m_currentPointId + << "is missing from current event, but was neither canceled nor released"; + return false; + } + } else { + // We have not yet chosen a point; choose the first one for which wantsEventPoint() returns true. + int c = event->pointCount(); + for (int i = 0; i < c && !m_currentPointId; ++i) { + QQuickEventPoint *p = event->point(i); + if (!p->grabber() && wantsEventPoint(p)) { + m_currentPointId = p->pointId(); + p->setAccepted(); + } + } + } + return m_currentPointId; +} + +void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *event) +{ + QQuickPointerDeviceHandler::handlePointerEventImpl(event); + QQuickEventPoint *currentPoint = event->pointById(m_currentPointId); + Q_ASSERT(currentPoint); + if (!m_currentPointId || !currentPoint->isAccepted()) { + m_currentPointId = 0; + setPressedButtons(Qt::NoButton); + } else { + setPressedButtons(event->buttons()); + handleEventPoint(currentPoint); + } + bool grab = currentPoint->isAccepted() && currentPoint->state() != QQuickEventPoint::Released; + setGrab(currentPoint, grab); + if (!grab) + m_currentPointId = 0; +} + +bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point) +{ + return targetContains(point); +} + +void QQuickPointerSingleHandler::handleGrabCancel(QQuickEventPoint *point) +{ + QQuickPointerHandler::handleGrabCancel(point); + m_currentPointId = 0; + setPressedButtons(Qt::NoButton); +} + +void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) +{ + if (buttons != m_pressedButtons) { + m_pressedButtons = buttons; + emit pressedButtonsChanged(); + } +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h new file mode 100644 index 0000000000..69a3263271 --- /dev/null +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPOINTERSINGLEHANDLER_H +#define QQUICKPOINTERSINGLEHANDLER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpointerdevicehandler_p.h" + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerSingleHandler : public QQuickPointerDeviceHandler +{ + Q_OBJECT + Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons NOTIFY pressedButtonsChanged) + +public: + QQuickPointerSingleHandler(QObject *parent = 0); + virtual ~QQuickPointerSingleHandler() { } + + Qt::MouseButtons pressedButtons() const { return m_pressedButtons; } + +signals: + void pressedButtonsChanged(); + +protected: + bool wantsPointerEvent(QQuickPointerEvent *event) override; + virtual bool wantsEventPoint(QQuickEventPoint *point); + void handlePointerEventImpl(QQuickPointerEvent *event) override; + virtual void handleEventPoint(QQuickEventPoint *point) = 0; + quint64 currentPointId() const { return m_currentPointId; } + QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } + void handleGrabCancel(QQuickEventPoint *point) override; + +private: + void setPressedButtons(Qt::MouseButtons buttons); + +private: + quint64 m_currentPointId; + Qt::MouseButtons m_pressedButtons; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPointerSingleHandler) + +#endif // QQUICKPOINTERSINGLEHANDLER_H diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 42588fd058..f8038ed11f 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -40,6 +40,7 @@ #include "qquickevents_p_p.h" #include <QtGui/private/qguiapplication_p.h> #include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickpointerhandler_p.h> #include <QtQuick/private/qquickwindow_p.h> #include <private/qdebug_p.h> @@ -447,7 +448,15 @@ Item { typedef QHash<QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash; Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices) -Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice, +struct ConstructableQQuickPointerDevice : public QQuickPointerDevice +{ + ConstructableQQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, + int maxPoints, int buttonCount, const QString &name, + qint64 uniqueId = 0) + : QQuickPointerDevice(devType, pType, caps, maxPoints, buttonCount, name, uniqueId) {} + +}; +Q_GLOBAL_STATIC_WITH_ARGS(ConstructableQQuickPointerDevice, g_genericMouseDevice, (QQuickPointerDevice::Mouse, QQuickPointerDevice::GenericPointer, QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover, @@ -504,35 +513,108 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) return nullptr; } -void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp) +void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity) { m_scenePos = scenePos; - m_pointId = pointId; + if (m_pointId != pointId) { + if (m_grabber) { + qWarning() << m_grabber << "failed to ungrab previous point" << m_pointId; + cancelGrab(); + } + m_pointId = pointId; + } m_valid = true; m_accept = false; m_state = static_cast<QQuickEventPoint::State>(state); m_timestamp = timestamp; - if (state == Qt::TouchPointPressed) + if (state == Qt::TouchPointPressed) { m_pressTimestamp = timestamp; - // TODO calculate velocity + m_scenePressPos = scenePos; + } + // TODO if Q_LIKELY(velocity.isNull) calculate velocity + m_velocity = velocity; } -QQuickItem *QQuickEventPoint::grabber() const +void QQuickEventPoint::localize(QQuickItem *target) +{ + if (target) + m_pos = target->mapFromScene(scenePos()); + else + m_pos = QPointF(); +} + +void QQuickEventPoint::invalidate() +{ + m_valid = false; + m_pointId = 0; +} + +QObject *QQuickEventPoint::grabber() const { return m_grabber.data(); } -void QQuickEventPoint::setGrabber(QQuickItem *grabber) +void QQuickEventPoint::setGrabber(QObject *grabber) +{ + if (QQuickPointerHandler *phGrabber = qmlobject_cast<QQuickPointerHandler *>(grabber)) + setGrabberPointerHandler(phGrabber); + else + setGrabberItem(static_cast<QQuickItem *>(grabber)); +} + +QQuickItem *QQuickEventPoint::grabberItem() const +{ + return (m_grabberIsHandler ? nullptr : static_cast<QQuickItem *>(m_grabber.data())); +} + +void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) { - if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled()) && m_grabber.data() != grabber) { - auto device = static_cast<const QQuickPointerEvent *>(parent())->device(); - static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); - QString deviceName = (device ? device->name() : QLatin1String("null device")); - deviceName.resize(16, ' '); // shorten, and align in case of sequential output - qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) - << ": grab" << m_grabber << "->" << grabber; + if (grabber != m_grabber.data()) { + if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { + auto device = static_cast<const QQuickPointerEvent *>(parent())->device(); + static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); + QString deviceName = (device ? device->name() : QLatin1String("null device")); + deviceName.resize(16, ' '); // shorten, and align in case of sequential output + qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) + << ": grab" << m_grabber << "->" << grabber; + } + m_grabber = QPointer<QObject>(grabber); + m_grabberIsHandler = false; + m_sceneGrabPos = m_scenePos; + } +} + +QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const +{ + return (m_grabberIsHandler ? static_cast<QQuickPointerHandler *>(m_grabber.data()) : nullptr); +} + +void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber) +{ + if (grabber != m_grabber.data()) { + if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { + auto device = static_cast<const QQuickPointerEvent *>(parent())->device(); + static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); + QString deviceName = (device ? device->name() : QLatin1String("null device")); + deviceName.resize(16, ' '); // shorten, and align in case of sequential output + qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) + << ": grab" << m_grabber << "->" << grabber; + } + m_grabber = QPointer<QObject>(grabber); + m_grabberIsHandler = true; + m_sceneGrabPos = m_scenePos; } - m_grabber = QPointer<QQuickItem>(grabber); +} + +void QQuickEventPoint::cancelGrab() +{ + if (m_grabber.isNull()) { + qWarning("cancelGrab: no grabber"); + return; + } + if (auto handler = grabberPointerHandler()) + handler->handleGrabCancel(this); + m_grabber.clear(); } void QQuickEventPoint::setAccepted(bool accepted) @@ -549,7 +631,7 @@ QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp) { - QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp); + QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp, tp.velocity()); m_rotation = tp.rotation(); m_pressure = tp.pressure(); // m_uniqueId = tp.uniqueId(); @@ -597,10 +679,15 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) default: break; } - m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0 + m_mousePoint->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1 return this; } +void QQuickPointerMouseEvent::localize(QQuickItem *target) +{ + m_mousePoint->localize(target); +} + QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) { auto ev = static_cast<QTouchEvent*>(event); @@ -620,11 +707,11 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) m_touchPoints.insert(i, new QQuickEventTouchPoint(this)); // Make sure the grabbers are right from one event to the next - QVector<QQuickItem*> grabbers; + QVector<QObject*> grabbers; // Copy all grabbers, because the order of points might have changed in the event. // The ID is all that we can rely on (release might remove the first point etc). for (int i = 0; i < newPointCount; ++i) { - QQuickItem *grabber = nullptr; + QObject *grabber = nullptr; if (auto point = pointById(tps.at(i).id())) grabber = point->grabber(); grabbers.append(grabber); @@ -636,7 +723,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) if (point->state() == QQuickEventPoint::Pressed) { if (grabbers.at(i)) qWarning() << "TouchPointPressed without previous release event" << point; - point->setGrabber(nullptr); + point->setGrabberItem(nullptr); } else { point->setGrabber(grabbers.at(i)); } @@ -645,6 +732,12 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) return this; } +void QQuickPointerTouchEvent::localize(QQuickItem *target) +{ + for (auto point : qAsConst(m_touchPoints)) + point->localize(target); +} + QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const { if (i == 0) return m_mousePoint; @@ -659,7 +752,7 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0), - m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false) + m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false), m_grabberIsHandler(false) { Q_UNUSED(m_reserved); } @@ -680,16 +773,16 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) cons return event; } -QVector<QQuickItem *> QQuickPointerMouseEvent::grabbers() const +QVector<QObject *> QQuickPointerMouseEvent::grabbers() const { - QVector<QQuickItem *> result; - if (QQuickItem *grabber = m_mousePoint->grabber()) + QVector<QObject *> result; + if (QObject *grabber = m_mousePoint->grabber()) result << grabber; return result; } void QQuickPointerMouseEvent::clearGrabbers() const { - m_mousePoint->setGrabber(nullptr); + m_mousePoint->setGrabberItem(nullptr); } bool QQuickPointerMouseEvent::isPressEvent() const @@ -707,12 +800,12 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const { return true; } -QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const +QVector<QObject *> QQuickPointerTouchEvent::grabbers() const { - QVector<QQuickItem *> result; + QVector<QObject *> result; for (int i = 0; i < m_pointCount; ++i) { auto point = m_touchPoints.at(i); - if (QQuickItem *grabber = point->grabber()) { + if (QObject *grabber = point->grabber()) { if (!result.contains(grabber)) result << grabber; } diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 6179791413..e8bc307b1a 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -68,6 +68,7 @@ class QQuickPointerEvent; class QQuickPointerMouseEvent; class QQuickPointerTabletEvent; class QQuickPointerTouchEvent; +class QQuickPointerHandler; class QQuickKeyEvent : public QObject { @@ -249,12 +250,17 @@ private: class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT + Q_PROPERTY(QQuickPointerEvent *event READ pointerEvent) + Q_PROPERTY(QPointF pos READ pos) Q_PROPERTY(QPointF scenePos READ scenePos) + Q_PROPERTY(QPointF scenePressPos READ scenePressPos) + Q_PROPERTY(QPointF sceneGrabPos READ sceneGrabPos) + Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(State state READ state) Q_PROPERTY(quint64 pointId READ pointId) Q_PROPERTY(qreal timeHeld READ timeHeld) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) - Q_PROPERTY(QQuickItem *grabber READ grabber WRITE setGrabber) + Q_PROPERTY(QObject *grabber READ grabber WRITE setGrabber) public: enum State { @@ -268,31 +274,49 @@ public: QQuickEventPoint(QQuickPointerEvent *parent); - void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp); + void reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); + void localize(QQuickItem *target); - void invalidate() { m_valid = false; } + void invalidate(); QQuickPointerEvent *pointerEvent() const; + QPointF pos() const { return m_pos; } QPointF scenePos() const { return m_scenePos; } + QPointF scenePressPos() const { return m_scenePressPos; } + QPointF sceneGrabPos() const { return m_sceneGrabPos; } + QVector2D velocity() const { return m_velocity; } State state() const { return m_state; } quint64 pointId() const { return m_pointId; } bool isValid() const { return m_valid; } qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true); - QQuickItem *grabber() const; - void setGrabber(QQuickItem *grabber); + QObject *grabber() const; + void setGrabber(QObject *grabber); + + QQuickItem *grabberItem() const; + void setGrabberItem(QQuickItem *grabber); + + QQuickPointerHandler *grabberPointerHandler() const; + void setGrabberPointerHandler(QQuickPointerHandler *grabber); + + Q_INVOKABLE void cancelGrab(); private: + QPointF m_pos; QPointF m_scenePos; + QPointF m_scenePressPos; + QPointF m_sceneGrabPos; + QVector2D m_velocity; quint64 m_pointId; - QPointer<QQuickItem> m_grabber; + QPointer<QObject> m_grabber; ulong m_timestamp; ulong m_pressTimestamp; State m_state; bool m_valid : 1; bool m_accept : 1; - int m_reserved : 30; + bool m_grabberIsHandler : 1; + int m_reserved : 29; Q_DISABLE_COPY(QQuickEventPoint) }; @@ -349,6 +373,7 @@ public: // property accessors public: // helpers for C++ only (during event delivery) virtual QQuickPointerEvent *reset(QEvent *ev) = 0; + virtual void localize(QQuickItem *target) = 0; virtual bool isPressEvent() const = 0; virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } @@ -366,7 +391,7 @@ public: // helpers for C++ only (during event delivery) virtual int pointCount() const = 0; virtual QQuickEventPoint *point(int i) const = 0; virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; - virtual QVector<QQuickItem *> grabbers() const = 0; + virtual QVector<QObject *> grabbers() const = 0; virtual void clearGrabbers() const = 0; ulong timestamp() const { return m_event->timestamp(); } @@ -388,6 +413,7 @@ public: : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { } QQuickPointerEvent *reset(QEvent *) override; + void localize(QQuickItem *target) override; bool isPressEvent() const override; QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } @@ -395,7 +421,7 @@ public: QQuickEventPoint *point(int i) const override; QQuickEventPoint *pointById(quint64 pointId) const override; bool allPointsAccepted() const override; - QVector<QQuickItem *> grabbers() const override; + QVector<QObject *> grabbers() const override; void clearGrabbers() const override; QMouseEvent *asMouseEvent(const QPointF& localPos) const; @@ -417,6 +443,7 @@ public: { } QQuickPointerEvent *reset(QEvent *) override; + void localize(QQuickItem *target) override; bool isPressEvent() const override; QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; } const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } @@ -425,7 +452,7 @@ public: QQuickEventPoint *pointById(quint64 pointId) const override; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; - QVector<QQuickItem *> grabbers() const override; + QVector<QObject *> grabbers() const override; void clearGrabbers() const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; @@ -496,20 +523,7 @@ public: Q_ENUM(CapabilityFlag) Q_FLAG(Capabilities) - QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) - : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) - , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId), m_event(nullptr) - { - if (m_deviceType == Mouse) { - m_event = new QQuickPointerMouseEvent; - } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) { - m_event = new QQuickPointerTouchEvent; - } else { - Q_ASSERT(false); - } - } - ~QQuickPointerDevice() { delete m_event; } DeviceType type() const { return m_deviceType; } PointerType pointerType() const { return m_pointerType; } Capabilities capabilities() const { return m_capabilities; } @@ -526,6 +540,21 @@ public: static QQuickPointerDevice *tabletDevice(qint64); private: + QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) + : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) + , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId), m_event(nullptr) + { + if (m_deviceType == Mouse) { + m_event = new QQuickPointerMouseEvent; + } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) { + m_event = new QQuickPointerTouchEvent; + } else { + Q_ASSERT(false); + } + } + ~QQuickPointerDevice() { delete m_event; } + +private: DeviceType m_deviceType; PointerType m_pointerType; Capabilities m_capabilities; @@ -537,6 +566,7 @@ private: QQuickPointerEvent *m_event; Q_DISABLE_COPY(QQuickPointerDevice) + friend struct ConstructableQQuickPointerDevice; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 00a1306627..5561f8d976 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -44,6 +44,7 @@ #include "qquickwindow_p.h" #include "qquickevents_p_p.h" +#include <QtQuick/private/qquickpointerhandler_p.h> #include <QtQuick/private/qquicktransition_p.h> #include <private/qqmlglobal_p.h> @@ -1784,6 +1785,8 @@ void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObjec i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem); } else { o->setParent(prop->object); // XXX todo - do we want this? + if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) + pointerHandler->setTarget(static_cast<QQuickItem *>(prop->object)); } } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 504446a8be..97a4e51def 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -67,6 +67,7 @@ #include <QtQuick/private/qquickstate_p.h> #include <private/qquickitem_p.h> #include <QtQuick/private/qquickaccessibleattached_p.h> +#include <QtQuick/private/qquickpointerhandler_p.h> #include <private/qv4engine_p.h> #include <private/qv4object_p.h> @@ -86,6 +87,7 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(DBG_MOUSE_TARGET) Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) +Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch) void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1) { @@ -3219,7 +3221,9 @@ void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o) } else { if (o->inherits("QGraphicsItem")) qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className()); - else { + else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) { + pointerHandler->setTarget(that); + } else { QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o); QQuickItem *item = that; QQuickWindow *itemWindow = that->window(); @@ -5082,6 +5086,17 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event) } } +void QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event) +{ + Q_Q(QQuickItem); + if (extra.isAllocated()) { + for (QQuickPointerHandler *handler : extra->pointerHandlers) { + qCDebug(lcPointerHandlerDispatch) << " delivering" << event << "to" << handler << "on" << q; + handler->handlePointerEvent(event); + } + } +} + /*! Called when \a change occurs for this item. diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index c0c9bd46bd..e7ead7fa87 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -87,6 +87,7 @@ class QQuickItemKeyFilter; class QQuickLayoutMirroringAttached; class QQuickEnterKeyAttached; class QQuickScreenAttached; +class QQuickPointerHandler; class QQuickContents : public QQuickItemChangeListener { @@ -344,6 +345,7 @@ public: QQuickLayoutMirroringAttached* layoutDirectionAttached; QQuickEnterKeyAttached *enterKeyAttached; QQuickItemKeyFilter *keyHandler; + QVector<QQuickPointerHandler *> pointerHandlers; #if QT_CONFIG(quick_shadereffect) mutable QQuickItemLayer *layer; #endif @@ -561,6 +563,8 @@ public: #endif void deliverShortcutOverrideEvent(QKeyEvent *); + virtual void handlePointerEvent(QQuickPointerEvent *); + bool isTransparentForPositioner() const; void setTransparentForPositioner(bool trans); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 07d87989b5..96c2937038 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -46,6 +46,7 @@ #include "qquickevents_p_p.h" #include <private/qquickdrag_p.h> +#include <private/qquickpointerhandler_p.h> #include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgtexture_p.h> @@ -659,7 +660,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (!q->mouseGrabberItem()) item->grabMouse(); auto pointerEventPoint = pointerEvent->pointById(p.id()); - pointerEventPoint->setGrabber(item); + pointerEventPoint->setGrabberItem(item); if (checkIfDoubleClicked(event->timestamp())) { QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); @@ -747,11 +748,11 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); if (point) - point->setGrabber(grabber); + point->setGrabberItem(grabber); } else { QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount() == 1); - event->point(0)->setGrabber(grabber); + event->point(0)->setGrabberItem(grabber); } if (oldGrabber) { @@ -762,9 +763,9 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) } } -void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids) +void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids) { - QSet<QQuickItem*> ungrab; + QSet<QObject*> ungrab; for (int i = 0; i < ids.count(); ++i) { // FIXME: deprecate this function, we need a device int id = ids.at(i); @@ -774,7 +775,7 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int } if (id == touchMouseId) { auto point = touchMouseDevice->pointerEvent()->pointById(id); - auto touchMouseGrabber = point->grabber(); + auto touchMouseGrabber = point->grabberItem(); if (touchMouseGrabber) { point->setGrabber(nullptr); touchMouseGrabber->mouseUngrabEvent(); @@ -790,7 +791,7 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int auto point = device->pointerEvent()->pointById(id); if (!point) continue; - QQuickItem *oldGrabber = point->grabber(); + QObject *oldGrabber = point->grabber(); if (oldGrabber == grabber) continue; @@ -799,8 +800,10 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int ungrab.insert(oldGrabber); } } - for (QQuickItem *oldGrabber : qAsConst(ungrab)) - oldGrabber->touchUngrabEvent(); + for (QObject *oldGrabber : qAsConst(ungrab)) + if (QQuickItem *item = qmlobject_cast<QQuickItem *>(oldGrabber)) + item->touchUngrabEvent(); + // TODO else if the old grabber was a PointerHandler, notify it somehow? } void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) @@ -812,7 +815,7 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to auto pointerEvent = device->pointerEvent(); for (int i = 0; i < pointerEvent->pointCount(); ++i) { if (pointerEvent->point(i)->grabber() == grabber) { - pointerEvent->point(i)->setGrabber(nullptr); + pointerEvent->point(i)->setGrabberItem(nullptr); // FIXME send ungrab event only once grabber->touchUngrabEvent(); } @@ -1485,12 +1488,12 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const QQuickPointerEvent *event = d->touchMouseDevice->pointerEvent(); auto point = event->pointById(d->touchMouseId); Q_ASSERT(point); - return point->grabber(); + return point->grabberItem(); } QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount()); - return event->point(0)->grabber(); + return event->point(0)->grabberItem(); } @@ -1642,7 +1645,13 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven Q_Q(QQuickWindow); auto point = pointerEvent->point(0); lastMousePosition = point->scenePos(); - QQuickItem *grabber = point->grabber(); + + if (point->grabberPointerHandler()) { + point->grabberPointerHandler()->handlePointerEvent(pointerEvent); + return; + } + + QQuickItem *grabber = point->grabberItem(); if (grabber) { // if the update consists of changing button state, then don't accept it // unless the button is one in which the item is interested @@ -1873,10 +1882,14 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) // A TouchCancel event will typically not contain any points. // Deliver it to all items that have active touches. QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent(); - QVector<QQuickItem *> grabbers = pointerEvent->grabbers(); - - for (QQuickItem *grabber: qAsConst(grabbers)) { - q->sendEvent(grabber, event); + QVector<QObject *> grabbers = pointerEvent->grabbers(); + + for (QObject *grabber: qAsConst(grabbers)) { + if (QQuickItem *grabberItem = qmlobject_cast<QQuickItem *>(grabber)) + q->sendEvent(grabberItem, event); + else //if (QQuickPointerHandler *grabberHandler = qmlobject_cast<QQuickPointerHandler *>(grabber)) +// grabberHandler->handlePointerEvent() + qWarning("unexpected: can't deliver touch cancel to a PointerHandler (yet?)"); } touchMouseId = -1; touchMouseDevice = nullptr; @@ -2014,8 +2027,6 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) { - Q_Q(QQuickWindow); - if (event->source() == Qt::MouseEventSynthesizedBySystem) { event->accept(); return; @@ -2048,7 +2059,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) updateCursor(event->windowPos()); #endif - if (!q->mouseGrabberItem()) { + if (!QQuickPointerDevice::genericMouseDevice()->pointerEvent()->point(0)->grabber()) { QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition; lastMousePosition = event->windowPos(); @@ -2216,7 +2227,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) if (point->state() == QQuickEventPoint::Released) { int id = point->pointId(); qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; - point->setGrabber(nullptr); + point->setGrabberItem(nullptr); if (id == touchMouseId) { touchMouseId = -1; touchMouseDevice = nullptr; @@ -2236,8 +2247,14 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered) { const auto grabbers = event->grabbers(); - for (auto grabber : grabbers) - deliverMatchingPointsToItem(grabber, event, hasFiltered); + for (auto grabber : grabbers) { + // The grabber is guaranteed to be either an item or a handler, but + // we need the item in order to call deliverMatchingPointsToItem(). + QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber); + if (!receiver) + receiver = static_cast<QQuickPointerHandler *>(grabber)->target(); + deliverMatchingPointsToItem(receiver, event, hasFiltered); + } return false; } @@ -2268,6 +2285,13 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem *> *hasFiltered) { Q_Q(QQuickWindow); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + pointerEvent->localize(item); + + // Let the Item's handlers (if any) have the event first. + itemPrivate->handlePointerEvent(pointerEvent); + if (pointerEvent->allPointsAccepted()) + return true; // TODO: unite this mouse point delivery with the synthetic mouse event below if (auto event = pointerEvent->asPointerMouseEvent()) { @@ -2275,7 +2299,6 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo auto point = event->point(0); if (point->isAccepted()) return false; - // The only reason to already have a mouse grabber here is // synthetic events - flickable sends one when setPressDelay is used. auto oldMouseGrabber = q->mouseGrabberItem(); @@ -2327,7 +2350,6 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo eventAccepted = touchEvent->isAccepted(); // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event if (deliverTouchAsMouse(item, event)) @@ -2341,7 +2363,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo auto pointerEventPoint = event->pointById(point.id()); pointerEventPoint->setAccepted(); if (point.state() == Qt::TouchPointPressed) - pointerEventPoint->setGrabber(item); + pointerEventPoint->setGrabberItem(item); } } else { // But if the event was not accepted then we know this item @@ -2350,7 +2372,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo if (point.state() == Qt::TouchPointPressed) { if (event->pointById(point.id())->grabber() == item) { qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; - event->pointById(point.id())->setGrabber(nullptr); + event->pointById(point.id())->setGrabberItem(nullptr); } } } @@ -2589,7 +2611,7 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; touchMouseId = tp.id(); touchMouseDevice = event->device(); - touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); + touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabberItem(target); target->grabMouse(); } filtered = true; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index be915903c6..30e3b71d0a 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -143,7 +143,7 @@ public: bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); - void grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids); + void grabTouchPoints(QObject *grabber, const QVector<int> &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); diff --git a/src/quick/quick.pro b/src/quick/quick.pro index 8f4f9a8290..2f49a53aa1 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -28,6 +28,7 @@ ANDROID_BUNDLED_FILES += \ include(util/util.pri) include(scenegraph/scenegraph.pri) include(items/items.pri) +include(handlers/handlers.pri) qtConfig(quick-designer): \ include(designer/designer.pri) qtConfig(accessibility) { |