aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2017-08-22 17:17:57 +0200
committerLars Knoll <lars.knoll@qt.io>2017-08-22 17:17:57 +0200
commitf1aff1f2d495562460a87d351e62c06109045a3a (patch)
tree93e4659421a3dc1fc664b539bf7e48ce28f64e84 /src/quick/items
parent842ada7b04d68cd37df2556bf50b48d8a5f39ec4 (diff)
parent46ed14da325c6c0382c0bc54cacc347d2d7f2b0a (diff)
Merge remote-tracking branch 'origin/dev' into wip/new-backend
Diffstat (limited to 'src/quick/items')
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp3
-rw-r--r--src/quick/items/qquickanimatedimage.cpp2
-rw-r--r--src/quick/items/qquickevents.cpp546
-rw-r--r--src/quick/items/qquickevents_p_p.h137
-rw-r--r--src/quick/items/qquickflickable.cpp5
-rw-r--r--src/quick/items/qquickframebufferobject.cpp16
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp3
-rw-r--r--src/quick/items/qquickimplicitsizeitem.cpp26
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p.h8
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p_p.h3
-rw-r--r--src/quick/items/qquickitem.cpp79
-rw-r--r--src/quick/items/qquickitem.h6
-rw-r--r--src/quick/items/qquickitem_p.h8
-rw-r--r--src/quick/items/qquickitemchangelistener_p.h1
-rw-r--r--src/quick/items/qquickitemsmodule.cpp6
-rw-r--r--src/quick/items/qquickitemview.cpp2
-rw-r--r--src/quick/items/qquickitemview_p_p.h1
-rw-r--r--src/quick/items/qquicklistview.cpp74
-rw-r--r--src/quick/items/qquickloader.cpp1
-rw-r--r--src/quick/items/qquickmousearea.cpp1
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp1
-rw-r--r--src/quick/items/qquickopenglshadereffect.cpp28
-rw-r--r--src/quick/items/qquickopenglshadereffectnode.cpp15
-rw-r--r--src/quick/items/qquickopenglshadereffectnode_p.h2
-rw-r--r--src/quick/items/qquickpincharea.cpp1
-rw-r--r--src/quick/items/qquickrectangle.cpp3
-rw-r--r--src/quick/items/qquickrendercontrol.cpp3
-rw-r--r--src/quick/items/qquicktext.cpp36
-rw-r--r--src/quick/items/qquicktext_p_p.h3
-rw-r--r--src/quick/items/qquicktextedit.cpp39
-rw-r--r--src/quick/items/qquicktextedit_p.h5
-rw-r--r--src/quick/items/qquicktextinput.cpp22
-rw-r--r--src/quick/items/qquickwindow.cpp542
-rw-r--r--src/quick/items/qquickwindow_p.h25
-rw-r--r--src/quick/items/qquickwindowattached_p.h3
-rw-r--r--src/quick/items/qquickwindowmodule.cpp2
-rw-r--r--src/quick/items/qquickwindowmodule_p.h5
37 files changed, 1316 insertions, 347 deletions
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index ed1f21c44c..bd66d844ff 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -40,6 +40,7 @@
#include "qquickcontext2d_p.h"
#include "qquickcontext2dcommandbuffer_p.h"
#include "qquickcanvasitem_p.h"
+#include <private/qtquickglobal_p.h>
#include <private/qquickcontext2dtexture_p.h>
#include <private/qquickitem_p.h>
#if QT_CONFIG(quick_shadereffect)
@@ -136,7 +137,7 @@ Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
THROW_GENERIC_ERROR("Not a Context2D object");
#define qClamp(val, min, max) qMin(qMax(val, min), max)
#define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
-QColor qt_color_from_string(const QV4::Value &name)
+Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name)
{
QByteArray str = name.toQString().toUtf8();
diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp
index adf460886a..a30d71dd1e 100644
--- a/src/quick/items/qquickanimatedimage.cpp
+++ b/src/quick/items/qquickanimatedimage.cpp
@@ -370,7 +370,7 @@ void QQuickAnimatedImage::movieRequestFinished()
}
#endif
- if (!d->_movie->isValid()) {
+ if (!d->_movie || !d->_movie->isValid()) {
qmlWarning(this) << "Error Reading Animated Image File " << d->url.toString();
delete d->_movie;
d->_movie = 0;
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 448b63c347..af2ba232f4 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -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,
@@ -457,6 +467,22 @@ Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice,
typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash;
Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices)
+// debugging helpers
+static const char *pointStateString(const QQuickEventPoint *point)
+{
+ static const QMetaEnum stateMetaEnum = point->metaObject()->enumerator(point->metaObject()->indexOfEnumerator("State"));
+ return stateMetaEnum.valueToKey(point->state());
+}
+
+static const QString pointDeviceName(const QQuickEventPoint *point)
+{
+ auto device = static_cast<const QQuickPointerEvent *>(point->parent())->device();
+ QString deviceName = (device ? device->name() : QLatin1String("null device"));
+ deviceName.resize(16, ' '); // shorten, and align in case of sequential output
+ return deviceName;
+}
+
+
QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d)
{
if (g_touchDevices->contains(d))
@@ -505,37 +531,241 @@ 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, int pointId, ulong timestamp, const QVector2D &velocity)
{
m_scenePos = scenePos;
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);
+}
+
+void QQuickEventPoint::localizePosition(QQuickItem *target)
+{
+ if (target)
+ m_pos = target->mapFromScene(scenePos());
+ else
+ m_pos = QPointF();
+}
+
+/*!
+ If this point has an exclusive grabber, returns a pointer to it; else
+ returns null, if there is no grabber. The grabber could be either
+ an Item or a PointerHandler.
+*/
+QObject *QQuickEventPoint::exclusiveGrabber() const
+{
+ return m_exclusiveGrabber.data();
+}
+
+/*!
+ Set the given Item or PointerHandler as the exclusive grabber of this point.
+ If there was already an exclusive grab, it will be canceled. If there
+ were passive grabbers, they will continue to lurk, but the exclusive grab
+ is a behavioral override of the passive grab as long as it remains.
+ If you already know whether the grabber is to be an Item or a PointerHandler,
+ you should instead call setGrabberItem() or setGrabberPointerHandler(),
+ because it is slightly more efficient.
+*/
+void QQuickEventPoint::setExclusiveGrabber(QObject *grabber)
+{
+ if (QQuickPointerHandler *phGrabber = qmlobject_cast<QQuickPointerHandler *>(grabber))
+ setGrabberPointerHandler(phGrabber, true);
+ else
+ setGrabberItem(static_cast<QQuickItem *>(grabber));
+}
+
+/*!
+ If the exclusive grabber of this point is an Item, returns a
+ pointer to that Item; else returns null, if there is no grabber or if
+ the grabber is a PointerHandler.
+*/
+QQuickItem *QQuickEventPoint::grabberItem() const
+{
+ return (m_grabberIsHandler ? nullptr : static_cast<QQuickItem *>(m_exclusiveGrabber.data()));
+}
+
+/*!
+ Set the given Item \a grabber as the exclusive grabber of this point.
+ If there was already an exclusive grab, it will be canceled. If there
+ were passive grabbers, they will continue to lurk, but the exclusive grab
+ is a behavioral override of the passive grab as long as it remains.
+*/
+void QQuickEventPoint::setGrabberItem(QQuickItem *grabber)
+{
+ if (grabber != m_exclusiveGrabber.data()) {
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab" << m_exclusiveGrabber << "->" << grabber;
+ }
+ QQuickPointerHandler *oldGrabberHandler = grabberPointerHandler();
+ QQuickItem *oldGrabberItem = grabberItem();
+ m_exclusiveGrabber = QPointer<QObject>(grabber);
+ m_grabberIsHandler = false;
+ m_sceneGrabPos = m_scenePos;
+ if (oldGrabberHandler)
+ oldGrabberHandler->onGrabChanged(oldGrabberHandler, CancelGrabExclusive, this);
+ else if (oldGrabberItem && oldGrabberItem != grabber && grabber && pointerEvent()->asPointerTouchEvent())
+ oldGrabberItem->touchUngrabEvent();
+ for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers)
+ passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this);
+ }
+}
+
+/*!
+ If the exclusive grabber of this point is a PointerHandler, returns a
+ pointer to that handler; else returns null, if there is no grabber or if
+ the grabber is an Item.
+*/
+QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const
+{
+ return (m_grabberIsHandler ? static_cast<QQuickPointerHandler *>(m_exclusiveGrabber.data()) : nullptr);
+}
+
+/*!
+ Set the given PointerHandler \a grabber as grabber of this point. If \a
+ exclusive is true, it will override any other grabs; if false, \a grabber
+ will be added to the list of passive grabbers of this point.
+*/
+void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive)
+{
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ if (exclusive) {
+ if (m_exclusiveGrabber != grabber)
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (exclusive)" << m_exclusiveGrabber << "->" << grabber;
+ } else {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (passive)" << grabber;
+ }
+ }
+ if (exclusive) {
+ if (grabber != m_exclusiveGrabber.data()) {
+ if (grabber) {
+ // set variables before notifying the new grabber
+ m_exclusiveGrabber = QPointer<QObject>(grabber);
+ m_grabberIsHandler = true;
+ m_sceneGrabPos = m_scenePos;
+ grabber->onGrabChanged(grabber, GrabExclusive, this);
+ for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) {
+ if (passiveGrabber != grabber)
+ passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this);
+ }
+ } else if (QQuickPointerHandler *oldGrabberPointerHandler = qmlobject_cast<QQuickPointerHandler *>(m_exclusiveGrabber.data())) {
+ oldGrabberPointerHandler->onGrabChanged(oldGrabberPointerHandler, UngrabExclusive, this);
+ } else if (!m_exclusiveGrabber.isNull()) {
+ // If there is a previous grabber and it's not a PointerHandler, it must be an Item.
+ QQuickItem *oldGrabberItem = static_cast<QQuickItem *>(m_exclusiveGrabber.data());
+ // If this point came from a touchscreen, notify that previous grabber Item that it's losing its touch grab.
+ if (pointerEvent()->asPointerTouchEvent())
+ oldGrabberItem->touchUngrabEvent();
+ }
+ // set variables after notifying the old grabber
+ m_exclusiveGrabber = QPointer<QObject>(grabber);
+ m_grabberIsHandler = true;
+ m_sceneGrabPos = m_scenePos;
+ }
+ } else {
+ if (!grabber) {
+ qDebug() << "can't set passive grabber to null";
+ return;
+ }
+ auto ptr = QPointer<QQuickPointerHandler>(grabber);
+ if (!m_passiveGrabbers.contains(ptr)) {
+ m_passiveGrabbers.append(ptr);
+ grabber->onGrabChanged(grabber, GrabPassive, this);
+ }
+ }
+}
+
+/*!
+ If this point has an existing exclusive grabber (Item or PointerHandler),
+ inform the grabber that its grab is canceled, and remove it as grabber.
+ This normally happens when the grab is stolen by another Item.
+*/
+void QQuickEventPoint::cancelExclusiveGrab()
+{
+ if (m_exclusiveGrabber.isNull())
+ qWarning("cancelGrab: no grabber");
+ else
+ cancelExclusiveGrabImpl();
+}
+
+void QQuickEventPoint::cancelExclusiveGrabImpl(QTouchEvent *cancelEvent)
+{
+ if (m_exclusiveGrabber.isNull())
+ return;
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr";
+ }
+ if (auto handler = grabberPointerHandler()) {
+ handler->onGrabChanged(handler, CancelGrabExclusive, this);
+ } else if (auto item = grabberItem()) {
+ if (cancelEvent)
+ QCoreApplication::sendEvent(item, cancelEvent);
+ else
+ item->touchUngrabEvent();
+ }
+ m_exclusiveGrabber.clear();
+}
+
+/*!
+ If this point has the given \a handler as a passive grabber,
+ inform the grabber that its grab is canceled, and remove it as grabber.
+ This normally happens when another Item or PointerHandler does an exclusive grab.
+*/
+void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler)
+{
+ if (removePassiveGrabber(handler)) {
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (passive)" << handler << "removed";
+ }
+ handler->onGrabChanged(handler, CancelGrabPassive, this);
+ }
}
-QQuickItem *QQuickEventPoint::grabber() const
+/*!
+ If this point has the given \a handler as a passive grabber, remove it as grabber.
+ Returns true if it was removed, false if it wasn't a grabber.
+*/
+bool QQuickEventPoint::removePassiveGrabber(QQuickPointerHandler *handler)
{
- return m_grabber.data();
+ return m_passiveGrabbers.removeOne(handler);
}
-void QQuickEventPoint::setGrabber(QQuickItem *grabber)
+/*!
+ If the given \a handler is grabbing this point passively, exclusively
+ or both, cancel the grab and remove it as grabber.
+ This normally happens when the handler decides that the behavior of this
+ point can no longer satisfy the handler's behavioral constraints within
+ the remainder of the gesture which the user is performing: for example
+ the handler tries to detect a tap but a drag is occurring instead, or
+ it tries to detect a drag in one direction but the drag is going in
+ another direction. In such cases the handler no longer needs or wants
+ to be informed of any further movements of this point.
+*/
+void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler)
{
- 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 (m_exclusiveGrabber == handler) {
+ handler->onGrabChanged(handler, CancelGrabExclusive, this);
+ m_exclusiveGrabber.clear();
}
- m_grabber = QPointer<QQuickItem>(grabber);
+ cancelPassiveGrab(handler);
}
+/*!
+ Set this point as \a accepted (true) or rejected (false).
+ Accepting a point is intended to stop event propagation.
+ It does not imply any kind of grab, passive or exclusive.
+ TODO explain further under what conditions propagation really does stop...
+*/
void QQuickEventPoint::setAccepted(bool accepted)
{
if (m_accept != accepted) {
@@ -550,12 +780,71 @@ 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_exclusiveGrabber.clear();
+ m_passiveGrabbers.clear();
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
+
+/*!
+ \internal
+ 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.
+*/
+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);
+ }
+ 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;
+}
+
/*!
\internal
\class QQuickPointerEvent
@@ -581,11 +870,14 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
return this;
m_device = QQuickPointerDevice::genericMouseDevice();
+ m_device->eventDeliveryTargets().clear();
m_button = ev->button();
m_pressedButtons = ev->buttons();
Qt::TouchPointState state = Qt::TouchPointStationary;
switch (ev->type()) {
case QEvent::MouseButtonPress:
+ m_mousePoint->clearPassiveGrabbers();
+ Q_FALLTHROUGH();
case QEvent::MouseButtonDblClick:
state = Qt::TouchPointPressed;
break;
@@ -598,10 +890,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->localizePosition(target);
+}
+
QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
{
auto ev = static_cast<QTouchEvent*>(event);
@@ -610,6 +907,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
return this;
m_device = QQuickPointerDevice::touchDevice(ev->device());
+ m_device->eventDeliveryTargets().clear();
m_button = Qt::NoButton;
m_pressedButtons = Qt::NoButton;
@@ -620,32 +918,63 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
for (int i = m_touchPoints.size(); i < newPointCount; ++i)
m_touchPoints.insert(i, new QQuickEventTouchPoint(this));
- // Make sure the grabbers are right from one event to the next
- QVector<QQuickItem*> grabbers;
- // Copy all grabbers, because the order of points might have changed in the event.
+ // Make sure the grabbers and on-pressed values are right from one event to the next
+ struct ToPreserve {
+ int pointId; // just for double-checking
+ ulong pressTimestamp;
+ QPointF scenePressPos;
+ QPointF sceneGrabPos;
+ QObject * grabber;
+ QVector <QPointer <QQuickPointerHandler> > passiveGrabbers;
+
+ ToPreserve() : pointId(0), pressTimestamp(0), grabber(nullptr) {}
+ };
+ QVector<ToPreserve> preserves(newPointCount); // jar of pickled touchpoints, in order of points in the _new_ event
+
+ // Copy stuff we need to preserve, 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;
- if (auto point = pointById(tps.at(i).id()))
- grabber = point->grabber();
- grabbers.append(grabber);
+ int pid = tps.at(i).id();
+ if (auto point = pointById(pid)) {
+ preserves[i].pointId = pid;
+ preserves[i].pressTimestamp = point->m_pressTimestamp;
+ preserves[i].scenePressPos = point->scenePressPos();
+ preserves[i].sceneGrabPos = point->sceneGrabPos();
+ preserves[i].grabber = point->exclusiveGrabber();
+ preserves[i].passiveGrabbers = point->passiveGrabbers();
+ }
}
for (int i = 0; i < newPointCount; ++i) {
auto point = m_touchPoints.at(i);
point->reset(tps.at(i), ev->timestamp());
+ const auto &preserved = preserves.at(i);
if (point->state() == QQuickEventPoint::Pressed) {
- if (grabbers.at(i))
+ if (preserved.grabber)
qWarning() << "TouchPointPressed without previous release event" << point;
- point->setGrabber(nullptr);
+ point->setGrabberItem(nullptr);
+ point->clearPassiveGrabbers();
} else {
- point->setGrabber(grabbers.at(i));
+ // Restore the grabbers without notifying (don't call onGrabChanged)
+ Q_ASSERT(preserved.pointId == 0 || preserved.pointId == point->pointId());
+ point->m_pressTimestamp = preserved.pressTimestamp;
+ point->m_scenePressPos = preserved.scenePressPos;
+ point->m_sceneGrabPos = preserved.sceneGrabPos;
+ point->m_exclusiveGrabber = preserved.grabber;
+ point->m_grabberIsHandler = (qmlobject_cast<QQuickPointerHandler *>(point->m_exclusiveGrabber) != nullptr);
+ point->m_passiveGrabbers = preserved.passiveGrabbers;
}
}
m_pointCount = newPointCount;
return this;
}
+void QQuickPointerTouchEvent::localize(QQuickItem *target)
+{
+ for (auto point : qAsConst(m_touchPoints))
+ point->localizePosition(target);
+}
+
QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const {
if (i == 0)
return m_mousePoint;
@@ -659,8 +988,8 @@ 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)
+ : QObject(parent), m_pointId(0), m_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
+ m_state(QQuickEventPoint::Released), m_accept(false), m_grabberIsHandler(false)
{
Q_UNUSED(m_reserved);
}
@@ -674,6 +1003,15 @@ bool QQuickPointerMouseEvent::allPointsAccepted() const {
return m_mousePoint->isAccepted();
}
+bool QQuickPointerMouseEvent::allUpdatedPointsAccepted() const {
+ return m_mousePoint->state() == QQuickEventPoint::Pressed || m_mousePoint->isAccepted();
+}
+
+bool QQuickPointerMouseEvent::allPointsGrabbed() const
+{
+ return m_mousePoint->exclusiveGrabber() != nullptr;
+}
+
QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const
{
auto event = static_cast<QMouseEvent *>(m_event);
@@ -681,16 +1019,31 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) cons
return event;
}
-QVector<QQuickItem *> QQuickPointerMouseEvent::grabbers() const
+/*!
+ Returns the exclusive grabber of this event, if any, in a vector.
+*/
+QVector<QObject *> QQuickPointerMouseEvent::exclusiveGrabbers() const
{
- QVector<QQuickItem *> result;
- if (QQuickItem *grabber = m_mousePoint->grabber())
+ QVector<QObject *> result;
+ if (QObject *grabber = m_mousePoint->exclusiveGrabber())
result << grabber;
return result;
}
+/*!
+ Remove all passive and exclusive grabbers of this event, without notifying.
+*/
void QQuickPointerMouseEvent::clearGrabbers() const {
- m_mousePoint->setGrabber(nullptr);
+ m_mousePoint->setGrabberItem(nullptr);
+ m_mousePoint->clearPassiveGrabbers();
+}
+
+/*!
+ Returns whether the given \a handler is the exclusive grabber of this event.
+*/
+bool QQuickPointerMouseEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+{
+ return m_mousePoint->exclusiveGrabber() == handler;
}
bool QQuickPointerMouseEvent::isPressEvent() const
@@ -700,6 +1053,24 @@ bool QQuickPointerMouseEvent::isPressEvent() const
(me->buttons() & me->button()) == me->buttons());
}
+bool QQuickPointerMouseEvent::isDoubleClickEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return (me->type() == QEvent::MouseButtonDblClick);
+}
+
+bool QQuickPointerMouseEvent::isUpdateEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return me->type() == QEvent::MouseMove;
+}
+
+bool QQuickPointerMouseEvent::isReleaseEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return me->type() == QEvent::MouseButtonRelease;
+}
+
bool QQuickPointerTouchEvent::allPointsAccepted() const {
for (int i = 0; i < m_pointCount; ++i) {
if (!m_touchPoints.at(i)->isAccepted())
@@ -708,12 +1079,32 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const {
return true;
}
-QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const
-{
- QVector<QQuickItem *> result;
+bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const {
for (int i = 0; i < m_pointCount; ++i) {
auto point = m_touchPoints.at(i);
- if (QQuickItem *grabber = point->grabber()) {
+ if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted())
+ return false;
+ }
+ return true;
+}
+
+bool QQuickPointerTouchEvent::allPointsGrabbed() const
+{
+ for (int i = 0; i < m_pointCount; ++i) {
+ if (!m_touchPoints.at(i)->exclusiveGrabber())
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns the exclusive grabbers of all points in this event, if any, in a vector.
+*/
+QVector<QObject *> QQuickPointerTouchEvent::exclusiveGrabbers() const
+{
+ QVector<QObject *> result;
+ for (int i = 0; i < m_pointCount; ++i) {
+ if (QObject *grabber = m_touchPoints.at(i)->exclusiveGrabber()) {
if (!result.contains(grabber))
result << grabber;
}
@@ -721,9 +1112,27 @@ QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const
return result;
}
+/*!
+ Remove all passive and exclusive grabbers of all touchpoints in this event,
+ without notifying.
+*/
void QQuickPointerTouchEvent::clearGrabbers() const {
+ for (auto point: m_touchPoints) {
+ point->setGrabberItem(nullptr);
+ point->clearPassiveGrabbers();
+ }
+}
+
+/*!
+ Returns whether the given \a handler is the exclusive grabber of any
+ touchpoint within this event.
+*/
+bool QQuickPointerTouchEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+{
for (auto point: m_touchPoints)
- point->setGrabber(nullptr);
+ if (point->exclusiveGrabber() == handler)
+ return true;
+ return false;
}
bool QQuickPointerTouchEvent::isPressEvent() const
@@ -731,6 +1140,16 @@ bool QQuickPointerTouchEvent::isPressEvent() const
return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed;
}
+bool QQuickPointerTouchEvent::isUpdateEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & (Qt::TouchPointMoved | Qt::TouchPointStationary);
+}
+
+bool QQuickPointerTouchEvent::isReleaseEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointReleased;
+}
+
QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const
{
QVector<QPointF> points;
@@ -793,15 +1212,15 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte
\l {QQuickEventPoint::pointId}{pointId}.
Returns nullptr if there is no point with that ID.
- \fn QQuickPointerEvent::pointById(quint64 pointId) const
+ \fn QQuickPointerEvent::pointById(int pointId) const
*/
-QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const {
+QQuickEventPoint *QQuickPointerMouseEvent::pointById(int pointId) const {
if (m_mousePoint && pointId == m_mousePoint->pointId())
return m_mousePoint;
return nullptr;
}
-QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 pointId) const {
+QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const {
auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(),
[pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } );
if (it != m_touchPoints.constEnd())
@@ -831,7 +1250,12 @@ const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int point
\internal
Make a new QTouchEvent, giving it a subset of the original touch points.
- Returns a nullptr if all points are stationary or there are no points inside the item.
+ Returns a nullptr if all points are stationary, or there are no points inside the item,
+ or none of the points were pressed inside and the item was not grabbing any of them
+ and isFiltering is false. When isFiltering is true, it is assumed that the item
+ cares about all points which are inside its bounds, because most filtering items
+ need to monitor eventpoint movements until a drag threshold is exceeded or the
+ requirements for a gesture to be recognized are met in some other way.
*/
QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const
{
@@ -841,19 +1265,24 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
// Or else just document that velocity is always scene-relative and is not scaled and rotated with the item
// but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity
+ bool anyPressOrReleaseInside = false;
+ bool anyGrabber = false;
QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform());
for (int i = 0; i < m_pointCount; ++i) {
auto p = m_touchPoints.at(i);
if (p->isAccepted())
continue;
// include points where item is the grabber
- bool isGrabber = p->grabber() == item;
- // include newly pressed points inside the bounds
- bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos()));
+ bool isGrabber = p->exclusiveGrabber() == item;
+ if (isGrabber)
+ anyGrabber = true;
+ // include points inside the bounds if no other item is the grabber or if the item is filtering
+ bool isInside = item->contains(item->mapFromScene(p->scenePos()));
+ bool hasAnotherGrabber = p->exclusiveGrabber() && p->exclusiveGrabber() != item;
// filtering: (childMouseEventFilter) include points that are grabbed by children of the target item
bool grabberIsChild = false;
- auto parent = p->grabber();
+ auto parent = p->grabberItem();
while (isFiltering && parent) {
if (parent == item) {
grabberIsChild = true;
@@ -862,11 +1291,11 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
parent = parent->parentItem();
}
- // when filtering, send points that are grabbed by a child and points that are not grabbed but inside
- bool filterRelevant = isFiltering && (grabberIsChild || (!p->grabber() && item->contains(item->mapFromScene(p->scenePos()))));
- if (!(isGrabber || isPressInside || filterRelevant))
+ bool filterRelevant = isFiltering && grabberIsChild;
+ if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant))
continue;
-
+ if ((p->state() == QQuickEventPoint::Pressed || p->state() == QQuickEventPoint::Released) && isInside)
+ anyPressOrReleaseInside = true;
const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId());
if (tp) {
eventStates |= tp->state();
@@ -880,7 +1309,9 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
}
}
- if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty())
+ // Now touchPoints will have only points which are inside the item.
+ // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway.
+ if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering))
return nullptr;
// if all points have the same state, set the event type accordingly
@@ -942,7 +1373,12 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *
Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) {
QDebugStateSaver saver(dbg);
dbg.nospace();
- dbg << "QQuickPointerEvent(dev:";
+ dbg << "QQuickPointerEvent(";
+ if (event->isValid())
+ dbg << event->timestamp();
+ else
+ dbg << "invalid";
+ dbg << " dev:";
QtDebugUtils::formatQEnum(dbg, event->device()->type());
if (event->buttons() != Qt::NoButton) {
dbg << " buttons:";
@@ -959,7 +1395,7 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *e
Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) {
QDebugStateSaver saver(dbg);
dbg.nospace();
- dbg << "QQuickEventPoint(valid:" << event->isValid() << " accepted:" << event->isAccepted()
+ dbg << "QQuickEventPoint(accepted:" << event->isAccepted()
<< " state:";
QtDebugUtils::formatQEnum(dbg, event->state());
dbg << " scenePos:" << event->scenePos() << " id:" << hex << event->pointId() << dec
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index 3735d68a85..0d6e06ac41 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -68,6 +68,7 @@ class QQuickPointerEvent;
class QQuickPointerMouseEvent;
class QQuickPointerTabletEvent;
class QQuickPointerTouchEvent;
+class QQuickPointerHandler;
class QQuickKeyEvent : public QObject
{
@@ -251,12 +252,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(State state READ state)
- Q_PROPERTY(quint64 pointId READ pointId)
+ Q_PROPERTY(int 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 *exclusiveGrabber READ exclusiveGrabber WRITE setExclusiveGrabber)
public:
enum State {
@@ -266,35 +272,79 @@ public:
Released = Qt::TouchPointReleased
// Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe
};
- Q_ENUM(State)
+ Q_DECLARE_FLAGS(States, State)
+ Q_FLAG(States)
+
+ enum GrabState {
+ GrabPassive = 0x01,
+ UngrabPassive = 0x02,
+ CancelGrabPassive = 0x03,
+ OverrideGrabPassive = 0x04,
+ GrabExclusive = 0x10,
+ UngrabExclusive = 0x20,
+ CancelGrabExclusive = 0x30,
+ };
+ Q_ENUM(GrabState)
QQuickEventPoint(QQuickPointerEvent *parent);
- void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp);
-
- void invalidate() { m_valid = false; }
+ void reset(Qt::TouchPointState state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity = QVector2D());
+ void localizePosition(QQuickItem *target);
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; }
+ int pointId() const { return m_pointId; }
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 *exclusiveGrabber() const;
+ void setExclusiveGrabber(QObject *exclusiveGrabber);
+
+ QQuickItem *grabberItem() const;
+ Q_DECL_DEPRECATED QQuickItem *grabber() const { return grabberItem(); }
+ void setGrabberItem(QQuickItem *exclusiveGrabber);
+
+ QQuickPointerHandler *grabberPointerHandler() const;
+ void setGrabberPointerHandler(QQuickPointerHandler *exclusiveGrabber, bool exclusive = false);
+
+ void cancelExclusiveGrab();
+ void cancelPassiveGrab(QQuickPointerHandler *handler);
+ bool removePassiveGrabber(QQuickPointerHandler *handler);
+ void cancelAllGrabs(QQuickPointerHandler *handler);
+
+ QVector<QPointer <QQuickPointerHandler> > passiveGrabbers() const { return m_passiveGrabbers; }
+ void setPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &grabbers) { m_passiveGrabbers = grabbers; }
+ void clearPassiveGrabbers() { m_passiveGrabbers.clear(); }
+
+protected:
+ void cancelExclusiveGrabImpl(QTouchEvent *cancelEvent = nullptr);
private:
+ QVector2D estimatedVelocity() const;
+
+protected:
+ QPointF m_pos;
QPointF m_scenePos;
- quint64 m_pointId;
- QPointer<QQuickItem> m_grabber;
+ QPointF m_scenePressPos;
+ QPointF m_sceneGrabPos;
+ QVector2D m_velocity;
+ int m_pointId;
+ QPointer<QObject> m_exclusiveGrabber;
+ QVector<QPointer <QQuickPointerHandler> > m_passiveGrabbers;
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;
+
+ friend class QQuickPointerTouchEvent;
+ friend class QQuickWindowPrivate;
Q_DISABLE_COPY(QQuickEventPoint)
};
@@ -304,6 +354,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,13 +364,17 @@ 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;
+ friend class QQuickPointerTouchEvent;
+
Q_DISABLE_COPY(QQuickEventTouchPoint)
};
@@ -350,8 +405,12 @@ 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 bool isDoubleClickEvent() const { return false; }
+ virtual bool isUpdateEvent() const = 0;
+ virtual bool isReleaseEvent() const = 0;
virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; }
virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; }
virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; }
@@ -360,15 +419,18 @@ public: // helpers for C++ only (during event delivery)
virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; }
bool isValid() const { return m_event != nullptr; }
virtual bool allPointsAccepted() const = 0;
+ virtual bool allUpdatedPointsAccepted() const = 0;
+ virtual bool allPointsGrabbed() const = 0;
bool isAccepted() { return m_event->isAccepted(); }
void setAccepted(bool accepted) { m_event->setAccepted(accepted); }
QVector<QPointF> unacceptedPressedPointScenePositions() const;
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 QQuickEventPoint *pointById(int pointId) const = 0;
+ virtual QVector<QObject *> exclusiveGrabbers() const = 0;
virtual void clearGrabbers() const = 0;
+ virtual bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const = 0;
ulong timestamp() const { return m_event->timestamp(); }
@@ -389,15 +451,22 @@ public:
: QQuickPointerEvent(parent, device), m_mousePoint(new QQuickEventPoint(this)) { }
QQuickPointerEvent *reset(QEvent *) override;
+ void localize(QQuickItem *target) override;
bool isPressEvent() const override;
+ bool isDoubleClickEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; }
int pointCount() const override { return 1; }
QQuickEventPoint *point(int i) const override;
- QQuickEventPoint *pointById(quint64 pointId) const override;
+ QQuickEventPoint *pointById(int pointId) const override;
bool allPointsAccepted() const override;
- QVector<QQuickItem *> grabbers() const override;
+ bool allUpdatedPointsAccepted() const override;
+ bool allPointsGrabbed() const override;
+ QVector<QObject *> exclusiveGrabbers() const override;
void clearGrabbers() const override;
+ bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override;
QMouseEvent *asMouseEvent(const QPointF& localPos) const;
@@ -418,16 +487,22 @@ public:
{ }
QQuickPointerEvent *reset(QEvent *) override;
+ void localize(QQuickItem *target) override;
bool isPressEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; }
const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; }
int pointCount() const override { return m_pointCount; }
QQuickEventPoint *point(int i) const override;
- QQuickEventPoint *pointById(quint64 pointId) const override;
+ QQuickEventPoint *pointById(int pointId) const override;
const QTouchEvent::TouchPoint *touchPointById(int pointId) const;
bool allPointsAccepted() const override;
- QVector<QQuickItem *> grabbers() const override;
+ bool allUpdatedPointsAccepted() const override;
+ bool allPointsGrabbed() const override;
+ QVector<QObject *> exclusiveGrabbers() const override;
void clearGrabbers() const override;
+ bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override;
QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const;
QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const;
@@ -497,13 +572,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))
- {
- }
-
DeviceType type() const { return m_deviceType; }
PointerType pointerType() const { return m_pointerType; }
Capabilities capabilities() const { return m_capabilities; }
@@ -518,6 +586,17 @@ public:
static QQuickPointerDevice *genericMouseDevice();
static QQuickPointerDevice *tabletDevice(qint64);
+ QVector<QQuickPointerHandler *> &eventDeliveryTargets() { return m_eventDeliveryTargets; }
+
+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))
+ {
+ }
+ ~QQuickPointerDevice() { }
+
private:
DeviceType m_deviceType;
PointerType m_pointerType;
@@ -526,8 +605,10 @@ private:
int m_buttonCount;
QString m_name;
QPointingDeviceUniqueId m_uniqueId;
+ QVector<QQuickPointerHandler *> m_eventDeliveryTargets; // during delivery, handlers which have already seen the 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 a3bc7635a1..ce584cd283 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>
@@ -269,6 +270,7 @@ void QQuickFlickablePrivate::init()
qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()),
q, QQuickFlickable, SLOT(velocityTimelineCompleted()))
q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch
q->setFiltersChildMouseEvents(true);
QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
@@ -1547,6 +1549,8 @@ void QQuickFlickablePrivate::replayDelayedPress()
// If we have the grab, release before delivering the event
if (QQuickWindow *w = q->window()) {
+ QQuickWindowPrivate *wpriv = QQuickWindowPrivate::get(w);
+ wpriv->allowChildEventFiltering = false; // don't allow re-filtering during replay
replayingPressEvent = true;
if (w->mouseGrabberItem() == q)
q->ungrabMouse();
@@ -1554,6 +1558,7 @@ void QQuickFlickablePrivate::replayDelayedPress()
// Use the event handler that will take care of finding the proper item to propagate the event
QCoreApplication::sendEvent(w, mouseEvent.data());
replayingPressEvent = false;
+ wpriv->allowChildEventFiltering = true;
}
}
}
diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp
index 52b19d994c..042ee21aec 100644
--- a/src/quick/items/qquickframebufferobject.cpp
+++ b/src/quick/items/qquickframebufferobject.cpp
@@ -260,7 +260,7 @@ public:
bool renderPending;
bool invalidatePending;
- int devicePixelRatio;
+ qreal devicePixelRatio;
};
static inline bool isOpenGL(QSGRenderContext *rc)
@@ -312,14 +312,12 @@ QSGNode *QQuickFramebufferObject::updatePaintNode(QSGNode *node, UpdatePaintNode
n->devicePixelRatio = window()->effectiveDevicePixelRatio();
desiredFboSize *= n->devicePixelRatio;
- if (n->fbo && (d->followsItemSize || n->invalidatePending)) {
- if (n->fbo->size() != desiredFboSize) {
- delete n->fbo;
- n->fbo = 0;
- delete n->msDisplayFbo;
- n->msDisplayFbo = 0;
- n->invalidatePending = false;
- }
+ if (n->fbo && ((d->followsItemSize && n->fbo->size() != desiredFboSize) || n->invalidatePending)) {
+ delete n->fbo;
+ n->fbo = 0;
+ delete n->msDisplayFbo;
+ n->msDisplayFbo = 0;
+ n->invalidatePending = false;
}
if (!n->fbo) {
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
index b366071962..305ef7e778 100644
--- a/src/quick/items/qquickgenericshadereffect.cpp
+++ b/src/quick/items/qquickgenericshadereffect.cpp
@@ -546,7 +546,10 @@ void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType)
// Have a QSignalMapper that emits mapped() with an index+type on each property change notify signal.
auto &sm(m_signalMappers[shaderType][i]);
if (!sm.mapper) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
sm.mapper = new QSignalMapper;
+QT_WARNING_POP
sm.mapper->setMapping(m_item, i | (shaderType << 16));
}
sm.active = true;
diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp
index 1996fb9489..2569b2a224 100644
--- a/src/quick/items/qquickimplicitsizeitem.cpp
+++ b/src/quick/items/qquickimplicitsizeitem.cpp
@@ -45,29 +45,11 @@ QT_BEGIN_NAMESPACE
/*!
\internal
- The purpose of QQuickImplicitSizeItem is not immediately clear, as both
- the implicit size properties and signals exist on QQuickItem. However,
- for some items - where the implicit size has an underlying meaning (such as
- Image, where the implicit size represents the real size of the image)
- having implicit size writable is an undesirable thing.
-
- QQuickImplicitSizeItem redefines the properties as being readonly.
- Unfortunately, this also means they need to redefine the change signals.
- See QTBUG-30258 for more information.
+ QQuickImplicitSizeItem redefines the implicitWidth and implicitHeight
+ properties as readonly, as some items (e.g. Image, where the implicit size
+ represents the real size of the image) should not be able to have their
+ implicit size modified.
*/
-void QQuickImplicitSizeItemPrivate::implicitWidthChanged()
-{
- Q_Q(QQuickImplicitSizeItem);
- QQuickItemPrivate::implicitWidthChanged();
- emit q->implicitWidthChanged2();
-}
-
-void QQuickImplicitSizeItemPrivate::implicitHeightChanged()
-{
- Q_Q(QQuickImplicitSizeItem);
- QQuickItemPrivate::implicitHeightChanged();
- emit q->implicitHeightChanged2();
-}
QQuickImplicitSizeItem::QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent)
: QQuickItem(dd, parent)
diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h
index 75b04449f8..8ae8f9f447 100644
--- a/src/quick/items/qquickimplicitsizeitem_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p.h
@@ -60,16 +60,12 @@ class QQuickImplicitSizeItemPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItem : public QQuickItem
{
Q_OBJECT
- Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged2)
- Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged2)
+ Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged)
+ Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged)
protected:
QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent);
-Q_SIGNALS:
- Q_REVISION(1) void implicitWidthChanged2();
- Q_REVISION(1) void implicitHeightChanged2();
-
private:
Q_DISABLE_COPY(QQuickImplicitSizeItem)
Q_DECLARE_PRIVATE(QQuickImplicitSizeItem)
diff --git a/src/quick/items/qquickimplicitsizeitem_p_p.h b/src/quick/items/qquickimplicitsizeitem_p_p.h
index 2c42fa62f2..0495cf87e1 100644
--- a/src/quick/items/qquickimplicitsizeitem_p_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p_p.h
@@ -65,9 +65,6 @@ public:
QQuickImplicitSizeItemPrivate()
{
}
-
- void implicitWidthChanged() Q_DECL_OVERRIDE;
- void implicitHeightChanged() Q_DECL_OVERRIDE;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index a8a862bb2f..9f2d543387 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>
@@ -2138,6 +2139,9 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
\value ItemAntialiasingHasChanged The antialiasing has changed. The current
(boolean) value can be found in QQuickItem::antialiasing.
+
+ \value ItemEnabledHasChanged The item's enabled state has changed.
+ ItemChangeData::boolValue contains the new enabled state. (since Qt 5.10)
*/
/*!
@@ -3186,6 +3190,11 @@ QQuickItemPrivate::QQuickItemPrivate()
, antialiasingValid(false)
, isTabFence(false)
, replayingPressEvent(false)
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ , touchEnabled(true)
+#else
+ , touchEnabled(false)
+#endif
, dirtyAttributes(0)
, nextDirtyItem(0)
, prevDirtyItem(0)
@@ -3237,7 +3246,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();
@@ -5102,6 +5118,28 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
}
/*!
+ \internal
+ Deliver the \a event to all PointerHandlers which are in the pre-determined
+ eventDeliveryTargets() vector. If \a avoidExclusiveGrabber is true, it skips
+ delivery to any handler which is the exclusive grabber of any point within this event
+ (because delivery to exclusive grabbers is handled separately).
+*/
+bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidExclusiveGrabber)
+{
+ bool delivered = false;
+ QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets();
+ if (extra.isAllocated()) {
+ for (QQuickPointerHandler *handler : extra->pointerHandlers) {
+ if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) {
+ handler->handlePointerEvent(event);
+ delivered = true;
+ }
+ }
+ }
+ return delivered;
+}
+
+/*!
Called when \a change occurs for this item.
\a value contains extra information relating to the change, when
@@ -5927,6 +5965,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
}
+ itemChange(QQuickItem::ItemEnabledHasChanged, effectiveEnable);
emit q->enabledChanged();
}
@@ -6099,6 +6138,18 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
}
break;
}
+ case QQuickItem::ItemEnabledHasChanged: {
+ q->itemChange(change, data);
+ if (!changeListeners.isEmpty()) {
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
+ for (const QQuickItemPrivate::ChangeListener &change : listeners) {
+ if (change.types & QQuickItemPrivate::Enabled) {
+ change.listener->itemEnabledChanged(q);
+ }
+ }
+ }
+ break;
+ }
case QQuickItem::ItemParentHasChanged: {
q->itemChange(change, data);
if (!changeListeners.isEmpty()) {
@@ -7173,6 +7224,32 @@ void QQuickItem::setAcceptHoverEvents(bool enabled)
d->setHasHoverInChild(enabled);
}
+/*!
+ Returns whether touch events are accepted by this item.
+
+ The default value is false.
+
+ If this is false, then the item will not receive any touch events through
+ the touchEvent() function.
+*/
+bool QQuickItem::acceptTouchEvents() const
+{
+ Q_D(const QQuickItem);
+ return d->touchEnabled;
+}
+
+/*!
+ If \a enabled is true, this sets the item to accept touch events;
+ otherwise, touch events are not accepted by this item.
+
+ \sa acceptTouchEvents()
+*/
+void QQuickItem::setAcceptTouchEvents(bool accept)
+{
+ Q_D(QQuickItem);
+ d->touchEnabled = accept;
+}
+
void QQuickItemPrivate::setHasCursorInChild(bool hasCursor)
{
#if QT_CONFIG(cursor)
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index f58946d01d..25641f16f9 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -173,7 +173,8 @@ public:
ItemActiveFocusHasChanged, // value.boolValue
ItemRotationHasChanged, // value.realValue
ItemAntialiasingHasChanged, // value.boolValue
- ItemDevicePixelRatioHasChanged // value.realValue
+ ItemDevicePixelRatioHasChanged, // value.realValue
+ ItemEnabledHasChanged // value.boolValue
};
union ItemChangeData {
@@ -292,6 +293,8 @@ public:
void setAcceptedMouseButtons(Qt::MouseButtons buttons);
bool acceptHoverEvents() const;
void setAcceptHoverEvents(bool enabled);
+ bool acceptTouchEvents() const;
+ void setAcceptTouchEvents(bool accept);
#if QT_CONFIG(cursor)
QCursor cursor() const;
@@ -448,6 +451,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_resourceObjectDeleted(QObject *))
Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QV4::ExecutionEngine *))
+ friend class QQuickEventPoint;
friend class QQuickWindow;
friend class QQuickWindowPrivate;
friend class QSGRenderer;
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index e56d839de9..d1aaf6026b 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
{
@@ -321,7 +322,8 @@ public:
Children = 0x40,
Rotation = 0x80,
ImplicitWidth = 0x100,
- ImplicitHeight = 0x200
+ ImplicitHeight = 0x200,
+ Enabled = 0x400,
};
Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
@@ -348,6 +350,7 @@ public:
QQuickLayoutMirroringAttached* layoutDirectionAttached;
QQuickEnterKeyAttached *enterKeyAttached;
QQuickItemKeyFilter *keyHandler;
+ QVector<QQuickPointerHandler *> pointerHandlers;
#if QT_CONFIG(quick_shadereffect)
mutable QQuickItemLayer *layer;
#endif
@@ -437,6 +440,7 @@ public:
// focus chain and prevents tabbing outside.
bool isTabFence:1;
bool replayingPressEvent:1;
+ bool touchEnabled:1;
enum DirtyType {
TransformOrigin = 0x00000001,
@@ -565,6 +569,8 @@ public:
#endif
void deliverShortcutOverrideEvent(QKeyEvent *);
+ virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidExclusiveGrabber = false);
+
bool isTransparentForPositioner() const;
void setTransparentForPositioner(bool trans);
diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h
index 83c69a9330..cb0af75c4c 100644
--- a/src/quick/items/qquickitemchangelistener_p.h
+++ b/src/quick/items/qquickitemchangelistener_p.h
@@ -125,6 +125,7 @@ public:
virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF & /* oldGeometry */) {}
virtual void itemSiblingOrderChanged(QQuickItem *) {}
virtual void itemVisibilityChanged(QQuickItem *) {}
+ virtual void itemEnabledChanged(QQuickItem *) {}
virtual void itemOpacityChanged(QQuickItem *) {}
virtual void itemDestroyed(QQuickItem *) {}
virtual void itemChildAdded(QQuickItem *, QQuickItem * /* child */ ) {}
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 13f23c918a..e6321e9365 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -376,6 +376,12 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickFlickable, 9>(uri, 2, 9, "Flickable");
qmlRegisterType<QQuickMouseArea, 9>(uri, 2, 9, "MouseArea");
+
+#if QT_CONFIG(quick_path)
+ qmlRegisterType<QQuickPathArc, 2>(uri, 2, 9, "PathArc");
+ qmlRegisterType<QQuickPathMove>(uri, 2, 9, "PathMove");
+#endif
+
qmlRegisterType<QQuickText, 9>(uri, 2, 9, "Text");
qmlRegisterType<QQuickTextInput, 9>(uri, 2, 9, "TextInput");
qmlRegisterType<QQuickTouchPoint>(uri, 2, 9, "TouchPoint");
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index 084b1f197a..1d0d042839 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -1921,7 +1921,7 @@ void QQuickItemViewPrivate::layout()
markExtentsDirty();
updateHighlight();
- if (!q->isMoving() && !q->isFlicking()) {
+ if (!q->isMoving() && !q->isFlicking() && !movingFromHighlight()) {
fixupPosition();
refill();
}
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index b6353246e8..2c04022cde 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -363,6 +363,7 @@ protected:
virtual void createHighlight() = 0;
virtual void updateHighlight() = 0;
virtual void resetHighlightPosition() = 0;
+ virtual bool movingFromHighlight() { return false; }
virtual void setPosition(qreal pos) = 0;
virtual void fixupPosition() = 0;
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 18f9b8512d..edfa970035 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -101,6 +101,7 @@ public:
void createHighlight() override;
void updateHighlight() override;
void resetHighlightPosition() override;
+ bool movingFromHighlight() override;
void setPosition(qreal pos) override;
void layoutVisibleItems(int fromModelIndex = 0) override;
@@ -945,6 +946,17 @@ void QQuickListViewPrivate::resetHighlightPosition()
static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
}
+bool QQuickListViewPrivate::movingFromHighlight()
+{
+ if (!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange)
+ return false;
+
+ return (highlightPosAnimator && highlightPosAnimator->isRunning()) ||
+ (highlightHeightAnimator && highlightHeightAnimator->isRunning()) ||
+ (highlightWidthAnimator && highlightWidthAnimator->isRunning());
+}
+
+
QQuickItem * QQuickListViewPrivate::getSectionItem(const QString &section)
{
Q_Q(QQuickListView);
@@ -1835,6 +1847,38 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
\snippet qml/listview/listview.qml flickBothDirections
+ \section1 Stacking Order in ListView
+
+ The \l {QQuickItem::z}{Z value} of items determines whether they are
+ rendered above or below other items. ListView uses several different
+ default Z values, depending on what type of item is being created:
+
+ \table
+ \header
+ \li Property
+ \li Default Z value
+ \row
+ \li \l delegate
+ \li 1
+ \row
+ \li \l footer
+ \li 1
+ \row
+ \li \l header
+ \li 1
+ \row
+ \li \l highlight
+ \li 0
+ \row
+ \li \l section.delegate
+ \li 2
+ \endtable
+
+ These default values are set if the Z value of the item is \c 0, so setting
+ the Z value of these items to \c 0 has no effect. Note that the Z value is
+ of type \l [QML] {real}, so it is possible to set fractional
+ values like \c 0.1.
+
\sa {QML Data Models}, GridView, PathView, {Qt Quick Examples - Views}
*/
QQuickListView::QQuickListView(QQuickItem *parent)
@@ -1963,6 +2007,8 @@ QQuickListView::~QQuickListView()
\note Delegates are instantiated as needed and may be destroyed at any time.
They are parented to ListView's \l {Flickable::contentItem}{contentItem}, not to the view itself.
State should \e never be stored in a delegate.
+
+ \sa {Stacking Order in ListView}
*/
/*!
\qmlproperty int QtQuick::ListView::currentIndex
@@ -1990,7 +2036,7 @@ QQuickListView::~QQuickListView()
The default \l {QQuickItem::z}{stacking order}
of the highlight item is \c 0.
- \sa highlight, highlightFollowsCurrentItem
+ \sa highlight, highlightFollowsCurrentItem, {Stacking Order in ListView}
*/
/*!
@@ -2009,7 +2055,8 @@ QQuickListView::~QQuickListView()
highlight item is \c 0.
\sa highlightItem, highlightFollowsCurrentItem,
- {Qt Quick Examples - Views#Highlight}{ListView highlight example}
+ {Qt Quick Examples - Views#Highlight}{ListView highlight example},
+ {Stacking Order in ListView}
*/
/*!
@@ -2352,7 +2399,8 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
differing sections will result in a section header being created
even if that section exists elsewhere.
- \sa {Qt Quick Examples - Views}{ListView examples}
+ \sa {Qt Quick Examples - Views}{ListView examples},
+ {Stacking Order in ListView}
*/
QQuickViewSection *QQuickListView::sectionCriteria()
{
@@ -2503,7 +2551,7 @@ void QQuickListView::setSnapMode(SnapMode mode)
footer is positioned at the end of the view, after any items. The
default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
- \sa header, footerItem
+ \sa header, footerItem, {Stacking Order in ListView}
*/
@@ -2515,7 +2563,7 @@ void QQuickListView::setSnapMode(SnapMode mode)
header is positioned at the beginning of the view, before any items.
The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
- \sa footer, headerItem
+ \sa footer, headerItem, {Stacking Order in ListView}
*/
/*!
@@ -2526,7 +2574,7 @@ void QQuickListView::setSnapMode(SnapMode mode)
header is positioned at the beginning of the view, before any items.
The default \l {QQuickItem::z}{stacking order} of the header is \c 1.
- \sa header, footerItem
+ \sa header, footerItem, {Stacking Order in ListView}
*/
/*!
@@ -2537,7 +2585,7 @@ void QQuickListView::setSnapMode(SnapMode mode)
footer is positioned at the end of the view, after any items. The
default \l {QQuickItem::z}{stacking order} of the footer is \c 1.
- \sa footer, headerItem
+ \sa footer, headerItem, {Stacking Order in ListView}
*/
/*!
@@ -2555,6 +2603,12 @@ void QQuickListView::setSnapMode(SnapMode mode)
The header can be pushed away by moving the content forwards, and pulled back by
moving the content backwards.
\endlist
+
+ \note This property has no effect on the \l {QQuickItem::z}{stacking order}
+ of the header. For example, if the header should be shown above the
+ \l delegate items when using \c ListView.OverlayHeader, its Z value
+ should be set to a value higher than that of the delegates. For more
+ information, see \l {Stacking Order in ListView}.
*/
QQuickListView::HeaderPositioning QQuickListView::headerPositioning() const
{
@@ -2592,6 +2646,12 @@ void QQuickListView::setHeaderPositioning(QQuickListView::HeaderPositioning posi
The footer can be pushed away by moving the content backwards, and pulled back by
moving the content forwards.
\endlist
+
+ \note This property has no effect on the \l {QQuickItem::z}{stacking order}
+ of the footer. For example, if the footer should be shown above the
+ \l delegate items when using \c ListView.OverlayFooter, its Z value
+ should be set to a value higher than that of the delegates. For more
+ information, see \l {Stacking Order in ListView}.
*/
QQuickListView::FooterPositioning QQuickListView::footerPositioning() const
{
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index 5d5934bbd2..2c8f854d4d 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -270,6 +270,7 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
In some cases you may wish to use a Loader within a view delegate to improve delegate
loading performance. This works well in most cases, but there is one important issue to
+ be aware of related to the \l{QtQml::Component#Creation Context}{creation context} of a Component.
In the following example, the \c index context property inserted by the ListView into \c delegateComponent's
context will be inaccessible to Text, as the Loader will use the creation context of \c myComponent as the parent
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index d8bad7d793..96f34ef276 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -85,6 +85,7 @@ void QQuickMouseAreaPrivate::init()
{
Q_Q(QQuickMouseArea);
q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch
q->setFiltersChildMouseEvents(true);
if (qmlVisualTouchDebugging()) {
q->setFlag(QQuickItem::ItemHasContents);
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index d4c447a384..54136b1bbf 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -418,6 +418,7 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent)
if (qmlVisualTouchDebugging()) {
setFlag(QQuickItem::ItemHasContents);
}
+ setAcceptTouchEvents(true);
#ifdef Q_OS_OSX
setAcceptHoverEvents(true); // needed to enable touch events on mouse hover.
#endif
diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp
index 4fcfe04b55..c3e0ba05bd 100644
--- a/src/quick/items/qquickopenglshadereffect.cpp
+++ b/src/quick/items/qquickopenglshadereffect.cpp
@@ -230,7 +230,7 @@ void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item,
auto mapper = signalMappers[shaderType].at(i);
void *a = mapper;
QObjectPrivate::disconnect(item, mapper->signalIndex(), &a);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source) {
if (item->window())
@@ -272,7 +272,7 @@ void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item,
qWarning("QQuickOpenGLShaderEffect: '%s' does not have a matching property!", d.name.constData());
}
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source) {
if (item->window())
@@ -335,6 +335,7 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item,
Q_ASSERT(decl == UniformQualifier);
const int sampLen = sizeof("sampler2D") - 1;
+ const int sampExtLen = sizeof("samplerExternalOES") - 1;
const int opLen = sizeof("qt_Opacity") - 1;
const int matLen = sizeof("qt_Matrix") - 1;
const int srLen = sizeof("qt_SubRect_") - 1;
@@ -357,8 +358,12 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item,
mapper = new QtPrivate::MappedSlotObject([this, mappedId](){
this->mappedPropertyChanged(mappedId);
});
- bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
- d.specialType = sampler ? UniformData::Sampler : UniformData::None;
+ if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0)
+ d.specialType = UniformData::Sampler;
+ else if (typeLength == sampExtLen && qstrncmp("samplerExternalOES", s + typeIndex, sampExtLen) == 0)
+ d.specialType = UniformData::SamplerExternal;
+ else
+ d.specialType = UniformData::None;
d.setValueFromProperty(item, itemMetaObject);
}
uniformData[shaderType].append(d);
@@ -451,7 +456,8 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode
int textureProviderCount = 0;
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- if (uniformData[shaderType].at(i).specialType == UniformData::Sampler)
+ if (uniformData[shaderType].at(i).specialType == UniformData::Sampler ||
+ uniformData[shaderType].at(i).specialType == UniformData::SamplerExternal)
++textureProviderCount;
}
material->uniforms[shaderType] = uniformData[shaderType];
@@ -474,7 +480,7 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType != UniformData::Sampler)
+ if (d.specialType != UniformData::Sampler && d.specialType != UniformData::SamplerExternal)
continue;
QSGTextureProvider *oldProvider = material->textureProviders.at(index);
QSGTextureProvider *newProvider = 0;
@@ -513,7 +519,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source)
QQuickItemPrivate::get(source)->refWindow(window);
@@ -524,7 +530,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source)
QQuickItemPrivate::get(source)->derefWindow();
@@ -539,7 +545,7 @@ void QQuickOpenGLShaderEffectCommon::sourceDestroyed(QObject *object)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
UniformData &d = uniformData[shaderType][i];
- if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) {
+ if ((d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) && d.value.canConvert<QObject *>()) {
if (qvariant_cast<QObject *>(d.value) == object)
d.value = QVariant();
}
@@ -554,7 +560,7 @@ static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector<QQuickO
if (s == typeToSkip && i == indexToSkip)
continue;
const QQuickOpenGLShaderEffectMaterial::UniformData &d = uniformData[s][i];
- if (d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler && qvariant_cast<QObject *>(d.value) == source)
+ if ((d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler || d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::SamplerExternal) && qvariant_cast<QObject *>(d.value) == source)
return false;
}
}
@@ -568,7 +574,7 @@ void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item,
Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
int index = mappedId & 0xffff;
UniformData &d = uniformData[shaderType][index];
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source) {
if (item->window())
diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp
index e1ea98641d..5dbfee73cb 100644
--- a/src/quick/items/qquickopenglshadereffectnode.cpp
+++ b/src/quick/items/qquickopenglshadereffectnode.cpp
@@ -47,6 +47,10 @@
#include <QtCore/qmutex.h>
#include <QtGui/qopenglfunctions.h>
+#ifndef GL_TEXTURE_EXTERNAL_OES
+#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#endif
+
QT_BEGIN_NAMESPACE
static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProviders)
@@ -124,7 +128,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
const UniformData &d = material->uniforms[shaderType].at(i);
QByteArray name = d.name;
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
program()->setUniformValue(d.name.constData(), textureProviderIndex++);
// We don't need to store the sampler uniform locations, since their values
// only need to be set once. Look for the "qt_SubRect_" uniforms instead.
@@ -143,7 +147,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
const UniformData &d = material->uniforms[shaderType].at(i);
int loc = m_uniformLocs[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
int idx = textureProviderIndex++;
functions->glActiveTexture(GL_TEXTURE0 + idx);
if (QSGTextureProvider *provider = material->textureProviders.at(idx)) {
@@ -164,7 +168,10 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
continue;
}
}
- functions->glBindTexture(GL_TEXTURE_2D, 0);
+ if (d.specialType == UniformData::Sampler)
+ functions->glBindTexture(GL_TEXTURE_2D, 0);
+ else if (d.specialType == UniformData::SamplerExternal)
+ functions->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
} else if (d.specialType == UniformData::Opacity) {
program()->setUniformValue(loc, state.opacity());
} else if (d.specialType == UniformData::Matrix) {
@@ -396,7 +403,7 @@ bool QQuickOpenGLShaderEffectMaterial::UniformData::operator == (const UniformDa
if (name != other.name)
return false;
- if (specialType == UniformData::Sampler) {
+ if (specialType == UniformData::Sampler || specialType == UniformData::SamplerExternal) {
// We can't check the source objects as these live in the GUI thread,
// so return true here and rely on the textureProvider check for
// equality of these..
diff --git a/src/quick/items/qquickopenglshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h
index aea28e6612..784294d9eb 100644
--- a/src/quick/items/qquickopenglshadereffectnode_p.h
+++ b/src/quick/items/qquickopenglshadereffectnode_p.h
@@ -90,7 +90,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectMaterial : public QSGMateri
public:
struct UniformData
{
- enum SpecialType { None, Sampler, SubRect, Opacity, Matrix };
+ enum SpecialType { None, Sampler, SamplerExternal, SubRect, Opacity, Matrix };
QByteArray name;
QVariant value;
diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp
index 3f6ce7b8ba..476acd3a3e 100644
--- a/src/quick/items/qquickpincharea.cpp
+++ b/src/quick/items/qquickpincharea.cpp
@@ -288,6 +288,7 @@ QQuickPinchArea::QQuickPinchArea(QQuickItem *parent)
{
Q_D(QQuickPinchArea);
d->init();
+ setAcceptTouchEvents(true);
#ifdef Q_OS_OSX
setAcceptHoverEvents(true); // needed to enable touch events on mouse hover.
#endif
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index 65284eb532..9308553a79 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -325,6 +325,9 @@ QQuickRectangle::QQuickRectangle(QQuickItem *parent)
: QQuickItem(*(new QQuickRectanglePrivate), parent)
{
setFlag(ItemHasContents);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ setAcceptTouchEvents(false);
+#endif
}
void QQuickRectangle::doUpdate()
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index f2828bbedd..7e995936af 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -284,6 +284,7 @@ bool QQuickRenderControl::sync()
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
cd->syncSceneGraph();
+ d->rc->endSync();
// TODO: find out if the sync actually caused a scenegraph update.
return true;
@@ -383,6 +384,7 @@ QImage QQuickRenderControl::grab()
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
cd->polishItems();
cd->syncSceneGraph();
+ d->rc->endSync();
render();
grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false);
if (QQuickRenderControl::renderWindowFor(d->window)) {
@@ -402,6 +404,7 @@ QImage QQuickRenderControl::grab()
softwareRenderer->markDirty();
cd->polishItems();
cd->syncSceneGraph();
+ d->rc->endSync();
render();
softwareRenderer->setCurrentPaintDevice(prevDev);
}
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 080cc9412e..5ca0bb5f79 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -269,9 +269,6 @@ void QQuickTextPrivate::updateLayout()
formatModifiesFontSize = fontSizeModified;
multilengthEos = -1;
} else {
- layout.clearFormats();
- if (elideLayout)
- elideLayout->clearFormats();
QString tmp = text;
multilengthEos = tmp.indexOf(QLatin1Char('\x9c'));
if (multilengthEos != -1)
@@ -632,6 +629,13 @@ QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QT
}
}
+void QQuickTextPrivate::clearFormats()
+{
+ layout.clearFormats();
+ if (elideLayout)
+ elideLayout->clearFormats();
+}
+
/*!
Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
@@ -1055,12 +1059,15 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (eos != multilengthEos)
truncated = true;
+ assignedFont = QFontInfo(font).family();
+
if (elide) {
if (!elideLayout) {
elideLayout = new QTextLayout;
elideLayout->setCacheEnabled(true);
}
- if (styledText) {
+ QTextEngine *engine = layout.engine();
+ if (engine && engine->hasFormats()) {
QVector<QTextLayout::FormatRange> formats;
switch (elideMode) {
case QQuickText::ElideRight:
@@ -1499,6 +1506,19 @@ QQuickText::~QQuickText()
Text { text: "Hello"; renderType: Text.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
\endqml
*/
+
+/*!
+ \qmlproperty bool QtQuick::Text::font.kerning
+ \since 5.10
+
+ Enables or disables the kerning OpenType feature when shaping the text. This may improve performance
+ when creating or changing the text, at the expense of some cosmetic features. The default value
+ is true.
+
+ \qml
+ Text { text: "OATS FLAVOUR WAY"; font.kerning: false }
+ \endqml
+*/
QFont QQuickText::font() const
{
Q_D(const QQuickText);
@@ -1599,6 +1619,7 @@ void QQuickText::setText(const QString &n)
d->extra->doc->setText(n);
d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
} else {
+ d->clearFormats();
d->rightToLeftText = d->text.isRightToLeft();
}
d->determineHorizontalAlignment();
@@ -2089,6 +2110,7 @@ void QQuickText::setTextFormat(TextFormat format)
d->extra->doc->setText(d->text);
d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
} else {
+ d->clearFormats();
d->rightToLeftText = d->text.isRightToLeft();
d->textHasChanged = true;
}
@@ -2394,6 +2416,12 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
void QQuickText::updatePolish()
{
Q_D(QQuickText);
+ // If the fonts used for rendering are different from the ones used in the GUI thread,
+ // it means we will get warnings and corrupted text. If this case is detected, we need
+ // to update the text layout before creating the scenegraph nodes.
+ if (!d->assignedFont.isEmpty() && QFontInfo(d->font).family() != d->assignedFont)
+ d->polishSize = true;
+
if (d->polishSize) {
d->updateSize();
d->polishSize = false;
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index fde07eaf2e..87f5162384 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -85,6 +85,7 @@ public:
int lineHeightOffset() const;
QString elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine = 0) const;
void elideFormats(int start, int length, int offset, QVector<QTextLayout::FormatRange> *elidedFormats);
+ void clearFormats();
void processHoverEvent(QHoverEvent *event);
@@ -153,6 +154,8 @@ public:
QQuickText::RenderType renderType;
UpdateType updateType;
+ QString assignedFont;
+
bool maximumLineCountValid:1;
bool updateOnComponentComplete:1;
bool richText:1;
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 61d610520f..f22c5f00be 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -356,6 +356,19 @@ QString QQuickTextEdit::text() const
*/
/*!
+ \qmlproperty bool QtQuick::TextEdit::font.kerning
+ \since 5.10
+
+ Enables or disables the kerning OpenType feature when shaping the text. This may improve performance
+ when creating or changing the text, at the expense of some cosmetic features. The default value
+ is true.
+
+ \qml
+ TextEdit { text: "OATS FLAVOUR WAY"; kerning: font.false }
+ \endqml
+*/
+
+/*!
\qmlproperty string QtQuick::TextEdit::text
The text to display. If the text format is AutoText the text edit will
@@ -3038,6 +3051,32 @@ void QQuickTextEdit::resetBottomPadding()
}
/*!
+ \qmlproperty real QtQuick::TextEdit::tabStopDistance
+ \since 5.10
+
+ The default distance, in device units, between tab stops.
+
+ \sa QTextOption::setTabStop()
+*/
+int QQuickTextEdit::tabStopDistance() const
+{
+ Q_D(const QQuickTextEdit);
+ return d->document->defaultTextOption().tabStop();
+}
+
+void QQuickTextEdit::setTabStopDistance(qreal distance)
+{
+ Q_D(QQuickTextEdit);
+ QTextOption textOptions = d->document->defaultTextOption();
+ if (textOptions.tabStop() == distance)
+ return;
+
+ textOptions.setTabStop(distance);
+ d->document->setDefaultTextOption(textOptions);
+ emit tabStopDistanceChanged(distance);
+}
+
+/*!
\qmlmethod QtQuick::TextEdit::clear()
\since 5.7
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index c8d3515be1..23033edb88 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -111,6 +111,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem
Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding RESET resetRightPadding NOTIFY rightPaddingChanged REVISION 6)
Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged REVISION 6)
Q_PROPERTY(QString preeditText READ preeditText NOTIFY preeditTextChanged REVISION 7)
+ Q_PROPERTY(qreal tabStopDistance READ tabStopDistance WRITE setTabStopDistance NOTIFY tabStopDistanceChanged REVISION 10)
public:
QQuickTextEdit(QQuickItem *parent=0);
@@ -296,6 +297,9 @@ public:
void setBottomPadding(qreal padding);
void resetBottomPadding();
+ int tabStopDistance() const;
+ void setTabStopDistance(qreal distance);
+
Q_SIGNALS:
void textChanged();
Q_REVISION(7) void preeditTextChanged();
@@ -340,6 +344,7 @@ Q_SIGNALS:
Q_REVISION(6) void leftPaddingChanged();
Q_REVISION(6) void rightPaddingChanged();
Q_REVISION(6) void bottomPaddingChanged();
+ Q_REVISION(10) void tabStopDistanceChanged(qreal distance);
public Q_SLOTS:
void selectAll();
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index a378359c95..06ad0a9dbf 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -378,6 +378,19 @@ QString QQuickTextInputPrivate::realText() const
TextInput { text: "Hello"; renderType: TextInput.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
\endqml
*/
+
+/*!
+ \qmlproperty bool QtQuick::TextInput::font.kerning
+ \since 5.10
+
+ Enables or disables the kerning OpenType feature when shaping the text. This may improve performance
+ when creating or changing the text, at the expense of some cosmetic features. The default value
+ is true.
+
+ \qml
+ TextInput { text: "OATS FLAVOUR WAY"; font.kerning: false }
+ \endqml
+*/
QFont QQuickTextInput::font() const
{
Q_D(const QQuickTextInput);
@@ -3407,10 +3420,10 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
if (!event->commitString().isEmpty()) {
internalInsert(event->commitString());
cursorPositionChanged = true;
+ } else {
+ m_cursor = qBound(0, c, m_text.length());
}
- m_cursor = qBound(0, c, m_text.length());
-
for (int i = 0; i < event->attributes().size(); ++i) {
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
if (a.type == QInputMethodEvent::Selection) {
@@ -3545,18 +3558,23 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
#endif
if (m_maskData) {
+ m_validInput = true;
if (m_text.length() != m_maxLength) {
+ m_validInput = false;
m_acceptableInput = false;
} else {
for (int i = 0; i < m_maxLength; ++i) {
if (m_maskData[i].separator) {
if (m_text.at(i) != m_maskData[i].maskChar) {
+ m_validInput = false;
m_acceptableInput = false;
break;
}
} else {
if (!isValidInput(m_text.at(i), m_maskData[i].maskChar)) {
m_acceptableInput = false;
+ if (m_text.at(i) != m_blank)
+ m_validInput = false;
break;
}
}
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 2cb518d691..7b331251f0 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -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>
@@ -432,10 +433,8 @@ void QQuickWindowPrivate::syncSceneGraph()
emit q->afterSynchronizing();
runAndClearJobs(&afterSynchronizingJobs);
- context->endSync();
}
-
void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
{
QML_MEMORY_SCOPE_STRING("SceneGraph");
@@ -501,6 +500,8 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, persistentSceneGraph(true)
, lastWheelEventAccepted(false)
, componentCompleted(true)
+ , allowChildEventFiltering(true)
+ , allowDoubleClick(true)
, lastFocusReason(Qt::OtherFocusReason)
, renderTarget(0)
, renderTargetId(0)
@@ -654,7 +655,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve
if (!item->contains(pos))
break;
- qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item;
+ qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << item;
QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event.data(), item, false));
// Send a single press and see if that's accepted
@@ -666,7 +667,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.data(), item, false));
@@ -690,7 +691,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve
QCoreApplication::sendEvent(item, me.data());
event->setAccepted(me->isAccepted());
if (me->isAccepted()) {
- qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem;
+ qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << mouseGrabberItem;
}
return event->isAccepted();
} else {
@@ -746,39 +747,42 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber)
if (q->mouseGrabberItem() == grabber)
return;
- qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber;
QQuickItem *oldGrabber = q->mouseGrabberItem();
- bool fromTouch = false;
+ qCDebug(DBG_MOUSE_TARGET) << "grabber" << oldGrabber << "->" << grabber;
if (grabber && touchMouseId != -1 && touchMouseDevice) {
// update the touch item for mouse touch id to the new grabber
qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem();
auto point = pointerEventInstance(touchMouseDevice)->pointById(touchMouseId);
- if (point)
- point->setGrabber(grabber);
- fromTouch = true;
+ if (point) {
+ auto originalEvent = pointerEventInstance(point->pointerEvent()->device());
+ for (int i = 0; i < originalEvent->pointCount(); ++i)
+ originalEvent->point(i)->cancelExclusiveGrab();
+ point->setGrabberItem(grabber);
+ for (auto handler : point->passiveGrabbers())
+ point->cancelPassiveGrab(handler);
+ }
} else {
QQuickPointerEvent *event = pointerEventInstance(QQuickPointerDevice::genericMouseDevice());
Q_ASSERT(event->pointCount() == 1);
- event->point(0)->setGrabber(grabber);
+ auto point = event->point(0);
+ point->setGrabberItem(grabber);
+ for (auto handler : point->passiveGrabbers())
+ point->cancelPassiveGrab(handler);
}
+
if (oldGrabber) {
QEvent e(QEvent::UngrabMouse);
QSet<QQuickItem *> hasFiltered;
- if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) {
+ if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered))
oldGrabber->mouseUngrabEvent();
- if (fromTouch)
- oldGrabber->touchUngrabEvent();
- }
}
}
-void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids)
+void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids)
{
- QSet<QQuickItem*> ungrab;
for (int i = 0; i < ids.count(); ++i) {
- // FIXME: deprecate this function, we need a device
int id = ids.at(i);
if (Q_UNLIKELY(id < 0)) {
qWarning("ignoring grab of touchpoint %d", id);
@@ -786,11 +790,11 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int
}
if (id == touchMouseId) {
auto point = pointerEventInstance(touchMouseDevice)->pointById(id);
- auto touchMouseGrabber = point->grabber();
+ auto touchMouseGrabber = point->grabberItem();
if (touchMouseGrabber) {
- point->setGrabber(nullptr);
+ point->setExclusiveGrabber(nullptr);
touchMouseGrabber->mouseUngrabEvent();
- ungrab.insert(touchMouseGrabber);
+ touchMouseGrabber->touchUngrabEvent();
touchMouseDevice = nullptr;
touchMouseId = -1;
}
@@ -802,19 +806,23 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int
auto point = pointerEventInstance(device)->pointById(id);
if (!point)
continue;
- QQuickItem *oldGrabber = point->grabber();
+ QObject *oldGrabber = point->exclusiveGrabber();
if (oldGrabber == grabber)
continue;
-
- point->setGrabber(grabber);
- if (oldGrabber)
- ungrab.insert(oldGrabber);
+ point->setExclusiveGrabber(grabber);
}
}
- for (QQuickItem *oldGrabber : qAsConst(ungrab))
- oldGrabber->touchUngrabEvent();
}
+/*!
+ Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber.
+ This should not be called when processing a release event - that's redundant.
+ It is called in other cases, when the points may not be released, but the item
+ nevertheless must lose its grab due to becoming disabled, invisible, etc.
+ QQuickEventPoint::setGrabberItem() calls touchUngrabEvent() when all points are released,
+ but if not all points are released, it cannot be sure whether to call touchUngrabEvent()
+ or not; so we have to do it here.
+*/
void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch)
{
Q_Q(QQuickWindow);
@@ -823,17 +831,19 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to
setMouseGrabber(nullptr);
}
if (Q_LIKELY(touch)) {
+ bool ungrab = false;
const auto touchDevices = QQuickPointerDevice::touchDevices();
for (auto device : touchDevices) {
auto pointerEvent = pointerEventInstance(device);
for (int i = 0; i < pointerEvent->pointCount(); ++i) {
- if (pointerEvent->point(i)->grabber() == grabber) {
- pointerEvent->point(i)->setGrabber(nullptr);
- // FIXME send ungrab event only once
- grabber->touchUngrabEvent();
+ if (pointerEvent->point(i)->exclusiveGrabber() == grabber) {
+ pointerEvent->point(i)->setGrabberItem(nullptr);
+ ungrab = true;
}
}
}
+ if (ungrab)
+ grabber->touchUngrabEvent();
}
}
@@ -1497,12 +1507,12 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const
QQuickPointerEvent *event = d->pointerEventInstance(d->touchMouseDevice);
auto point = event->pointById(d->touchMouseId);
Q_ASSERT(point);
- return point->grabber();
+ return point->grabberItem();
}
QQuickPointerEvent *event = d->pointerEventInstance(QQuickPointerDevice::genericMouseDevice());
Q_ASSERT(event->pointCount());
- return event->point(0)->grabber();
+ return event->point(0)->grabberItem();
}
@@ -1651,36 +1661,75 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t
void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent)
{
- Q_Q(QQuickWindow);
auto point = pointerEvent->point(0);
lastMousePosition = point->scenePos();
- QQuickItem *grabber = point->grabber();
- 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
- if (pointerEvent->button() != Qt::NoButton
- && grabber->acceptedMouseButtons()
- && !(grabber->acceptedMouseButtons() & pointerEvent->button())) {
- pointerEvent->setAccepted(false);
- return;
- }
- // send update
- QPointF localPos = grabber->mapFromScene(lastMousePosition);
- auto me = pointerEvent->asMouseEvent(localPos);
- me->accept();
- q->sendEvent(grabber, me);
- point->setAccepted(me->isAccepted());
+ if (point->exclusiveGrabber()) {
+ bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton);
+ if (auto grabber = point->grabberItem()) {
+ if (sendFilteredPointerEvent(pointerEvent, grabber))
+ return;
+ // if the grabber is an Item:
+ // if the update consists of changing button state, don't accept it unless
+ // the button is one in which the grabber is interested
+ if (pointerEvent->button() != Qt::NoButton && grabber->acceptedMouseButtons()
+ && !(grabber->acceptedMouseButtons() & pointerEvent->button())) {
+ pointerEvent->setAccepted(false);
+ return;
+ }
- // release event, make sure to ungrab if there still is a grabber
- if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem())
- q->mouseGrabberItem()->ungrabMouse();
+ // send update
+ QPointF localPos = grabber->mapFromScene(lastMousePosition);
+ auto me = pointerEvent->asMouseEvent(localPos);
+ me->accept();
+ QCoreApplication::sendEvent(grabber, me);
+ point->setAccepted(me->isAccepted());
+
+ // release event: ungrab if no buttons are pressed anymore
+ if (mouseIsReleased)
+ setMouseGrabber(nullptr);
+ } else {
+ // if the grabber is not an Item, it must be a PointerHandler
+ auto handler = point->grabberPointerHandler();
+ pointerEvent->localize(handler->parentItem());
+ if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem()))
+ handler->handlePointerEvent(pointerEvent);
+ if (mouseIsReleased) {
+ point->setGrabberPointerHandler(nullptr, true);
+ point->clearPassiveGrabbers();
+ }
+ }
} else {
- // send initial press
bool delivered = false;
if (pointerEvent->isPressEvent()) {
- QSet<QQuickItem*> hasFiltered;
- delivered = deliverPressEvent(pointerEvent, &hasFiltered);
+ // send initial press
+ delivered = deliverPressOrReleaseEvent(pointerEvent);
+ } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) {
+ // if this is an update or release from an actual mouse,
+ // and the point wasn't grabbed, deliver only to PointerHandlers:
+ // passive grabbers first, then the rest
+ const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets();
+ for (auto handler : point->passiveGrabbers()) {
+ // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
+ if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) {
+ if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem()))
+ handler->handlePointerEvent(pointerEvent);
+ }
+ }
+ // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
+ if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) {
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePos(), false, false);
+ for (QQuickItem *item : targetItems) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ pointerEvent->localize(item);
+ if (!sendFilteredPointerEvent(pointerEvent, item)) {
+ if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers
+ delivered = true;
+ }
+ if (point->exclusiveGrabber())
+ break;
+ }
+ }
}
if (!delivered)
@@ -1883,19 +1932,16 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
Q_Q(QQuickWindow);
// A TouchCancel event will typically not contain any points.
- // Deliver it to all items that have active touches.
+ // Deliver it to all items and handlers that have active touches.
QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::touchDevice(event->device()));
- QVector<QQuickItem *> grabbers = pointerEvent->grabbers();
-
- for (QQuickItem *grabber: qAsConst(grabbers)) {
- q->sendEvent(grabber, event);
- }
+ for (int i = 0; i < pointerEvent->pointCount(); ++i)
+ pointerEvent->point(i)->cancelExclusiveGrabImpl(event);
touchMouseId = -1;
touchMouseDevice = nullptr;
if (q->mouseGrabberItem())
q->mouseGrabberItem()->ungrabMouse();
- // The next touch event can only be a TouchBegin so clean up.
+ // The next touch event can only be a TouchBegin, so clean up.
pointerEvent->clearGrabbers();
return true;
}
@@ -2032,8 +2078,6 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
{
- Q_Q(QQuickWindow);
-
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
event->accept();
return;
@@ -2054,7 +2098,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
case QEvent::MouseButtonDblClick:
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
event->button(), event->buttons());
- deliverPointerEvent(pointerEventInstance(event));
+ if (allowDoubleClick)
+ deliverPointerEvent(pointerEventInstance(event));
break;
case QEvent::MouseMove:
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
@@ -2066,7 +2111,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
updateCursor(event->windowPos());
#endif
- if (!q->mouseGrabberItem()) {
+ if (!pointerEventInstance(QQuickPointerDevice::genericMouseDevice())->point(0)->exclusiveGrabber()) {
QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition;
lastMousePosition = event->windowPos();
@@ -2077,7 +2122,6 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
accepted = clearHover(event->timestamp());
}
event->setAccepted(accepted);
- return;
}
deliverPointerEvent(pointerEventInstance(event));
break;
@@ -2197,7 +2241,10 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
// check if item or any of its child items contain the point
// FIXME: should this be iterative instead of recursive?
-QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons) const
+// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added.
+// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR
+// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't.
+QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons, bool checkAcceptsTouch) const
{
QVector<QQuickItem *> targets;
auto itemPrivate = QQuickItemPrivate::get(item);
@@ -2215,13 +2262,16 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons
auto childPrivate = QQuickItemPrivate::get(child);
if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
continue;
- targets << pointerTargets(child, scenePos, false);
+ targets << pointerTargets(child, scenePos, checkMouseButtons, checkAcceptsTouch);
}
- if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) {
- // add this item last - children take precedence
- targets << item;
- }
+ bool relevant = item->contains(itemPos);
+ if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
+ relevant = false;
+ if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
+ relevant = false;
+ if (relevant)
+ targets << item; // add this item last: children take precedence
return targets;
}
@@ -2251,11 +2301,12 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
{
qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent();
- QSet<QQuickItem *> hasFiltered;
if (event->isPressEvent())
- deliverPressEvent(event, &hasFiltered);
- if (!event->allPointsAccepted())
- deliverUpdatedTouchPoints(event, &hasFiltered);
+ deliverPressOrReleaseEvent(event);
+ if (!event->allUpdatedPointsAccepted())
+ deliverUpdatedTouchPoints(event);
+ if (event->isReleaseEvent())
+ deliverPressOrReleaseEvent(event, true);
// Remove released points from itemForTouchPointId
bool allReleased = true;
@@ -2265,7 +2316,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
if (point->state() == QQuickEventPoint::Released) {
int id = point->pointId();
qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << id << "released";
- point->setGrabber(nullptr);
+ point->setGrabberItem(nullptr);
if (id == touchMouseId) {
touchMouseId = -1;
touchMouseDevice = nullptr;
@@ -2275,29 +2326,92 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
}
}
- if (allReleased && !event->grabbers().isEmpty()) {
- qWarning() << "No release received for some grabbers" << event->grabbers();
+ if (allReleased && !event->exclusiveGrabbers().isEmpty()) {
+ qWarning() << "No release received for some grabbers" << event->exclusiveGrabbers();
event->clearGrabbers();
}
}
// Deliver touch points to existing grabbers
-bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
-{
- const auto grabbers = event->grabbers();
- for (auto grabber : grabbers)
- deliverMatchingPointsToItem(grabber, event, hasFiltered);
+void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event)
+{
+ const auto grabbers = event->exclusiveGrabbers();
+ for (auto grabber : grabbers) {
+ // The grabber is guaranteed to be either an item or a handler.
+ QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber);
+ if (!receiver) {
+ // The grabber is not an item? It's a handler then. Let it have the event first.
+ QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber);
+ receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem();
+ if (sendFilteredPointerEvent(event, receiver))
+ return;
+ event->localize(receiver);
+ handler->handlePointerEvent(event);
+ if (event->allPointsAccepted())
+ return;
+ }
+ // If the grabber is an item or the grabbing handler didn't handle it,
+ // then deliver the event to the item (which may have multiple handlers).
+ deliverMatchingPointsToItem(receiver, event);
+ }
- return false;
+ // If some points weren't grabbed, deliver only to non-grabber PointerHandlers
+ if (!event->allPointsGrabbed()) {
+ int pointCount = event->pointCount();
+
+ // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once)
+ const QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets();
+ for (int i = 0; i < pointCount; ++i) {
+ QQuickEventPoint *point = event->point(i);
+ for (auto handler : point->passiveGrabbers()) {
+ if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) {
+ if (sendFilteredPointerEvent(event, handler->parentItem()))
+ return;
+ handler->handlePointerEvent(event);
+ }
+ }
+ }
+
+ // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
+ if (!event->allPointsGrabbed()) {
+ QVector<QQuickItem *> targetItems;
+ for (int i = 0; i < pointCount; ++i) {
+ QQuickEventPoint *point = event->point(i);
+ if (point->state() == QQuickEventPoint::Pressed)
+ continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false, false);
+ if (targetItems.count()) {
+ targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
+ } else {
+ targetItems = targetItemsForPoint;
+ }
+ }
+
+ for (QQuickItem *item: targetItems) {
+ if (grabbers.contains(item))
+ continue;
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ event->localize(item);
+ itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers
+ if (event->allPointsGrabbed())
+ break;
+ }
+ }
+ }
}
-// Deliver newly pressed touch points
-bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQuickItem *> *hasFiltered)
+// Deliver an event containing newly pressed or released touch points
+bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly)
{
- const QVector<QPointF> points = event->unacceptedPressedPointScenePositions();
+ int pointCount = event->pointCount();
QVector<QQuickItem *> targetItems;
- for (QPointF point: points) {
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false);
+ bool isTouchEvent = (event->asPointerTouchEvent() != nullptr);
+ for (int i = 0; i < pointCount; ++i) {
+ auto point = event->point(i);
+ point->setAccepted(false); // because otherwise touchEventForItem will ignore it
+ if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released)
+ point->setGrabberPointerHandler(nullptr, true);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), !isTouchEvent, isTouchEvent);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2305,8 +2419,18 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui
}
}
+ if (allowChildEventFiltering && !handlersOnly) {
+ updateFilteringParentItems(targetItems);
+ QQuickItem *filteredItem;
+ if (sendFilteredPointerEvent(event, nullptr, &filteredItem)) {
+ if (event->isAccepted())
+ return true;
+ targetItems.removeAll(filteredItem);
+ }
+ }
+
for (QQuickItem *item: targetItems) {
- deliverMatchingPointsToItem(item, event, hasFiltered);
+ deliverMatchingPointsToItem(item, event, handlersOnly);
if (event->allPointsAccepted())
break;
}
@@ -2314,17 +2438,35 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui
return event->allPointsAccepted();
}
-bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem *> *hasFiltered)
+void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly)
{
Q_Q(QQuickWindow);
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ pointerEvent->localize(item);
+
+ // Let the Item's handlers (if any) have the event first.
+ // However, double click should never be delivered to handlers.
+ if (!pointerEvent->isDoubleClickEvent()) {
+ itemPrivate->handlePointerEvent(pointerEvent);
+ allowDoubleClick = !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted());
+ }
+ if (handlersOnly)
+ return;
+ if (pointerEvent->allPointsAccepted() && !pointerEvent->isReleaseEvent())
+ return;
+
+ // If all points are released and the item is not the grabber, it doesn't get the event.
+ // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario.
+ if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent()
+ && !pointerEvent->exclusiveGrabbers().contains(item))
+ return;
// TODO: unite this mouse point delivery with the synthetic mouse event below
- if (auto event = pointerEvent->asPointerMouseEvent()) {
- if (item->acceptedMouseButtons() & event->button()) {
+ auto event = pointerEvent->asPointerMouseEvent();
+ if (event && item->acceptedMouseButtons() & event->button()) {
auto point = event->point(0);
if (point->isAccepted())
- return false;
-
+ return;
// The only reason to already have a mouse grabber here is
// synthetic events - flickable sends one when setPressDelay is used.
auto oldMouseGrabber = q->mouseGrabberItem();
@@ -2332,7 +2474,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
Q_ASSERT(item->contains(localPos)); // transform is checked already
QMouseEvent *me = event->asMouseEvent(localPos);
me->accept();
- q->sendEvent(item, me);
+ QCoreApplication::sendEvent(item, me);
if (me->isAccepted()) {
auto mouseGrabber = q->mouseGrabberItem();
if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
@@ -2342,33 +2484,25 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
}
point->setAccepted(true);
}
- return me->isAccepted();
- }
- return false;
+ return;
}
- QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent();
- if (!event)
- return false;
+ QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent();
+ if (!ptEvent)
+ return;
- QScopedPointer<QTouchEvent> touchEvent(event->touchEventForItem(item));
+ QScopedPointer<QTouchEvent> touchEvent(ptEvent->touchEventForItem(item));
if (!touchEvent)
- return false;
+ return;
- qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item;
+ qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item;
bool eventAccepted = false;
- // First check whether the parent wants to be a filter,
- // and if the parent accepts the event we are done.
- if (sendFilteredTouchEvent(item->parentItem(), item, event, hasFiltered)) {
- // If the touch was accepted (regardless by whom or in what form),
- // update acceptedNewPoints
- qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item;
- for (auto point: qAsConst(touchEvent->touchPoints())) {
- event->pointById(point.id())->setAccepted();
- }
- return true;
- }
+ // If any parent filters the event, we're done.
+ // updateFilteringParentItems was called when the press occurred,
+ // and we assume that the filtering relationships don't change between press and release.
+ if (sendFilteredPointerEvent(pointerEvent, item))
+ return;
// Deliver the touch event to the given item
qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item;
@@ -2376,36 +2510,34 @@ 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))
+ if (deliverTouchAsMouse(item, ptEvent))
eventAccepted = true;
}
if (eventAccepted) {
// If the touch was accepted (regardless by whom or in what form),
// update accepted new points.
+ bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent();
for (auto point: qAsConst(touchEvent->touchPoints())) {
- auto pointerEventPoint = event->pointById(point.id());
+ auto pointerEventPoint = ptEvent->pointById(point.id());
pointerEventPoint->setAccepted();
- if (point.state() == Qt::TouchPointPressed)
- pointerEventPoint->setGrabber(item);
+ if (isPressOrRelease)
+ pointerEventPoint->setGrabberItem(item);
}
} else {
// But if the event was not accepted then we know this item
// will not be interested in further updates for those touchpoint IDs either.
for (auto point: qAsConst(touchEvent->touchPoints())) {
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);
+ if (ptEvent->pointById(point.id())->exclusiveGrabber() == item) {
+ qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << point.id() << "disassociated";
+ ptEvent->pointById(point.id())->setGrabberItem(nullptr);
}
}
}
}
-
- return eventAccepted;
}
#if QT_CONFIG(draganddrop)
@@ -2580,38 +2712,86 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
}
#endif
-bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
+void QQuickWindowPrivate::updateFilteringParentItems(const QVector<QQuickItem *> &targetItems)
{
- Q_Q(QQuickWindow);
+ if (Q_UNLIKELY(DBG_MOUSE_TARGET().isDebugEnabled())) {
+ // qDebug() << map(&objectName, targetItems) but done the hard way because C++ is still that primitive
+ QStringList targetNames;
+ for (QQuickItem *t : targetItems)
+ targetNames << (QLatin1String(t->metaObject()->className()) + QLatin1Char(' ') + t->objectName());
+ qCDebug(DBG_MOUSE_TARGET) << "finding potential filtering parents of" << targetNames;
+ }
+ filteringParentItems.clear();
+ for (QQuickItem *item : targetItems) {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ bool acceptsTouchEvents = item->acceptTouchEvents();
+#else
+ // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true.
+ bool acceptsTouchEvents = false;
+#endif
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
+ // If the item neither handles events nor has handlers which do, then it will never be a receiver, so filtering is irrelevant.
+ if (!item->acceptedMouseButtons() && !acceptsTouchEvents &&
+ !(itemPriv->extra.isAllocated() && !itemPriv->extra->pointerHandlers.isEmpty()))
+ continue;
+ QQuickItem *parent = item->parentItem();
+ while (parent) {
+ if (parent->filtersChildMouseEvents()) {
+ bool foundParent = false;
+ for (const QPair<QQuickItem*,QQuickItem*> existingItemAndParent : filteringParentItems)
+ if (existingItemAndParent.second == parent)
+ foundParent = true;
+ if (!foundParent)
+ filteringParentItems.append(QPair<QQuickItem*,QQuickItem*>(item, parent));
+ }
+ parent = parent->parentItem();
+ }
+ }
+}
- if (!target)
+bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem **itemThatFiltered)
+{
+ if (!allowChildEventFiltering)
return false;
-
- bool filtered = false;
-
- QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
- if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) {
- hasFiltered->insert(target);
- QScopedPointer<QTouchEvent> targetEvent(event->touchEventForItem(target, true));
- if (targetEvent) {
- if (target->childMouseEventFilter(item, targetEvent.data())) {
- qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target;
- QVector<int> touchIds;
- const int touchPointCount = targetEvent->touchPoints().size();
- touchIds.reserve(touchPointCount);
- for (int i = 0; i < touchPointCount; ++i)
- touchIds.append(targetEvent->touchPoints().at(i).id());
- target->grabTouchPoints(touchIds);
- if (q->mouseGrabberItem()) {
- q->mouseGrabberItem()->ungrabMouse();
- touchMouseId = -1;
- touchMouseDevice = nullptr;
- }
- filtered = true;
+ bool ret = false;
+ if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) {
+ for (QPair<QQuickItem *,QQuickItem *> itemAndParent : filteringParentItems) {
+ QQuickItem *item = receiver ? receiver : itemAndParent.first;
+ QQuickItem *filteringParent = itemAndParent.second;
+ if (item == filteringParent)
+ continue; // a filtering item never needs to filter for itself
+ QPointF localPos = item->mapFromScene(pme->point(0)->scenePos());
+ QMouseEvent *me = pme->asMouseEvent(localPos);
+ if (filteringParent->childMouseEventFilter(item, me)) {
+ if (itemThatFiltered) *itemThatFiltered = item;
+ ret = true;
}
-
- for (int i = 0; i < targetEvent->touchPoints().size(); ++i) {
- const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().at(i);
+ }
+ } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) {
+ QVarLengthArray<QPair<QQuickPointerHandler *, QQuickEventPoint *>, 32> passiveGrabsToCancel;
+ for (QPair<QQuickItem *,QQuickItem *> itemAndParent : filteringParentItems) {
+ QQuickItem *item = receiver ? receiver : itemAndParent.first;
+ if (item != itemAndParent.first)
+ continue;
+ if (!item->acceptTouchEvents() && !item->acceptedMouseButtons())
+ continue; // if this item won't accept, parents don't need to filter the touch for it
+ QQuickItem *filteringParent = itemAndParent.second;
+ if (item == filteringParent)
+ continue; // a filtering item never needs to filter for itself
+ // get a touch event customized for delivery to filteringParent
+ QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(filteringParent, true));
+ if (!filteringParentTouchEvent)
+ continue; // all points are stationary, or no touchpoints inside that parent
+ if (filteringParent->childMouseEventFilter(item, filteringParentTouchEvent.data())) {
+ qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
+ for (auto point: qAsConst(filteringParentTouchEvent->touchPoints()))
+ event->pointById(point.id())->setAccepted();
+ ret = true;
+ break;
+ }
+ // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event.
+ for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) {
+ const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i);
QEvent::Type t;
switch (tp.state()) {
@@ -2631,29 +2811,29 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
bool touchMouseUnset = (touchMouseId == -1);
// Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
if (touchMouseUnset || touchMouseId == tp.id()) {
- // targetEvent is already transformed wrt local position, velocity, etc.
-
- // FIXME: remove asTouchEvent!!!
- QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false));
+ // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.)
+ // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that
+ QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), item, false));
// If a filtering item calls QQuickWindow::mouseGrabberItem(), it should
// report the touchpoint's grabber. Whenever we send a synthetic mouse event,
// touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed.
touchMouseId = tp.id();
touchMouseDevice = event->device();
- if (target->childMouseEventFilter(item, mouseEvent.data())) {
- qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target;
+ if (filteringParent->childMouseEventFilter(item, mouseEvent.data())) {
+ qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent;
if (t != QEvent::MouseButtonRelease) {
- qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target;
- if (touchMouseUnset) {
- // the point was grabbed as a pure touch point before, now it will be treated as mouse
- // but the old receiver still needs to be informed
- if (auto oldGrabber = pointerEventInstance(touchMouseDevice)->pointById(tp.id())->grabber())
- oldGrabber->touchUngrabEvent();
- }
+ qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << tp.id() << "->" << filteringParent;
+ pointerEventInstance(touchMouseDevice)->pointById(tp.id())->setGrabberItem(filteringParent);
touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
- target->grabMouse();
+ filteringParent->grabMouse();
+ auto pointerEventPoint = pte->pointById(tp.id());
+ for (auto handler : pointerEventPoint->passiveGrabbers()) {
+ QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint);
+ if (!passiveGrabsToCancel.contains(grab))
+ passiveGrabsToCancel.append(grab);
+ }
}
- filtered = true;
+ ret = true;
}
if (touchMouseUnset) {
// Now that we're done sending a synth mouse event, and it wasn't grabbed,
@@ -2661,14 +2841,16 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
touchMouseId = -1;
touchMouseDevice = nullptr;
}
- // Only one event can be filtered as a mouse event.
- break;
+ // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter
+ // has been called once, we're done with this loop over the touchpoints.
+ break;
}
}
}
+ for (auto grab : passiveGrabsToCancel)
+ grab.second->cancelPassiveGrab(grab.first);
}
-
- return sendFilteredTouchEvent(target->parentItem(), item, event, hasFiltered) || filtered;
+ return ret;
}
bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event, QSet<QQuickItem *> *hasFiltered)
@@ -3434,7 +3616,7 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
{
Q_D(QQuickWindow);
if (d->context && QThread::currentThread() != d->context->thread()) {
- qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
+ qWarning("QQuickWindow::setRenderTarget: Cannot set render target from outside the rendering thread");
return;
}
@@ -4096,6 +4278,8 @@ void QQuickWindow::resetOpenGLState()
The flags which you read from this property might differ from the ones
that you set if the requested flags could not be fulfilled.
+
+ \sa Qt::WindowFlags
*/
/*!
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index b3ff5a2b35..14564a7f55 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -143,11 +143,12 @@ 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);
bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *);
+ bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem **itemThatFiltered = 0);
#if QT_CONFIG(wheelevent)
bool deliverWheelEvent(QQuickItem *, QWheelEvent *);
#endif
@@ -171,13 +172,13 @@ public:
void deliverPointerEvent(QQuickPointerEvent *);
void deliverTouchEvent(QQuickPointerTouchEvent *);
bool deliverTouchCancelEvent(QTouchEvent *);
- bool deliverPressEvent(QQuickPointerEvent *, QSet<QQuickItem *> *);
- bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered);
- bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem*> *filtered);
- bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem*> *filtered);
+ bool deliverPressOrReleaseEvent(QQuickPointerEvent *, bool handlersOnly = false);
+ void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event);
+ void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false);
- QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const;
+ QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const;
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
+ void updateFilteringParentItems(const QVector<QQuickItem *> &targetItems);
// hover delivery
bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted);
@@ -226,6 +227,7 @@ public:
QList<QSGNode *> cleanupNodeList;
QVector<QQuickItem *> itemsToPolish;
+ QVector<QPair<QQuickItem *,QQuickItem *> > filteringParentItems; // item:parent pairs
qreal devicePixelRatio;
QMetaObject::Connection physicalDpiChangedConnection;
@@ -263,6 +265,9 @@ public:
uint lastWheelEventAccepted : 1;
bool componentCompleted : 1;
+ bool allowChildEventFiltering : 1;
+ bool allowDoubleClick : 1;
+
Qt::FocusReason lastFocusReason;
QOpenGLFramebufferObject *renderTarget;
@@ -290,6 +295,14 @@ public:
return overThreshold;
}
+ static bool dragOverThreshold(const QQuickEventPoint *point)
+ {
+ QPointF delta = point->scenePos() - point->scenePressPos();
+ return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) ||
+ QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point));
+ }
+
+
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
static int data_count(QQmlListProperty<QObject> *);
diff --git a/src/quick/items/qquickwindowattached_p.h b/src/quick/items/qquickwindowattached_p.h
index 3212508fd8..191f22137c 100644
--- a/src/quick/items/qquickwindowattached_p.h
+++ b/src/quick/items/qquickwindowattached_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
#include <qqml.h>
#include <QWindow>
@@ -59,7 +60,7 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickWindow;
-class Q_AUTOTEST_EXPORT QQuickWindowAttached : public QObject
+class Q_QUICK_PRIVATE_EXPORT QQuickWindowAttached : public QObject
{
Q_OBJECT
diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp
index a7f45534c4..45e3f0004d 100644
--- a/src/quick/items/qquickwindowmodule.cpp
+++ b/src/quick/items/qquickwindowmodule.cpp
@@ -205,6 +205,4 @@ void QQuickWindowModule::defineModule()
QT_END_NAMESPACE
-QML_DECLARE_TYPEINFO(QQuickWindowQmlImpl, QML_HAS_ATTACHED_PROPERTIES)
-
#include "moc_qquickwindowmodule_p.cpp"
diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h
index 16130bc8a0..869d5b9a8e 100644
--- a/src/quick/items/qquickwindowmodule_p.h
+++ b/src/quick/items/qquickwindowmodule_p.h
@@ -54,10 +54,10 @@
#include <private/qtquickglobal_p.h>
#include <qquickwindow.h>
#include <qqmlparserstatus.h>
+#include <private/qquickwindowattached_p.h>
QT_BEGIN_NAMESPACE
-class QQuickWindowAttached;
class QQuickWindowQmlImplPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public QQmlParserStatus
@@ -105,4 +105,7 @@ public:
QT_END_NAMESPACE
+QML_DECLARE_TYPE(QQuickWindowQmlImpl)
+QML_DECLARE_TYPEINFO(QQuickWindowQmlImpl, QML_HAS_ATTACHED_PROPERTIES)
+
#endif