diff options
Diffstat (limited to 'src/quick/items')
-rw-r--r-- | src/quick/items/qquickevents.cpp | 217 | ||||
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 87 | ||||
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 1 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 22 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 4 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 79 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 2 |
7 files changed, 329 insertions, 83 deletions
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index b6c45c40a8..0c18cb4104 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -38,8 +38,10 @@ ****************************************************************************/ #include "qquickevents_p_p.h" +#include <QtCore/qmap.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> @@ -448,7 +450,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, @@ -505,35 +515,107 @@ 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; + } + m_velocity = (Q_LIKELY(velocity.isNull()) ? estimatedVelocity() : 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 { - 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; + return (m_grabberIsHandler ? nullptr : static_cast<QQuickItem *>(m_grabber.data())); +} + +void QQuickEventPoint::setGrabberItem(QQuickItem *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; } - m_grabber = QPointer<QQuickItem>(grabber); +} + +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; + } +} + +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) @@ -544,18 +626,86 @@ void QQuickEventPoint::setAccepted(bool accepted) } } +bool QQuickEventPoint::isDraggedOverThreshold() const +{ + QPointF delta = scenePos() - scenePressPos(); + return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, this) || + QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, this)); +} + QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) {} 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_ellipseDiameters = tp.ellipseDiameters(); m_uniqueId = tp.uniqueId(); } +struct PointVelocityData { + QVector2D velocity; + QPointF pos; + ulong timestamp; +}; + +typedef QMap<quint64, PointVelocityData*> PointDataForPointIdMap; +Q_GLOBAL_STATIC(PointDataForPointIdMap, g_previousPointData) +static const int PointVelocityAgeLimit = 500; // milliseconds + +/*! + * \interal + * \brief Estimates the velocity based on a weighted average of all previous velocities. + * The older the velocity is, the less significant it becomes for the estimate. + * \return + */ +QVector2D QQuickEventPoint::estimatedVelocity() const +{ + PointVelocityData *prevPoint = g_previousPointData->value(m_pointId); + if (!prevPoint) { + // cleanup events older than PointVelocityAgeLimit + auto end = g_previousPointData->end(); + for (auto it = g_previousPointData->begin(); it != end; ) { + PointVelocityData *data = it.value(); + if (m_timestamp - data->timestamp > PointVelocityAgeLimit) { + it = g_previousPointData->erase(it); + delete data; + } else { + ++it; + } + } + // TODO optimize: stop this dynamic memory thrashing + prevPoint = new PointVelocityData; + prevPoint->velocity = QVector2D(); + prevPoint->timestamp = 0; + prevPoint->pos = QPointF(); + g_previousPointData->insert(m_pointId, prevPoint); + } + if (prevPoint) { + const ulong timeElapsed = m_timestamp - prevPoint->timestamp; + if (timeElapsed == 0) // in case we call estimatedVelocity() twice on the same QQuickEventPoint + return m_velocity; + + QVector2D newVelocity; + if (prevPoint->timestamp != 0) + newVelocity = QVector2D(m_scenePos - prevPoint->pos)/timeElapsed; + + // VERY simple kalman filter: does a weighted average + // where the older velocities get less and less significant + static const float KalmanGain = 0.7f; + QVector2D filteredVelocity = newVelocity * KalmanGain + m_velocity * (1.0f - KalmanGain); + + prevPoint->velocity = filteredVelocity; + prevPoint->pos = m_scenePos; + prevPoint->timestamp = m_timestamp; + return filteredVelocity; + } + return QVector2D(); +} + /*! \internal \class QQuickPointerEvent @@ -598,10 +748,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); @@ -621,11 +776,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); @@ -637,7 +792,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)); } @@ -646,6 +801,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; @@ -660,7 +821,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); } @@ -681,16 +842,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 @@ -708,12 +869,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 cf6f83e5b1..4a78e98705 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 { @@ -251,12 +252,18 @@ 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(QVector2D velocity READ velocity) 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 { @@ -270,31 +277,53 @@ 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); + bool isDraggedOverThreshold() const; + 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: + QVector2D estimatedVelocity() const; 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) }; @@ -304,6 +333,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint Q_OBJECT Q_PROPERTY(qreal rotation READ rotation) Q_PROPERTY(qreal pressure READ pressure) + Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters) Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId) public: @@ -313,11 +343,13 @@ public: qreal rotation() const { return m_rotation; } qreal pressure() const { return m_pressure; } + QSizeF ellipseDiameters() const { return m_ellipseDiameters; } QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; } private: qreal m_rotation; qreal m_pressure; + QSizeF m_ellipseDiameters; QPointingDeviceUniqueId m_uniqueId; Q_DISABLE_COPY(QQuickEventTouchPoint) @@ -350,6 +382,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; } @@ -367,7 +400,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(); } @@ -389,6 +422,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; } @@ -396,7 +430,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; @@ -418,6 +452,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; } @@ -426,7 +461,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; @@ -497,21 +532,6 @@ 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(QPointingDeviceUniqueId::fromNumericId(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; } @@ -528,6 +548,22 @@ 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(QPointingDeviceUniqueId::fromNumericId(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; @@ -539,6 +575,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 4ab604b30f..45bb2a367d 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> diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index f7b9a58329..98cc3112df 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,14 @@ 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)) { + Q_ASSERT(pointerHandler->parentItem() == that); + // Accept all buttons, and leave filtering to pointerEvent() and/or user JS, + // because there can be multiple handlers... + that->setAcceptedMouseButtons(Qt::AllButtons); + QQuickItemPrivate *p = QQuickItemPrivate::get(that); + p->extra.value().pointerHandlers.append(pointerHandler); + } else { QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o); QQuickItem *item = that; QQuickWindow *itemWindow = that->window(); @@ -5082,6 +5091,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 660c5f8067..fa3093c245 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,14 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven Q_Q(QQuickWindow); auto point = pointerEvent->point(0); lastMousePosition = point->scenePos(); - QQuickItem *grabber = point->grabber(); + + if (auto handler = point->grabberPointerHandler()) { + pointerEvent->localize(handler->parentItem()); + handler->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 +1883,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 +2028,6 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) { - Q_Q(QQuickWindow); - if (event->source() == Qt::MouseEventSynthesizedBySystem) { event->accept(); return; @@ -2048,7 +2060,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(); @@ -2217,7 +2229,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; @@ -2237,8 +2249,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)->parentItem(); + deliverMatchingPointsToItem(receiver, event, hasFiltered); + } return false; } @@ -2269,6 +2287,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()) { @@ -2276,7 +2301,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(); @@ -2328,7 +2352,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)) @@ -2342,7 +2365,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 @@ -2351,7 +2374,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); } } } @@ -2590,7 +2613,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); |