diff options
Diffstat (limited to 'src/quick/items/qquickevents.cpp')
-rw-r--r-- | src/quick/items/qquickevents.cpp | 217 |
1 files changed, 189 insertions, 28 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; } |