aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items')
-rw-r--r--src/quick/items/qquickevents.cpp217
-rw-r--r--src/quick/items/qquickevents_p_p.h87
-rw-r--r--src/quick/items/qquickflickable.cpp1
-rw-r--r--src/quick/items/qquickitem.cpp22
-rw-r--r--src/quick/items/qquickitem_p.h4
-rw-r--r--src/quick/items/qquickwindow.cpp79
-rw-r--r--src/quick/items/qquickwindow_p.h2
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);