aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/CMakeLists.txt2
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp2
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp15
-rw-r--r--src/quick/handlers/qquickpointerhandler_p.h1
-rw-r--r--src/quick/items/qquickdrag.cpp2
-rw-r--r--src/quick/items/qquickflickable.cpp16
-rw-r--r--src/quick/items/qquickitem.cpp127
-rw-r--r--src/quick/items/qquickitem.h2
-rw-r--r--src/quick/items/qquickitem_p.h8
-rw-r--r--src/quick/items/qquickmousearea.cpp8
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp13
-rw-r--r--src/quick/items/qquickpathview.cpp3
-rw-r--r--src/quick/items/qquicktableview.cpp2
-rw-r--r--src/quick/items/qquickwindow.cpp259
-rw-r--r--src/quick/items/qquickwindow.h4
-rw-r--r--src/quick/items/qquickwindow_p.h143
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp707
-rw-r--r--src/quick/util/qquickdeliveryagent_p.h101
-rw-r--r--src/quick/util/qquickdeliveryagent_p_p.h209
-rw-r--r--src/quickwidgets/qquickwidget.cpp12
-rw-r--r--tests/auto/quick/qquickitem/tst_qquickitem.cpp8
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp8
-rw-r--r--tests/auto/quick/shared/viewtestutil.cpp11
-rw-r--r--tests/auto/quick/touchmouse/tst_touchmouse.cpp14
24 files changed, 1149 insertions, 528 deletions
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index e14db9ec59..4583a29433 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -169,7 +169,7 @@ qt_internal_add_module(Quick
util/qquickanimatorjob.cpp util/qquickanimatorjob_p.h
util/qquickapplication.cpp util/qquickapplication_p.h
util/qquickbehavior.cpp util/qquickbehavior_p.h
- util/qquickdeliveryagent.cpp
+ util/qquickdeliveryagent.cpp util/qquickdeliveryagent_p.h util/qquickdeliveryagent_p_p.h
util/qquickfontloader.cpp util/qquickfontloader_p.h
util/qquickfontmetrics.cpp util/qquickfontmetrics_p.h
util/qquickforeignutils_p.h
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index 0794b86b11..e5e9b03f32 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -247,7 +247,7 @@ void QQuickDragHandler::handlePointerEventImpl(QPointerEvent *event)
m_pressedInsideTarget &= target()->contains(localPressPos);
m_pressTargetPos = targetCentroidPosition();
}
- // QQuickWindowPrivate::deliverToPassiveGrabbers() skips subsequent delivery if the event is filtered.
+ // QQuickDeliveryAgentPrivate::deliverToPassiveGrabbers() skips subsequent delivery if the event is filtered.
// (That affects behavior for mouse but not for touch, because Flickable only handles mouse.)
// So we have to compensate by accepting the event here to avoid any parent Flickable from
// getting the event via direct delivery and grabbing too soon.
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index cae700b1c8..f9ae620c75 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -40,6 +40,8 @@
#include "qquickpointerhandler_p.h"
#include "qquickpointerhandler_p_p.h"
#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickhandlerpoint_p.h>
+#include <QtQuick/private/qquickdeliveryagent_p_p.h>
#include <QtGui/private/qinputdevice_p.h>
QT_BEGIN_NAMESPACE
@@ -361,8 +363,9 @@ bool QQuickPointerHandler::approveGrabTransition(QPointerEvent *event, const QEv
// Flickable's wishes in that case, because then it would never have a chance.
if (existingItemGrabber->keepMouseGrab() &&
!(existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(parentItem()))) {
- QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(parentItem()->window());
- if (winPriv->isDeliveringTouchAsMouse() && point.id() == winPriv->touchMouseId) {
+ auto da = QQuickItemPrivate::get(parentItem())->deliveryAgentPrivate();
+ Q_ASSERT(da);
+ if (da->isDeliveringTouchAsMouse() && point.id() == da->touchMouseId) {
qCDebug(lcPointerHandlerGrab) << this << "wants to grab touchpoint" << point.id()
<< "but declines to steal grab from touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber;
allowed = false;
@@ -752,13 +755,7 @@ bool QQuickPointerHandlerPrivate::dragOverThreshold(const QEventPoint &point) co
QVector<QObject *> &QQuickPointerHandlerPrivate::deviceDeliveryTargets(const QInputDevice *device)
{
- QInputDevicePrivate *devPriv = QInputDevicePrivate::get(const_cast<QInputDevice *>(device));
- if (devPriv->qqExtra)
- return *static_cast<QVector<QObject *>*>(devPriv->qqExtra);
- auto targets = new QVector<QObject *>;
- devPriv->qqExtra = targets;
- QObject::connect(device, &QObject::destroyed, [targets]() { delete targets; });
- return *targets;
+ return QQuickDeliveryAgentPrivate::deviceExtra(device)->deliveryTargets;
}
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h
index 8d7fdcdcad..f7e7b83164 100644
--- a/src/quick/handlers/qquickpointerhandler_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p.h
@@ -167,6 +167,7 @@ protected:
bool parentContains(const QEventPoint &point) const;
bool parentContains(const QPointF &scenePosition) const;
+ friend class QQuickDeliveryAgentPrivate;
friend class QQuickItemPrivate;
friend class QQuickWindowPrivate;
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index a980f83aba..e6ffcee2aa 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -240,7 +240,7 @@ void QQuickDragAttachedPrivate::deliverEvent(QQuickWindow *window, QEvent *event
{
Q_ASSERT(!inEvent);
inEvent = true;
- QQuickWindowPrivate::get(window)->deliverDragEvent(&dragGrabber, event);
+ QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->deliverDragEvent(&dragGrabber, event);
inEvent = false;
}
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 62bc078f4d..b02840208d 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -1341,9 +1341,9 @@ void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event)
QVector2D velocity = event->point(0).velocity();
if (q->yflick())
- overThreshold |= QQuickWindowPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
if (q->xflick())
- overThreshold |= QQuickWindowPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
drag(currentTimestamp, event->type(), pos, deltas, overThreshold, false, false, velocity);
}
@@ -1518,7 +1518,7 @@ void QQuickFlickable::touchEvent(QTouchEvent *event)
auto &firstPoint = event->point(0);
if (auto grabber = qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(firstPoint))) {
const auto localPos = grabber->mapFromScene(firstPoint.scenePosition());
- QScopedPointer<QPointerEvent> localizedEvent(QQuickWindowPrivate::clonePointerEvent(event, localPos));
+ QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos));
QCoreApplication::sendEvent(window(), localizedEvent.data());
}
@@ -1762,7 +1762,7 @@ void QQuickFlickablePrivate::captureDelayedPress(QQuickItem *item, QPointerEvent
if (!isInnermostPressDelay(item))
return;
- delayedPressEvent = QQuickWindowPrivate::clonePointerEvent(event);
+ delayedPressEvent = QQuickDeliveryAgentPrivate::clonePointerEvent(event);
delayedPressEvent->setAccepted(false);
delayedPressTimer.start(pressDelay, q);
qCDebug(lcReplay) << "begin press delay" << pressDelay << "ms with" << delayedPressEvent;
@@ -1789,8 +1789,8 @@ void QQuickFlickablePrivate::replayDelayedPress()
// If we have the grab, release before delivering the event
if (QQuickWindow *window = q->window()) {
- QQuickWindowPrivate *wpriv = QQuickWindowPrivate::get(window);
- wpriv->allowChildEventFiltering = false; // don't allow re-filtering during replay
+ auto da = deliveryAgentPrivate();
+ da->allowChildEventFiltering = false; // don't allow re-filtering during replay
replayingPressEvent = true;
auto &firstPoint = event->point(0);
// At first glance, it's weird for delayedPressEvent to already have a grabber;
@@ -1813,7 +1813,7 @@ void QQuickFlickablePrivate::replayDelayedPress()
// We're done with replay, go back to normal delivery behavior
replayingPressEvent = false;
- wpriv->allowChildEventFiltering = true;
+ da->allowChildEventFiltering = true;
}
}
}
@@ -2577,7 +2577,7 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
bool stealThisEvent = d->stealMouse;
bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab());
if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) {
- QScopedPointer<QPointerEvent> localizedEvent(QQuickWindowPrivate::clonePointerEvent(event, localPos));
+ QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos));
localizedEvent->setAccepted(false);
switch (firstPoint.state()) {
case QEventPoint::State::Updated:
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 93512d70f8..f4eea2923a 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -91,6 +91,7 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcMouseTarget)
Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
+Q_DECLARE_LOGGING_CATEGORY(lcPtr)
Q_DECLARE_LOGGING_CATEGORY(lcTransient)
Q_LOGGING_CATEGORY(lcHandlerParent, "qt.quick.handler.parent")
@@ -2684,8 +2685,9 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
while (!scopeItem->isFocusScope() && scopeItem->parentItem())
scopeItem = scopeItem->parentItem();
if (d->window) {
- QQuickWindowPrivate::get(d->window)->clearFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
- QQuickWindowPrivate::DontChangeFocusProperty);
+ d->deliveryAgentPrivate()->
+ clearFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
+ QQuickDeliveryAgentPrivate::DontChangeFocusProperty);
if (scopeFocusedItem != this)
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, true);
} else {
@@ -2760,8 +2762,9 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
emit scopeFocusedItem->focusChanged(false);
} else {
if (d->window) {
- QQuickWindowPrivate::get(d->window)->setFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
- QQuickWindowPrivate::DontChangeFocusProperty);
+ d->deliveryAgentPrivate()->
+ setFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
+ QQuickDeliveryAgentPrivate::DontChangeFocusProperty);
} else {
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, true);
}
@@ -3037,7 +3040,6 @@ void QQuickItemPrivate::derefWindow()
window->unsetCursor();
}
#endif
- c->hoverItems.removeAll(q);
if (itemNodeInstance)
c->cleanup(itemNodeInstance);
if (!parentItem)
@@ -3194,6 +3196,7 @@ QQuickItemPrivate::QQuickItemPrivate()
, replayingPressEvent(false)
, touchEnabled(false)
, hasCursorHandler(false)
+ , hasSubsceneDeliveryAgent(false)
, dirtyAttributes(0)
, nextDirtyItem(nullptr)
, prevDirtyItem(nullptr)
@@ -5195,6 +5198,57 @@ QPointF QQuickItemPrivate::adjustedPosForTransform(const QPointF &centroidParent
return pos;
}
+/*! \internal
+ Returns the delivery agent for the narrowest subscene containing this item,
+ but falls back to QQuickWindowPrivate::deliveryAgent if there are no subscenes.
+
+ \note When a Qt Quick scene is shown in the usual way in its own window,
+ subscenes are ignored, and QQuickWindowPrivate::deliveryAgent is used.
+ Subscene delivery agents are used only in QtQuick 3D so far.
+*/
+QQuickDeliveryAgent *QQuickItemPrivate::deliveryAgent()
+{
+ // optimization: don't go up the parent hierarchy if this item is not aware of being in a subscene
+ if (hasSubsceneDeliveryAgent) {
+ QQuickItemPrivate *p = this;
+ do {
+ if (p->extra.isAllocated()) {
+ if (auto da = p->extra->subsceneDeliveryAgent)
+ return da;
+ p = p->parentItem ? QQuickItemPrivate::get(p->parentItem) : nullptr;
+ }
+ } while (p);
+ }
+ if (window)
+ return QQuickWindowPrivate::get(window)->deliveryAgent;
+ return nullptr;
+}
+
+QQuickDeliveryAgentPrivate *QQuickItemPrivate::deliveryAgentPrivate()
+{
+ auto da = deliveryAgent();
+ return da ? static_cast<QQuickDeliveryAgentPrivate *>(QQuickDeliveryAgentPrivate::get(da)) : nullptr;
+}
+
+/*! \internal
+ Ensures that this item, presumably the root of a sub-scene (e.g. because it
+ is mapped onto a 3D object in Qt Quick 3D), has a delivery agent to be used
+ when delivering events to the subscene: i.e. when the viewport delivers an
+ event to the subscene, or when the outer delivery agent delivers an update
+ to an item that grabbed during a previous subscene delivery. Creates a new
+ agent if it was not already created, and returns a pointer to the instance.
+*/
+QQuickDeliveryAgent *QQuickItemPrivate::ensureSubsceneDeliveryAgent()
+{
+ Q_Q(QQuickItem);
+ hasSubsceneDeliveryAgent = true;
+ if (extra.isAllocated() && extra->subsceneDeliveryAgent)
+ return extra->subsceneDeliveryAgent;
+ extra.value().subsceneDeliveryAgent = new QQuickDeliveryAgent(q);
+ qCDebug(lcPtr) << "created new" << extra->subsceneDeliveryAgent;
+ return extra->subsceneDeliveryAgent;
+}
+
bool QQuickItemPrivate::filterKeyEvent(QKeyEvent *e, bool post)
{
if (!extra.isAllocated() || !extra->keyHandler)
@@ -6089,12 +6143,10 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
effectiveVisible = newEffectiveVisible;
dirty(Visible);
- if (parentItem) QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
-
- if (window) {
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- windowPriv->removeGrabber(q, true, true, true);
- }
+ if (parentItem)
+ QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
+ if (window)
+ deliveryAgentPrivate()->removeGrabber(q, true, true, true);
bool childVisibilityChanged = false;
for (int ii = 0; ii < childItems.count(); ++ii)
@@ -6138,12 +6190,13 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
effectiveEnable = newEffectiveEnable;
- if (window) {
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- windowPriv->removeGrabber(q, true, true, true);
+ QQuickDeliveryAgentPrivate *da = deliveryAgentPrivate();
+ if (da) {
+ da->removeGrabber(q, true, true, true);
if (scope && !effectiveEnable && activeFocus) {
- windowPriv->clearFocusInScope(
- scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
+ da->clearFocusInScope(scope, q, Qt::OtherFocusReason,
+ QQuickDeliveryAgentPrivate::DontChangeFocusProperty |
+ QQuickDeliveryAgentPrivate::DontChangeSubFocusItem);
}
}
@@ -6152,9 +6205,10 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
(flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable);
}
- if (window && scope && effectiveEnable && focus) {
- QQuickWindowPrivate::get(window)->setFocusInScope(
- scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
+ if (scope && effectiveEnable && focus && da) {
+ da->setFocusInScope(scope, q, Qt::OtherFocusReason,
+ QQuickDeliveryAgentPrivate::DontChangeFocusProperty |
+ QQuickDeliveryAgentPrivate::DontChangeSubFocusItem);
}
itemChange(QQuickItem::ItemEnabledHasChanged, effectiveEnable);
@@ -7252,10 +7306,12 @@ void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
scope = scope->parentItem();
if (d->window) {
if (reason != Qt::PopupFocusReason) {
+ auto da = d->deliveryAgentPrivate();
+ Q_ASSERT(da);
if (focus)
- QQuickWindowPrivate::get(d->window)->setFocusInScope(scope, this, reason);
+ da->setFocusInScope(scope, this, reason);
else
- QQuickWindowPrivate::get(d->window)->clearFocusInScope(scope, this, reason);
+ da->clearFocusInScope(scope, this, reason);
}
} else {
// do the focus changes from setFocusInScope/clearFocusInScope that are
@@ -7276,7 +7332,7 @@ void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
changed << this;
emit focusChanged(focus);
- QQuickWindowPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+ QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}
} else {
QVarLengthArray<QQuickItem *, 20> changed;
@@ -7291,7 +7347,7 @@ void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
changed << this;
emit focusChanged(focus);
- QQuickWindowPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+ QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}
}
@@ -7418,7 +7474,7 @@ bool QQuickItem::isUnderMouse() const
// QQuickWindow handles QEvent::Leave to reset the lastMousePosition
// FIXME: Using QPointF() as the reset value means an item will not be
// under the mouse if the mouse is at 0,0 of the window.
- if (QQuickWindowPrivate::get(d->window)->lastMousePosition == QPointF())
+ if (const_cast<QQuickItemPrivate *>(d)->deliveryAgentPrivate()->lastMousePosition == QPointF())
return false;
QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition;
@@ -7703,13 +7759,14 @@ void QQuickItem::grabMouse()
Q_D(QQuickItem);
if (!d->window)
return;
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
- auto eventInDelivery = windowPriv->eventInDelivery();
+ auto da = d->deliveryAgentPrivate();
+ Q_ASSERT(da);
+ auto eventInDelivery = da->eventInDelivery();
if (!eventInDelivery) {
qWarning() << "cannot grab mouse: no event is currently being delivered";
return;
}
- auto epd = windowPriv->mousePointData();
+ auto epd = da->mousePointData();
eventInDelivery->setExclusiveGrabber(epd->eventPoint, this);
}
@@ -7729,14 +7786,15 @@ void QQuickItem::ungrabMouse()
Q_D(QQuickItem);
if (!d->window)
return;
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
- auto eventInDelivery = windowPriv->eventInDelivery();
+ auto da = d->deliveryAgentPrivate();
+ Q_ASSERT(da);
+ auto eventInDelivery = da->eventInDelivery();
if (!eventInDelivery) {
// do it the expensive way
- windowPriv->removeGrabber(this);
+ da->removeGrabber(this);
return;
}
- const auto &eventPoint = windowPriv->mousePointData()->eventPoint;
+ const auto &eventPoint = da->mousePointData()->eventPoint;
if (eventInDelivery->exclusiveGrabber(eventPoint) == this)
eventInDelivery->setExclusiveGrabber(eventPoint, nullptr);
}
@@ -7785,8 +7843,8 @@ void QQuickItem::setKeepMouseGrab(bool keep)
*/
void QQuickItem::grabTouchPoints(const QList<int> &ids)
{
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window());
- auto event = windowPriv->eventInDelivery();
+ Q_D(QQuickItem);
+ auto event = d->deliveryAgentPrivate()->eventInDelivery();
if (Q_UNLIKELY(!event)) {
qWarning() << "cannot grab: no event is currently being delivered";
return;
@@ -7806,8 +7864,7 @@ void QQuickItem::ungrabTouchPoints()
Q_D(QQuickItem);
if (!d->window)
return;
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
- windowPriv->removeGrabber(this, false, true);
+ d->deliveryAgentPrivate()->removeGrabber(this, false, true);
}
/*!
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index c7bc2f54c3..887318c23f 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -461,8 +461,8 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_resourceObjectDeleted(QObject *))
Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QV4::ExecutionEngine *))
- friend class QQuickWindow;
friend class QQuickWindowPrivate;
+ friend class QQuickDeliveryAgentPrivate;
friend class QSGRenderer;
friend class QAccessibleQuickItem;
friend class QQuickAccessibleAttached;
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index b97b9cefd5..caa6edb15b 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -388,6 +388,8 @@ public:
QSGOpacityNode *opacityNode;
QQuickDefaultClipNode *clipNode;
QSGRootNode *rootNode;
+ // subsceneDeliveryAgent is set only if this item is the root of a subscene, not on all items within.
+ QQuickDeliveryAgent *subsceneDeliveryAgent = nullptr;
// Mask contains() method
QMetaMethod maskContains;
@@ -480,6 +482,8 @@ public:
bool replayingPressEvent:1;
bool touchEnabled:1;
bool hasCursorHandler:1;
+ // set true when this item expects events via a subscene delivery agent; false otherwise
+ bool hasSubsceneDeliveryAgent:1;
enum DirtyType {
TransformOrigin = 0x00000001,
@@ -607,6 +611,10 @@ public:
qreal startScale, qreal activeScale,
qreal startRotation, qreal activeRotation);
+ QQuickDeliveryAgent *deliveryAgent();
+ QQuickDeliveryAgentPrivate *deliveryAgentPrivate();
+ QQuickDeliveryAgent *ensureSubsceneDeliveryAgent();
+
void deliverKeyEvent(QKeyEvent *);
bool filterKeyEvent(QKeyEvent *, bool post);
#if QT_CONFIG(im)
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 037eddd6a8..1d4c707c76 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -752,10 +752,10 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
d->lastPos = mapFromScene(d->lastScenePos);
}
- bool dragOverThresholdX = QQuickWindowPrivate::dragOverThreshold(dragPos.x() - startPos.x(),
- Qt::XAxis, event, d->drag->threshold());
- bool dragOverThresholdY = QQuickWindowPrivate::dragOverThreshold(dragPos.y() - startPos.y(),
- Qt::YAxis, event, d->drag->threshold());
+ bool dragOverThresholdX = QQuickDeliveryAgentPrivate::dragOverThreshold(dragPos.x() - startPos.x(),
+ Qt::XAxis, event, d->drag->threshold());
+ bool dragOverThresholdY = QQuickDeliveryAgentPrivate::dragOverThreshold(dragPos.y() - startPos.y(),
+ Qt::YAxis, event, d->drag->threshold());
if (!d->overThreshold && (((targetPos.x() != boundedDragPos.x()) && dragOverThresholdX) ||
((targetPos.y() != boundedDragPos.y()) && dragOverThresholdY)))
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index ed424a3763..642b5ca047 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -578,7 +578,6 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
clearTouchLists();
QList<QEventPoint> touchPoints;
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window());
QPointingDevice *dev = nullptr;
switch (event->type()) {
@@ -590,10 +589,12 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
dev = const_cast<QPointingDevice *>(te->pointingDevice());
break;
}
- case QEvent::MouseButtonPress:
- _mouseQpaTouchPoint = QEventPoint(windowPriv->touchMouseId);
- _touchMouseDevice = windowPriv->touchMouseDevice;
+ case QEvent::MouseButtonPress: {
+ auto da = QQuickItemPrivate::get(this)->deliveryAgentPrivate();
+ _mouseQpaTouchPoint = QEventPoint(da->touchMouseId);
+ _touchMouseDevice = da->touchMouseDevice;
Q_FALLTHROUGH();
+ }
case QEvent::MouseMove:
case QEvent::MouseButtonRelease: {
QMouseEvent *me = static_cast<QMouseEvent*>(event);
@@ -950,11 +951,11 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *receiver, QEve
return QQuickItem::childMouseEventFilter(receiver, event);
switch (event->type()) {
case QEvent::MouseButtonPress: {
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window());
+ auto da = QQuickItemPrivate::get(this)->deliveryAgentPrivate();
// If we already got a chance to filter the touchpoint that generated this synth-mouse-press,
// and chose not to filter it, ignore it now, too.
if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventSynthesizedByQt &&
- _lastFilterableTouchPointIds.contains(windowPriv->touchMouseId))
+ _lastFilterableTouchPointIds.contains(da->touchMouseId))
return false;
} Q_FALLTHROUGH();
case QEvent::MouseMove:
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index 3d0b1b97f9..4cde344dce 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -1705,7 +1705,8 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
QPointF pathPoint = pointNear(event->position(), &newPc);
if (!stealMouse) {
QPointF posDelta = event->position() - startPos;
- if (QQuickWindowPrivate::dragOverThreshold(posDelta.y(), Qt::YAxis, event) || QQuickWindowPrivate::dragOverThreshold(posDelta.x(), Qt::XAxis, event)) {
+ if (QQuickDeliveryAgentPrivate::dragOverThreshold(posDelta.y(), Qt::YAxis, event) ||
+ QQuickDeliveryAgentPrivate::dragOverThreshold(posDelta.x(), Qt::XAxis, event)) {
// The touch has exceeded the threshold. If the movement along the path is close to the drag threshold
// then we'll assume that this gesture targets the PathView. This ensures PathView gesture grabbing
// is in sync with other items.
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 1b71bd5a53..0d66f81b24 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -1295,7 +1295,7 @@ void QQuickTableViewPrivate::releaseItem(FxTableItem *fxTableItem, QQmlTableInst
const bool hasFocus = item == focusItem || item->isAncestorOf(focusItem);
if (hasFocus) {
const auto focusChild = QQuickItemPrivate::get(q)->subFocusItem;
- QQuickWindowPrivate::get(window)->clearFocusInScope(q, focusChild, Qt::OtherFocusReason);
+ deliveryAgentPrivate()->clearFocusInScope(q, focusChild, Qt::OtherFocusReason);
}
}
}
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 295ab9dc43..643e47c8a0 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -45,23 +45,16 @@
#include "qquickevents_p_p.h"
#include "qquickgraphicsdevice_p.h"
-#if QT_CONFIG(quick_draganddrop)
-#include <private/qquickdrag_p.h>
-#endif
-#include <private/qquickhoverhandler_p.h>
-#include <private/qquickpointerhandler_p.h>
-
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgplaintexture_p.h>
+#include <QtQuick/private/qquickpointerhandler_p.h>
#include <private/qsgrenderloop_p.h>
#include <private/qsgrhisupport_p.h>
#include <private/qquickrendercontrol_p.h>
#include <private/qquickanimatorcontroller_p.h>
-#include <private/qquickpointerhandler_p_p.h>
#include <private/qquickprofiler_p.h>
#include <private/qguiapplication_p.h>
-#include <QtGui/QInputMethod>
#include <private/qabstractanimation_p.h>
@@ -70,7 +63,6 @@
#include <QtGui/qmatrix4x4.h>
#include <QtGui/private/qevent_p.h>
#include <QtGui/private/qpointingdevice_p.h>
-#include <QtGui/qpa/qplatformtheme.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qabstractanimation.h>
#include <QtCore/QLibraryInfo>
@@ -96,12 +88,11 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcTablet, "qt.quick.tablet")
+Q_DECLARE_LOGGING_CATEGORY(lcMouse)
+Q_DECLARE_LOGGING_CATEGORY(lcTouch)
Q_LOGGING_CATEGORY(lcDirty, "qt.quick.dirty")
Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
-extern Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1);
-
bool QQuickWindowPrivate::defaultAlphaBuffer = false;
#if defined(QT_QUICK_DEFAULT_TEXT_RENDER_TYPE)
@@ -110,20 +101,6 @@ QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow:
QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QtTextRendering;
#endif
-void QQuickWindowPrivate::updateFocusItemTransform()
-{
-#if QT_CONFIG(im)
- Q_Q(QQuickWindow);
- QQuickItem *focus = q->activeFocusItem();
- if (focus && QGuiApplication::focusObject() == focus) {
- QQuickItemPrivate *focusPrivate = QQuickItemPrivate::get(focus);
- QGuiApplication::inputMethod()->setInputItemTransform(focusPrivate->itemToWindowTransform());
- QGuiApplication::inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height));
- focus->updateInputMethod(Qt::ImInputItemClipRectangle);
- }
-#endif
-}
-
class QQuickWindowIncubationController : public QObject, public QQmlIncubationController
{
Q_OBJECT
@@ -280,9 +257,12 @@ void QQuickWindow::focusOutEvent(QFocusEvent *ev)
void QQuickWindow::focusInEvent(QFocusEvent *ev)
{
Q_D(QQuickWindow);
+ if (d->inDestructor)
+ return;
if (d->contentItem)
d->contentItem->setFocus(true, ev->reason());
- d->updateFocusItemTransform();
+ if (auto da = d->deliveryAgentPrivate())
+ da->updateFocusItemTransform();
}
#if QT_CONFIG(im)
@@ -408,7 +388,7 @@ void QQuickWindowPrivate::polishItems()
const bool isActiveFocusItem = (focusItem == QGuiApplication::focusObject());
const bool hasImEnabled = focusItem->inputMethodQuery(Qt::ImEnabled).toBool();
if (isActiveFocusItem && hasImEnabled && transformDirtyOnItemOrAncestor(focusItem))
- updateFocusItemTransform();
+ deliveryAgentPrivate()->updateFocusItemTransform();
}
#endif
}
@@ -719,47 +699,31 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
QQuickWindowPrivate::QQuickWindowPrivate()
: contentItem(nullptr)
- , activeFocusItem(nullptr)
-#if QT_CONFIG(cursor)
- , cursorItem(nullptr)
- , cursorHandler(nullptr)
-#endif
-#if QT_CONFIG(quick_draganddrop)
- , dragGrabber(nullptr)
-#endif
- , touchMouseId(-1)
- , touchMouseDevice(nullptr)
- , touchMousePressTimestamp(0)
, dirtyItemList(nullptr)
, devicePixelRatio(0)
, context(nullptr)
, renderer(nullptr)
, windowManager(nullptr)
, renderControl(nullptr)
- , pointerEventRecursionGuard(0)
, clearColor(Qt::white)
, persistentGraphics(true)
, persistentSceneGraph(true)
- , lastWheelEventAccepted(false)
, componentCompleted(true)
- , allowChildEventFiltering(true)
- , allowDoubleClick(true)
- , lastFocusReason(Qt::OtherFocusReason)
+ , inDestructor(false)
, incubationController(nullptr)
, hasActiveSwapchain(false)
, hasRenderableSwapchain(false)
, swapchainJustBecameRenderable(false)
{
-#if QT_CONFIG(quick_draganddrop)
- dragGrabber = new QQuickDragGrabber;
-#endif
}
QQuickWindowPrivate::~QQuickWindowPrivate()
{
+ inDestructor = true;
redirect.rt.reset(rhi, renderer);
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->removeWindow(q_func());
+ deliveryAgent = nullptr;
}
void QQuickWindowPrivate::updateChildrenPalettes(const QPalette &parentPalette)
@@ -780,6 +744,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
Q_Q(QQuickWindow);
contentItem = new QQuickRootItem;
+ contentItem->setObjectName(q->objectName());
QQml_setParent_noEvent(contentItem, c);
QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership);
QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(contentItem);
@@ -787,6 +752,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
contentItemPrivate->windowRefCount = 1;
contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
contentItem->setSize(q->size());
+ deliveryAgent = new QQuickDeliveryAgent(contentItem);
visualizationMode = qgetenv("QSG_VISUALIZE");
renderControl = control;
@@ -839,7 +805,7 @@ void QQuickWindow::handleApplicationStateChanged(Qt::ApplicationState state)
{
Q_D(QQuickWindow);
if (state != Qt::ApplicationActive && d->contentItem)
- d->handleWindowDeactivate();
+ d->deliveryAgentPrivate()->handleWindowDeactivate(this);
}
/*!
@@ -871,8 +837,8 @@ void QQuickWindowPrivate::dirtyItem(QQuickItem *)
QQuickItem *QQuickWindow::mouseGrabberItem() const
{
Q_D(const QQuickWindow);
- auto epd = const_cast<QQuickWindowPrivate *>(d)->mousePointData();
- if (!epd && d->eventsInDelivery.isEmpty()) {
+ auto epd = const_cast<QQuickWindowPrivate *>(d)->deliveryAgentPrivate()->mousePointData();
+ if (!epd && d->deliveryAgentPrivate()->eventsInDelivery.isEmpty()) {
qCDebug(lcMouse, "mouse grabber ambiguous: no event is currently being delivered");
return qmlobject_cast<QQuickItem *>(QPointingDevicePrivate::get(QPointingDevice::primaryPointingDevice())->
firstPointExclusiveGrabber());
@@ -1134,7 +1100,7 @@ QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control
QQuickWindow::~QQuickWindow()
{
Q_D(QQuickWindow);
-
+ d->inDestructor = true;
if (d->renderControl) {
QQuickRenderControlPrivate::get(d->renderControl)->windowDestroyed();
} else if (d->windowManager) {
@@ -1143,12 +1109,11 @@ QQuickWindow::~QQuickWindow()
}
delete d->incubationController; d->incubationController = nullptr;
-#if QT_CONFIG(quick_draganddrop)
- delete d->dragGrabber; d->dragGrabber = nullptr;
-#endif
QQuickRootItem *root = d->contentItem;
d->contentItem = nullptr;
+ root->setParent(nullptr); // avoid QChildEvent delivery during deletion
delete root;
+ d->deliveryAgent = nullptr; // avoid forwarding events there during destruction
d->renderJobMutex.lock();
qDeleteAll(d->beforeSynchronizingJobs);
@@ -1305,9 +1270,6 @@ bool QQuickWindow::isPersistentSceneGraph() const
return d->persistentSceneGraph;
}
-
-
-
/*!
\qmlattachedproperty Item Window::contentItem
\since 5.4
@@ -1342,7 +1304,7 @@ QQuickItem *QQuickWindow::activeFocusItem() const
{
Q_D(const QQuickWindow);
- return d->activeFocusItem;
+ return d->deliveryAgentPrivate()->activeFocusItem;
}
/*!
@@ -1353,27 +1315,153 @@ QObject *QQuickWindow::focusObject() const
{
Q_D(const QQuickWindow);
- if (d->activeFocusItem)
- return d->activeFocusItem;
+ if (!d->inDestructor && d->deliveryAgentPrivate()->activeFocusItem)
+ return d->deliveryAgentPrivate()->activeFocusItem;
return const_cast<QQuickWindow*>(this);
}
/*! \reimp */
+bool QQuickWindow::event(QEvent *e)
+{
+ Q_D(QQuickWindow);
+
+ // bypass QWindow::event dispatching of input events: deliveryAgent takes care of it
+ QQuickDeliveryAgent *da = d->deliveryAgent;
+ if (e->isPointerEvent()) {
+ /*
+ When delivering update and release events to existing grabbers,
+ use the subscene delivery agent, if any. A possible scenario:
+ 1) Two touchpoints pressed on the main window: QQuickWindowPrivate::deliveryAgent delivers to QQuick3DViewport,
+ which does picking and finds two subscenes ("root" Items mapped onto two different 3D objects) to deliver it to.
+ 2) The QTouchEvent is split up so that each subscene sees points relevant to it.
+ 3) During delivery to either subscene, an item in the subscene grabs.
+ 4) The user moves finger(s) generating a move event: the correct grabber item needs to get the update
+ via the same subscene delivery agent from which it got the press, so that the coord transform will be done properly.
+ 5) Likewise with the touchpoint releases.
+ With single-point events (mouse, or only one finger) it's simplified: there can only be one subscene of interest;
+ for (pt : pe->points()) would only iterate once, so we might as well skip that logic.
+ */
+ auto pe = static_cast<QPointerEvent *>(e);
+ if (pe->pointCount() > 1) {
+ bool ret = false;
+ Q_ASSERT(QQuickDeliveryAgentPrivate::isTouchEvent(pe));
+ // Split up the multi-point event according to the relevant QQuickDeliveryAgent that should deliver to each existing grabber
+ // but send ungrabbed points to d->deliveryAgent()
+ QFlatMap<QQuickDeliveryAgent*, QList<QEventPoint>> deliveryAgentsNeedingPoints;
+ QEventPoint::States eventStates;
+ for (const auto &pt : pe->points()) {
+ eventStates |= pt.state();
+ auto *ptda = QQuickDeliveryAgent::grabberAgent(pe, pt);
+ if (!ptda)
+ ptda = da;
+ if (ptda) {
+ auto danpit = deliveryAgentsNeedingPoints.find(ptda);
+ if (danpit == deliveryAgentsNeedingPoints.end()) {
+ deliveryAgentsNeedingPoints.insert(ptda, QList<QEventPoint>() << pt);
+ } else {
+ danpit.value().append(pt);
+ }
+ }
+ }
+ // Make new touch events for each subscene, the same way QQuickItemPrivate::localizedTouchEvent() does it
+ for (auto daAndPoints : deliveryAgentsNeedingPoints) {
+ // if all points have the same state, set the event type accordingly
+ QEvent::Type eventType = pe->type();
+ switch (eventStates) {
+ case QEventPoint::State::Pressed:
+ eventType = QEvent::TouchBegin;
+ break;
+ case QEventPoint::State::Released:
+ eventType = QEvent::TouchEnd;
+ break;
+ default:
+ eventType = QEvent::TouchUpdate;
+ break;
+ }
+ QMutableTouchEvent te(eventType, pe->pointingDevice(), pe->modifiers(), daAndPoints.second);
+ te.setTimestamp(pe->timestamp());
+ te.accept();
+ qCDebug(lcTouch) << "subscene touch:" << daAndPoints.first << "shall now receive" << &te;
+ ret = daAndPoints.first->event(&te) || ret;
+ }
+ if (ret)
+ return true;
+ } else if (pe->pointCount()) {
+ // single-point event
+ if (auto *ptda = QQuickDeliveryAgent::grabberAgent(pe, pe->points().first()))
+ da = ptda;
+ }
+ // else if it has no points, it's probably a TouchCancel, and DeliveryAgent needs to handle it.
+ // TODO should we deliver to all DAs at once then, since we don't know which one should get it?
+ // or fix QTBUG-90851 so that the event always has points?
+ if (da && da->event(e))
+ return true;
+ } else if (e->isInputEvent()) {
+ if (da && da->event(e))
+ return true;
+ }
+
+ switch (e->type()) {
+ // a few more types that are not QInputEvents, but QQuickDeliveryAgent needs to handle them anyway
+ case QEvent::FocusAboutToChange:
+ case QEvent::Enter:
+ case QEvent::Leave:
+ case QEvent::InputMethod:
+ case QEvent::InputMethodQuery:
+#if QT_CONFIG(quick_draganddrop)
+ case QEvent::DragEnter:
+ case QEvent::DragLeave:
+ case QEvent::DragMove:
+ case QEvent::Drop:
+#endif
+ if (d->inDestructor)
+ return false;
+ if (da && da->event(e))
+ return true;
+ break;
+ case QEvent::LanguageChange:
+ if (d->contentItem)
+ QCoreApplication::sendEvent(d->contentItem, e);
+ break;
+ case QEvent::UpdateRequest:
+ if (d->windowManager)
+ d->windowManager->handleUpdateRequest(this);
+ break;
+ case QEvent::PlatformSurface:
+ if ((static_cast<QPlatformSurfaceEvent *>(e))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
+ // Ensure that the rendering thread is notified before
+ // the QPlatformWindow is destroyed.
+ if (d->windowManager)
+ d->windowManager->hide(this);
+ }
+ break;
+ case QEvent::WindowDeactivate:
+ d->deliveryAgentPrivate()->handleWindowDeactivate(this);
+ break;
+ default:
+ break;
+ }
+
+ if (e->type() == QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))
+ update();
+ else if (e->type() == QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure))
+ d->windowManager->handleContextCreationFailure(this);
+
+ return QWindow::event(e);
+}
+
+/*! \reimp */
void QQuickWindow::keyPressEvent(QKeyEvent *e)
{
Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(),
- e->modifiers());
- d->deliverKeyEvent(e);
+ d->deliveryAgentPrivate()->deliverKeyEvent(e);
}
/*! \reimp */
void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
{
Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(),
- e->modifiers());
- d->deliverKeyEvent(e);
+ d->deliveryAgentPrivate()->deliverKeyEvent(e);
}
#if QT_CONFIG(wheelevent)
@@ -1381,18 +1469,7 @@ void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
void QQuickWindow::wheelEvent(QWheelEvent *event)
{
Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel,
- event->angleDelta().x(), event->angleDelta().y());
-
- qCDebug(lcMouse) << event;
-
- //if the actual wheel event was accepted, accept the compatibility wheel event and return early
- if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate)
- return;
-
- event->ignore();
- d->deliverSinglePointEventUntilAccepted(event);
- d->lastWheelEventAccepted = event->isAccepted();
+ d->deliveryAgentPrivate()->deliverSinglePointEventUntilAccepted(event);
}
#endif // wheelevent
@@ -1401,9 +1478,7 @@ void QQuickWindow::wheelEvent(QWheelEvent *event)
void QQuickWindow::tabletEvent(QTabletEvent *event)
{
Q_D(QQuickWindow);
- qCDebug(lcTablet) << event;
- // TODO Qt 6: make sure TabletEnterProximity and TabletLeaveProximity are delivered here
- d->deliverPointerEvent(event);
+ d->deliveryAgentPrivate()->deliverPointerEvent(event);
}
#endif // tabletevent
@@ -1411,34 +1486,40 @@ void QQuickWindow::tabletEvent(QTabletEvent *event)
void QQuickWindow::mousePressEvent(QMouseEvent *event)
{
Q_D(QQuickWindow);
- d->handleMouseEvent(event);
+ d->deliveryAgentPrivate()->handleMouseEvent(event);
}
/*! \reimp */
void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
{
Q_D(QQuickWindow);
- d->handleMouseEvent(event);
+ d->deliveryAgentPrivate()->handleMouseEvent(event);
}
/*! \reimp */
void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_D(QQuickWindow);
- d->handleMouseEvent(event);
+ d->deliveryAgentPrivate()->handleMouseEvent(event);
}
/*! \reimp */
void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
{
Q_D(QQuickWindow);
- d->handleMouseEvent(event);
+ d->deliveryAgentPrivate()->handleMouseEvent(event);
}
-#if QT_CONFIG(cursor)
-void QQuickWindowPrivate::updateCursor(const QPointF &scenePos)
+void QQuickWindowPrivate::flushFrameSynchronousEvents()
{
Q_Q(QQuickWindow);
+ deliveryAgentPrivate()->flushFrameSynchronousEvents(q);
+}
- auto cursorItemAndHandler = findCursorItemAndHandler(contentItem, scenePos);
-
+#if QT_CONFIG(cursor)
+void QQuickWindowPrivate::updateCursor(const QPointF &scenePos, QQuickItem *rootItem)
+{
+ Q_Q(QQuickWindow);
+ if (!rootItem)
+ rootItem = contentItem;
+ auto cursorItemAndHandler = findCursorItemAndHandler(rootItem, scenePos);
if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second) {
QWindow *renderWindow = QQuickRenderControl::renderWindowFor(q);
QWindow *window = renderWindow ? renderWindow : q;
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 8700b5a141..7ee3be9b50 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -231,6 +231,9 @@ protected:
void focusOutEvent(QFocusEvent *) override;
bool event(QEvent *) override;
+
+ // These overrides are no longer normal entry points for
+ // input events, but kept in case legacy code calls them.
void keyPressEvent(QKeyEvent *) override;
void keyReleaseEvent(QKeyEvent *) override;
void mousePressEvent(QMouseEvent *) override;
@@ -258,6 +261,7 @@ private:
friend class QQuickRenderControl;
friend class QQuickAnimatorController;
friend class QQuickWidgetPrivate;
+ friend class QQuickDeliveryAgentPrivate;
Q_DISABLE_COPY(QQuickWindow)
};
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 869324c317..30b5a36cb5 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtQuick/private/qquickdeliveryagent_p_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <QtQuick/private/qquickpaletteproviderprivatebase_p.h>
@@ -100,6 +101,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickRootItem : public QQuickItem
QML_ADDED_IN_VERSION(2, 0)
public:
QQuickRootItem();
+
public Q_SLOTS:
void setWidth(int w) {QQuickItem::setWidth(qreal(w));}
void setHeight(int h) {QQuickItem::setHeight(qreal(h));}
@@ -142,104 +144,25 @@ public:
QSet<QQuickItem *> parentlessItems;
QQmlListProperty<QObject> data();
- QQuickItem *activeFocusItem;
-
- void deliverKeyEvent(QKeyEvent *e);
+ // primary delivery agent for the whole scene, used by default for events that arrive in this window;
+ // but any subscene root can have a QQuickItemPrivate::ExtraData::subsceneDeliveryAgent
+ QQuickDeliveryAgent *deliveryAgent = nullptr;
+ QQuickDeliveryAgentPrivate *deliveryAgentPrivate() const
+ { return static_cast<QQuickDeliveryAgentPrivate *>(QQuickDeliveryAgentPrivate::get(deliveryAgent)); }
- // Keeps track of the item currently receiving mouse events
-#if QT_CONFIG(cursor)
- QQuickItem *cursorItem;
- QQuickPointerHandler *cursorHandler;
-#endif
-#if QT_CONFIG(quick_draganddrop)
- QQuickDragGrabber *dragGrabber;
-#endif
- QQuickItem *lastUngrabbed = nullptr;
- QStack<QPointerEvent *> eventsInDelivery;
-
- int touchMouseId; // only for obsolete stuff like QQuickItem::grabMouse()
- // TODO get rid of these
- const QPointingDevice *touchMouseDevice;
- ulong touchMousePressTimestamp;
- QPoint touchMousePressPos; // in screen coordiantes
- bool isDeliveringTouchAsMouse() const { return touchMouseId != -1 && touchMouseDevice; }
- void cancelTouchMouseSynthesis();
-
- bool checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos);
- QPointingDevicePrivate::EventPointData *mousePointData();
- QPointerEvent *eventInDelivery() const;
-
- // Mouse positions are saved in widget coordinates
- QPointF lastMousePosition;
- bool deliverTouchAsMouse(QQuickItem *item, QTouchEvent *pointerEvent);
- void translateTouchEvent(QTouchEvent *touchEvent);
- void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true, bool cancel = false);
- void onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, const QPointerEvent *event, const QEventPoint &point);
- static QPointerEvent *clonePointerEvent(QPointerEvent *event, std::optional<QPointF> transformedLocalPos = std::nullopt);
- void deliverToPassiveGrabbers(const QVector<QPointer<QObject> > &passiveGrabbers, QPointerEvent *pointerEvent);
- bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
- bool sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr);
- bool sendFilteredPointerEventImpl(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
- bool deliverSinglePointEventUntilAccepted(QPointerEvent *);
-
- // entry point of events to the window
- void handleTouchEvent(QTouchEvent *);
- void handleMouseEvent(QMouseEvent *);
- bool compressTouchEvent(QTouchEvent *);
+ // TODO remove these: they were moved to QQuickDeliveryAgentPrivate
+ static bool isMouseEvent(const QPointerEvent *ev) { return QQuickDeliveryAgentPrivate::isMouseEvent(ev); }
+ static bool isTouchEvent(const QPointerEvent *ev) { return QQuickDeliveryAgentPrivate::isTouchEvent(ev); }
+ static bool isTabletEvent(const QPointerEvent *ev) { return QQuickDeliveryAgentPrivate::isTabletEvent(ev); }
void flushFrameSynchronousEvents();
- void deliverDelayedTouchEvent();
- void handleWindowDeactivate();
-
- // utility functions that used to be in QQuickPointerEvent et al.
- bool allUpdatedPointsAccepted(const QPointerEvent *ev);
- static void localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest);
- QList<QObject *> exclusiveGrabbers(QPointerEvent *ev);
- static bool isMouseEvent(const QPointerEvent *ev);
- static bool isTouchEvent(const QPointerEvent *ev);
- static bool isTabletEvent(const QPointerEvent *ev);
-
- // delivery of pointer events:
- void touchToMouseEvent(QEvent::Type type, const QEventPoint &p, const QTouchEvent *touchEvent, QMutableSinglePointEvent *mouseEvent);
- void ensureDeviceConnected(const QPointingDevice *dev);
- void deliverPointerEvent(QPointerEvent *);
- bool deliverTouchCancelEvent(QTouchEvent *);
- bool deliverPressOrReleaseEvent(QPointerEvent *, bool handlersOnly = false);
- void deliverUpdatedPoints(QPointerEvent *event);
- void deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly = false);
-
- QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointerEvent *event, const QEventPoint &point,
- bool checkMouseButtons, bool checkAcceptsTouch) const;
- QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
-
- // hover delivery
- bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted);
- bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos,
- Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted);
- bool clearHover(ulong timestamp = 0);
-
-#if QT_CONFIG(quick_draganddrop)
- void deliverDragEvent(QQuickDragGrabber *, QEvent *);
- bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *, QVarLengthArray<QQuickItem*, 64> *currentGrabItems = nullptr);
-#endif
+
#if QT_CONFIG(cursor)
- void updateCursor(const QPointF &scenePos);
+ QQuickItem *cursorItem = nullptr;
+ QQuickPointerHandler *cursorHandler = nullptr;
+ void updateCursor(const QPointF &scenePos, QQuickItem *rootItem = nullptr);
QPair<QQuickItem*, QQuickPointerHandler*> findCursorItemAndHandler(QQuickItem *item, const QPointF &scenePos) const;
#endif
- QList<QQuickItem*> hoverItems;
- enum FocusOption {
- DontChangeFocusProperty = 0x01,
- DontChangeSubFocusItem = 0x02
- };
- Q_DECLARE_FLAGS(FocusOptions, FocusOption)
-
- void setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions = { });
- void clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions = { });
- static void notifyFocusChangesRecur(QQuickItem **item, int remaining);
- void clearFocusObject() override;
-
- void updateFocusItemTransform();
-
void dirtyItem(QQuickItem *);
void cleanup(QSGNode *);
@@ -272,8 +195,6 @@ public:
QList<QSGNode *> cleanupNodeList;
QVector<QQuickItem *> itemsToPolish;
- QVector<QQuickItem *> hasFiltered; // during event delivery to a single receiver, the filtering parents for which childMouseEventFilter was already called
- QVector<QQuickItem *> skipDelivery; // during delivery of one event to all receivers, Items to which we know delivery is no longer necessary
qreal devicePixelRatio;
QMetaObject::Connection physicalDpiChangedConnection;
@@ -295,23 +216,13 @@ public:
QSGRenderLoop *windowManager;
QQuickRenderControl *renderControl;
QScopedPointer<QQuickAnimatorController> animationController;
- QScopedPointer<QMutableTouchEvent> delayedTouch;
- QList<const QPointingDevice *> knownPointingDevices;
-
- int pointerEventRecursionGuard;
QColor clearColor;
uint persistentGraphics : 1;
uint persistentSceneGraph : 1;
-
- uint lastWheelEventAccepted : 1;
- bool componentCompleted : 1;
-
- bool allowChildEventFiltering : 1;
- bool allowDoubleClick : 1;
-
- Qt::FocusReason lastFocusReason;
+ uint componentCompleted : 1;
+ uint inDestructor : 1;
// Storage for setRenderTarget(QQuickRenderTarget).
// Gets baked into redirect.renderTarget by ensureCustomRenderTarget() when rendering the next frame.
@@ -332,15 +243,18 @@ public:
static bool defaultAlphaBuffer;
static QQuickWindow::TextRenderType textRenderType;
- static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1);
-
- static bool dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold = -1);
-
- // currently in use in Controls 2; TODO remove
+ // vvv currently in use in Controls 2; TODO remove
static bool dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint *tp, int startDragThreshold = -1)
- { return dragOverThreshold(d, axis, *tp, startDragThreshold); }
-
- static bool dragOverThreshold(QVector2D delta);
+ { return QQuickDeliveryAgentPrivate::dragOverThreshold(d, axis, *tp, startDragThreshold); }
+ static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1)
+ { return QQuickDeliveryAgentPrivate::dragOverThreshold(d, axis, event, startDragThreshold); }
+ void clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason)
+ { deliveryAgentPrivate()->clearFocusInScope(scope, item, reason); }
+ void handleTouchEvent(QTouchEvent *e)
+ { deliveryAgentPrivate()->handleTouchEvent(e); }
+ void handleMouseEvent(QMouseEvent *e)
+ { deliveryAgentPrivate()->handleMouseEvent(e); }
+ // ^^^ currently in use in Controls 2; TODO remove
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
@@ -393,7 +307,6 @@ public:
}
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickWindowPrivate::FocusOptions)
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickWindowPrivate::TextureFromNativeTextureFlags)
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index 3dfda6902a..dbe14826db 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -37,18 +37,20 @@
**
****************************************************************************/
+#include <QtCore/qdebug.h>
+#include <QtGui/private/qevent_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtQml/private/qabstractanimationjob_p.h>
-#include <QtQuick/private/qquickdrag_p.h>
-#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickdeliveryagent_p_p.h>
#include <QtQuick/private/qquickhoverhandler_p.h>
-#include <QtQuick/private/qquickpointerhandler_p.h>
#include <QtQuick/private/qquickpointerhandler_p_p.h>
+#if QT_CONFIG(quick_draganddrop)
+#include <QtQuick/private/qquickdrag_p.h>
+#endif
#include <QtQuick/private/qquickprofiler_p.h>
-#include <QtQuick/private/qquickrendercontrol_p.h>
+#include <QtQuick/qquickrendercontrol.h>
#include <QtQuick/private/qquickwindow_p.h>
-#include <QtQuick/private/qsgrenderloop_p.h>
QT_BEGIN_NAMESPACE
@@ -57,7 +59,9 @@ Q_LOGGING_CATEGORY(lcTouchCmprs, "qt.quick.touch.compression")
Q_LOGGING_CATEGORY(lcTouchTarget, "qt.quick.touch.target")
Q_LOGGING_CATEGORY(lcMouse, "qt.quick.mouse")
Q_LOGGING_CATEGORY(lcMouseTarget, "qt.quick.mouse.target")
+Q_LOGGING_CATEGORY(lcTablet, "qt.quick.tablet")
Q_LOGGING_CATEGORY(lcPtr, "qt.quick.pointer")
+Q_LOGGING_CATEGORY(lcPtrLoc, "qt.quick.pointer.localization")
Q_LOGGING_CATEGORY(lcPtrGrab, "qt.quick.pointer.grab")
Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target")
Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target")
@@ -66,7 +70,7 @@ Q_LOGGING_CATEGORY(lcFocus, "qt.quick.focus")
extern Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1);
-void QQuickWindowPrivate::touchToMouseEvent(QEvent::Type type, const QEventPoint &p, const QTouchEvent *touchEvent, QMutableSinglePointEvent *mouseEvent)
+void QQuickDeliveryAgentPrivate::touchToMouseEvent(QEvent::Type type, const QEventPoint &p, const QTouchEvent *touchEvent, QMutableSinglePointEvent *mouseEvent)
{
Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
QMutableSinglePointEvent ret(type, touchEvent->pointingDevice(), p,
@@ -77,7 +81,7 @@ void QQuickWindowPrivate::touchToMouseEvent(QEvent::Type type, const QEventPoint
*mouseEvent = ret;
}
-bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos)
+bool QQuickDeliveryAgentPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos)
{
bool doubleClicked = false;
@@ -103,7 +107,7 @@ bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoi
return doubleClicked;
}
-QPointerEvent *QQuickWindowPrivate::eventInDelivery() const
+QPointerEvent *QQuickDeliveryAgentPrivate::eventInDelivery() const
{
if (eventsInDelivery.isEmpty())
return nullptr;
@@ -115,7 +119,7 @@ QPointerEvent *QQuickWindowPrivate::eventInDelivery() const
that don't have the currently-being-delivered event in context.
Returns the device the currently-being-delivered event comse from.
*/
-QPointingDevicePrivate::EventPointData *QQuickWindowPrivate::mousePointData()
+QPointingDevicePrivate::EventPointData *QQuickDeliveryAgentPrivate::mousePointData()
{
if (eventsInDelivery.isEmpty())
return nullptr;
@@ -123,21 +127,22 @@ QPointingDevicePrivate::EventPointData *QQuickWindowPrivate::mousePointData()
return devPriv->pointById(isDeliveringTouchAsMouse() ? touchMouseId : 0);
}
-void QQuickWindowPrivate::cancelTouchMouseSynthesis()
+void QQuickDeliveryAgentPrivate::cancelTouchMouseSynthesis()
{
qCDebug(lcTouchTarget) << "id" << touchMouseId << "on" << touchMouseDevice;
touchMouseId = -1;
touchMouseDevice = nullptr;
}
-bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *pointerEvent)
+bool QQuickDeliveryAgentPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *pointerEvent)
{
+ Q_Q(QQuickDeliveryAgent);
Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
auto device = pointerEvent->pointingDevice();
// A touch event from a trackpad is likely to be followed by a mouse or gesture event, so mouse event synth is redundant
if (device->type() == QInputDevice::DeviceType::TouchPad && device->capabilities().testFlag(QInputDevice::Capability::MouseEmulation)) {
- qCDebug(lcTouchTarget) << "skipping delivery of synth-mouse event from" << device;
+ qCDebug(lcTouchTarget) << q << "skipping delivery of synth-mouse event from" << device;
return false;
}
@@ -159,7 +164,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *poi
if (!item->contains(pos))
break;
- qCDebug(lcTouchTarget) << device << "TP (mouse)" << Qt::hex << p.id() << "->" << item;
+ qCDebug(lcTouchTarget) << q << device << "TP (mouse)" << Qt::hex << p.id() << "->" << item;
QMutableSinglePointEvent mousePress;
touchToMouseEvent(QEvent::MouseButtonPress, p, &event, &mousePress);
@@ -203,7 +208,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *poi
QCoreApplication::sendEvent(item, &me);
event.setAccepted(me.isAccepted());
if (me.isAccepted())
- qCDebug(lcTouchTarget) << device << "TP (mouse)" << Qt::hex << p.id() << "->" << mouseGrabberItem;
+ qCDebug(lcTouchTarget) << q << device << "TP (mouse)" << Qt::hex << p.id() << "->" << mouseGrabberItem;
return event.isAccepted();
} else {
// no grabber, check if we care about mouse hover
@@ -217,7 +222,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *poi
lastMousePosition = me.scenePosition();
bool accepted = me.isAccepted();
- bool delivered = deliverHoverEvent(contentItem, me.scenePosition(), last, me.modifiers(), me.timestamp(), accepted);
+ bool delivered = deliverHoverEvent(rootItem, me.scenePosition(), last, me.modifiers(), me.timestamp(), accepted);
// take care of any exits
if (!delivered)
clearHover(me.timestamp());
@@ -260,9 +265,8 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *poi
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, bool cancel)
+void QQuickDeliveryAgentPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch, bool cancel)
{
- Q_Q(QQuickWindow);
if (eventsInDelivery.isEmpty()) {
// do it the expensive way
for (auto dev : knownPointingDevices) {
@@ -272,13 +276,13 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to
return;
}
auto eventInDelivery = eventsInDelivery.top();
- if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber && eventInDelivery) {
- const bool fromTouch = isDeliveringTouchAsMouse();
- auto point = eventInDelivery->pointById(fromTouch ? touchMouseId : 0);
- Q_ASSERT(point);
- QQuickItem *oldGrabber = qobject_cast<QQuickItem *>(eventInDelivery->exclusiveGrabber(*point));
- qCDebug(lcMouseTarget) << "removeGrabber" << oldGrabber << "-> null";
- eventInDelivery->setExclusiveGrabber(*point, nullptr);
+ if (Q_LIKELY(mouse) && eventInDelivery) {
+ auto epd = mousePointData();
+ if (epd && epd->exclusiveGrabber == grabber) {
+ QQuickItem *oldGrabber = qobject_cast<QQuickItem *>(epd->exclusiveGrabber);
+ qCDebug(lcMouseTarget) << "removeGrabber" << oldGrabber << "-> null";
+ eventInDelivery->setExclusiveGrabber(epd->eventPoint, nullptr);
+ }
}
if (Q_LIKELY(touch)) {
bool ungrab = false;
@@ -300,7 +304,7 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to
The item-local QEventPoint::position() is updated later, not here.
*/
-void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
+void QQuickDeliveryAgentPrivate::translateTouchEvent(QTouchEvent *touchEvent)
{
for (qsizetype i = 0; i != touchEvent->pointCount(); ++i) {
auto &pt = QMutableEventPoint::from(touchEvent->point(i));
@@ -333,18 +337,17 @@ static inline bool singleWindowOnScreen(QQuickWindow *win)
#endif
/*!
-Set the focus inside \a scope to be \a item.
-If the scope contains the active focus item, it will be changed to \a item.
-Calls notifyFocusChangesRecur for all changed items.
+ Set the focus inside \a scope to be \a item.
+ If the scope contains the active focus item, it will be changed to \a item.
+ Calls notifyFocusChangesRecur for all changed items.
*/
-void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
+void QQuickDeliveryAgentPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item,
+ Qt::FocusReason reason, FocusOptions options)
{
- Q_Q(QQuickWindow);
-
Q_ASSERT(item);
- Q_ASSERT(scope || item == contentItem);
+ Q_ASSERT(scope || item == rootItem);
- qCDebug(lcFocus) << "QQuickWindowPrivate::setFocusInScope():";
+ qCDebug(lcFocus) << "QQuickDeliveryAgentPrivate::setFocusInScope():";
qCDebug(lcFocus) << " scope:" << (QObject *)scope;
if (scope)
qCDebug(lcFocus) << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
@@ -364,7 +367,7 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q
QVarLengthArray<QQuickItem *, 20> changed;
// Does this change the active focus?
- if (item == contentItem || scopePrivate->activeFocus) {
+ if (item == rootItem || scopePrivate->activeFocus) {
oldActiveFocusItem = activeFocusItem;
if (item->isEnabled()) {
newActiveFocusItem = item;
@@ -395,7 +398,7 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q
}
}
- if (item != contentItem && !(options & DontChangeSubFocusItem)) {
+ if (item != rootItem && !(options & DontChangeSubFocusItem)) {
QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
if (oldSubFocusItem) {
QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
@@ -406,8 +409,7 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q
}
if (!(options & DontChangeFocusProperty)) {
- if (item != contentItem
- || windowHasFocus(q)
+ if (item != rootItem || windowHasFocus(rootItem->window())
#ifdef Q_OS_WEBOS
// Allow focused if there is only one window in the screen where it belongs.
// Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
@@ -419,7 +421,7 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q
}
}
- if (newActiveFocusItem && contentItem->hasFocus()) {
+ if (newActiveFocusItem && rootItem->hasFocus()) {
activeFocusItem = newActiveFocusItem;
QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
@@ -451,20 +453,18 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q
}
if (activeFocusItem != currentActiveFocusItem)
- emit q->focusObjectChanged(activeFocusItem);
+ emit rootItem->window()->focusObjectChanged(activeFocusItem);
if (!changed.isEmpty())
notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}
-void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
+void QQuickDeliveryAgentPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
{
- Q_Q(QQuickWindow);
-
Q_ASSERT(item);
- Q_ASSERT(scope || item == contentItem);
+ Q_ASSERT(scope || item == rootItem);
- qCDebug(lcFocus) << "QQuickWindowPrivate::clearFocusInScope():";
+ qCDebug(lcFocus) << "QQuickDeliveryAgentPrivate::clearFocusInScope():";
qCDebug(lcFocus) << " scope:" << (QObject *)scope;
qCDebug(lcFocus) << " item:" << (QObject *)item;
qCDebug(lcFocus) << " activeFocusItem:" << (QObject *)activeFocusItem;
@@ -473,7 +473,7 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item,
if (scope) {
scopePrivate = QQuickItemPrivate::get(scope);
if ( !scopePrivate->subFocusItem )
- return;//No focus, nothing to do.
+ return; // No focus, nothing to do.
}
QQuickItem *currentActiveFocusItem = activeFocusItem;
@@ -484,10 +484,10 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item,
QVarLengthArray<QQuickItem *, 20> changed;
- Q_ASSERT(item == contentItem || item == scopePrivate->subFocusItem);
+ Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
// Does this change the active focus?
- if (item == contentItem || scopePrivate->activeFocus) {
+ if (item == rootItem || scopePrivate->activeFocus) {
oldActiveFocusItem = activeFocusItem;
newActiveFocusItem = scope;
@@ -509,7 +509,7 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item,
}
}
- if (item != contentItem && !(options & DontChangeSubFocusItem)) {
+ if (item != rootItem && !(options & DontChangeSubFocusItem)) {
QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
@@ -530,8 +530,7 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item,
}
// Now that all the state is changed, emit signals & events
- // We must do this last, as this process may result in further changes to
- // focus.
+ // We must do this last, as this process may result in further changes to focus.
if (oldActiveFocusItem) {
QFocusEvent event(QEvent::FocusOut, reason);
QCoreApplication::sendEvent(oldActiveFocusItem, &event);
@@ -544,21 +543,21 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item,
}
if (activeFocusItem != currentActiveFocusItem)
- emit q->focusObjectChanged(activeFocusItem);
+ emit rootItem->window()->focusObjectChanged(activeFocusItem);
if (!changed.isEmpty())
notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}
-void QQuickWindowPrivate::clearFocusObject()
+void QQuickDeliveryAgentPrivate::clearFocusObject()
{
- if (activeFocusItem == contentItem)
+ if (activeFocusItem == rootItem)
return;
- clearFocusInScope(contentItem, QQuickItemPrivate::get(contentItem)->subFocusItem, Qt::OtherFocusReason);
+ clearFocusInScope(rootItem, QQuickItemPrivate::get(rootItem)->subFocusItem, Qt::OtherFocusReason);
}
-void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
+void QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
{
QPointer<QQuickItem> item(*items);
@@ -581,61 +580,152 @@ void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remain
}
}
-bool QQuickWindowPrivate::clearHover(ulong timestamp)
+bool QQuickDeliveryAgentPrivate::clearHover(ulong timestamp)
{
- Q_Q(QQuickWindow);
if (hoverItems.isEmpty())
return false;
- QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
+ QQuickWindow *window = rootItem->window();
+ if (!window)
+ return false;
+
+ QPointF pos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
bool accepted = false;
for (QQuickItem* item : qAsConst(hoverItems)) {
- accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
+ if (item)
+ accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
}
hoverItems.clear();
return accepted;
}
-/*! \reimp */
-bool QQuickWindow::event(QEvent *e)
+void QQuickDeliveryAgentPrivate::updateFocusItemTransform()
{
- Q_D(QQuickWindow);
+#if QT_CONFIG(im)
+ if (activeFocusItem && QGuiApplication::focusObject() == activeFocusItem) {
+ QQuickItemPrivate *focusPrivate = QQuickItemPrivate::get(activeFocusItem);
+ QGuiApplication::inputMethod()->setInputItemTransform(focusPrivate->itemToWindowTransform());
+ QGuiApplication::inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height));
+ activeFocusItem->updateInputMethod(Qt::ImInputItemClipRectangle);
+ }
+#endif
+}
- switch (e->type()) {
+/*! \internal
+ QQuickDeliveryAgent delivers events to a tree of Qt Quick Items, beginning
+ with the given root item, which is usually QQuickWindow::rootItem() but
+ may alternatively be embedded into a Qt Quick 3D scene or something else.
+*/
+QQuickDeliveryAgent::QQuickDeliveryAgent(QQuickItem *rootItem)
+ : QObject(*new QQuickDeliveryAgentPrivate(rootItem), rootItem)
+{
+}
+QQuickDeliveryAgent::~QQuickDeliveryAgent()
+{
+}
+
+QQuickDeliveryAgent::Transform::~Transform()
+{
+}
+
+/*! \internal
+ Returns the QQuickDeliveryAgent instance that we remember was delivering the
+ given \a pt at the time that it was grabbed. Failing that, choose a suitable agent.
+*/
+QQuickDeliveryAgent *QQuickDeliveryAgent::grabberAgent(QPointerEvent *pe, const QEventPoint &pt)
+{
+ auto devExtra = QQuickDeliveryAgentPrivate::deviceExtra(pe->device());
+ QQuickDeliveryAgent *ret = devExtra->grabbedEventPointDeliveryAgents.value(pt.id());
+ if (ret) {
+ qCDebug(lcPtr) << pe->type() << "point" << pt.id() << pt.state()
+ << "@" << pt.scenePosition() << "will be re-delivered via known agent" << ret;
+ }
+ return ret;
+}
+
+QQuickItem *QQuickDeliveryAgent::rootItem() const
+{
+ Q_D(const QQuickDeliveryAgent);
+ return d->rootItem;
+}
+
+/*! \internal
+ QQuickDeliveryAgent takes ownership of the given \a transform, which
+ encapsulates the ability to transform viewport coordinates to rootItem coordinates.
+*/
+void QQuickDeliveryAgent::setSceneTransform(QQuickDeliveryAgent::Transform *transform)
+{
+ Q_D(QQuickDeliveryAgent);
+ if (d->sceneTransform == transform)
+ return;
+ qCDebug(lcPtr) << this << d->sceneTransform << "->" << transform;
+ if (d->sceneTransform)
+ delete d->sceneTransform;
+ d->sceneTransform = transform;
+}
+
+bool QQuickDeliveryAgent::event(QEvent *ev)
+{
+ Q_D(QQuickDeliveryAgent);
+
+ switch (ev->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove: {
+ QMouseEvent *me = static_cast<QMouseEvent*>(ev);
+ d->handleMouseEvent(me);
+ break;
+ }
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::HoverMove: {
+ QHoverEvent *he = static_cast<QHoverEvent*>(ev);
+ bool accepted = he->isAccepted();
+ bool delivered = d->deliverHoverEvent(d->rootItem, he->scenePosition(),
+ he->points().first().sceneLastPosition(),
+ he->modifiers(), he->timestamp(), accepted);
+ d->lastMousePosition = he->scenePosition();
+ he->setAccepted(accepted);
+#if QT_CONFIG(cursor)
+ QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(d->sceneTransform ?
+ d->sceneTransform->map(he->scenePosition()) : he->scenePosition(), d->rootItem);
+#endif
+ return delivered;
+ }
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd: {
- QTouchEvent *touch = static_cast<QTouchEvent*>(e);
+ QTouchEvent *touch = static_cast<QTouchEvent*>(ev);
d->handleTouchEvent(touch);
if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
// we consume all touch events ourselves to avoid duplicate
// mouse delivery by QtGui mouse synthesis
- e->accept();
+ ev->accept();
}
- return true;
- }
break;
+ }
case QEvent::TouchCancel:
// return in order to avoid the QWindow::event below
- return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
+ return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(ev));
break;
case QEvent::Enter: {
- if (!d->contentItem)
+ if (!d->rootItem)
return false;
- QEnterEvent *enter = static_cast<QEnterEvent*>(e);
+ QEnterEvent *enter = static_cast<QEnterEvent*>(ev);
bool accepted = enter->isAccepted();
- bool delivered = d->deliverHoverEvent(d->contentItem, enter->scenePosition(), d->lastMousePosition,
- QGuiApplication::keyboardModifiers(), 0L, accepted);
+ bool delivered = d->deliverHoverEvent(d->rootItem, enter->scenePosition(),
+ enter->points().first().sceneLastPosition(),
+ enter->modifiers(), enter->timestamp(), accepted);
d->lastMousePosition = enter->scenePosition();
enter->setAccepted(accepted);
#if QT_CONFIG(cursor)
- d->updateCursor(mapFromGlobal(QCursor::pos()));
+ QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(enter->scenePosition(), d->rootItem);
#endif
return delivered;
}
- break;
case QEvent::Leave:
d->clearHover();
d->lastMousePosition = QPointF();
@@ -645,43 +735,23 @@ bool QQuickWindow::event(QEvent *e)
case QEvent::DragLeave:
case QEvent::DragMove:
case QEvent::Drop:
- d->deliverDragEvent(d->dragGrabber, e);
+ d->deliverDragEvent(d->dragGrabber, ev);
break;
#endif
- case QEvent::WindowDeactivate:
- d->handleWindowDeactivate();
- break;
- case QEvent::PlatformSurface:
- if ((static_cast<QPlatformSurfaceEvent *>(e))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
- // Ensure that the rendering thread is notified before
- // the QPlatformWindow is destroyed.
- if (d->windowManager)
- d->windowManager->hide(this);
- }
- break;
case QEvent::FocusAboutToChange:
#if QT_CONFIG(im)
if (d->activeFocusItem)
qGuiApp->inputMethod()->commit();
#endif
break;
- case QEvent::UpdateRequest: {
- if (d->windowManager)
- d->windowManager->handleUpdateRequest(this);
- break;
- }
#if QT_CONFIG(gestures)
case QEvent::NativeGesture:
- d->deliverSinglePointEventUntilAccepted(static_cast<QPointerEvent *>(e));
+ d->deliverSinglePointEventUntilAccepted(static_cast<QPointerEvent *>(ev));
break;
#endif
case QEvent::ShortcutOverride:
if (d->activeFocusItem)
- QCoreApplication::sendEvent(d->activeFocusItem, e);
- return true;
- case QEvent::LanguageChange:
- if (d->contentItem)
- QCoreApplication::sendEvent(d->contentItem, e);
+ QCoreApplication::sendEvent(d->activeFocusItem, ev);
break;
case QEvent::InputMethod:
case QEvent::InputMethodQuery:
@@ -689,38 +759,52 @@ bool QQuickWindow::event(QEvent *e)
QQuickItem *target = d->activeFocusItem;
// while an input method delivers the event, this window might still be inactive
if (!target) {
- target = d->contentItem;
+ target = d->rootItem;
if (!target || !target->isEnabled())
break;
// see setFocusInScope for a similar loop
while (target->isFocusScope() && target->scopedFocusItem() && target->scopedFocusItem()->isEnabled())
target = target->scopedFocusItem();
}
- if (target) {
- QCoreApplication::sendEvent(target, e);
- return true;
- }
+ if (target)
+ QCoreApplication::sendEvent(target, ev);
}
break;
- default:
+ case QEvent::Wheel: {
+ auto event = static_cast<QWheelEvent *>(ev);
+ qCDebug(lcMouse) << event;
+
+ //if the actual wheel event was accepted, accept the compatibility wheel event and return early
+ if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate)
+ return true;
+
+ event->ignore();
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel,
+ event->angleDelta().x(), event->angleDelta().y());
+ d->deliverSinglePointEventUntilAccepted(event);
+ d->lastWheelEventAccepted = event->isAccepted();
break;
}
+ default:
+ return false;
+ }
- if (e->type() == QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))
- update();
- else if (e->type() == QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure))
- d->windowManager->handleContextCreationFailure(this);
-
- return QWindow::event(e);
+ return true;
}
-void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e)
+void QQuickDeliveryAgentPrivate::deliverKeyEvent(QKeyEvent *e)
{
if (activeFocusItem) {
+ const bool keyPress = (e->type() == QEvent::KeyPress);
+ if (keyPress)
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(), e->modifiers());
+ else
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(), e->modifiers());
+
QQuickItem *item = activeFocusItem;
// In case of generated event, trigger ShortcutOverride event
- if (e->type() == QEvent::KeyPress && e->spontaneous() == false)
+ if (keyPress && e->spontaneous() == false)
qt_sendShortcutOverrideEvent(item, e->timestamp(),
e->key(), e->modifiers(), e->text(),
e->isAutoRepeat(), e->count());
@@ -734,6 +818,39 @@ void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e)
}
}
+QQuickDeliveryAgentPrivate::QQuickDeliveryAgentPrivate(QQuickItem *root) :
+ QObjectPrivate(),
+ rootItem(root),
+ // a plain QQuickItem can be a subscene root; a QQuickRootItem always belongs directly to a QQuickWindow
+ isSubsceneAgent(!qmlobject_cast<QQuickRootItem *>(rootItem))
+{
+#if QT_CONFIG(quick_draganddrop)
+ dragGrabber = new QQuickDragGrabber;
+#endif
+}
+
+QQuickDeliveryAgentPrivate::~QQuickDeliveryAgentPrivate()
+{
+ Q_Q(QQuickDeliveryAgent);
+ for (auto dev : knownPointingDevices) {
+ auto devPriv = QPointingDevicePrivate::get(dev);
+ if (devPriv->qqExtra) {
+ auto &flatmap = static_cast<QQuickPointingDeviceExtra *>(devPriv->qqExtra)->grabbedEventPointDeliveryAgents;
+ for (auto it = flatmap.begin(); it != flatmap.end(); ) {
+ if (it.value() == q)
+ it = flatmap.erase(it);
+ else
+ ++it;
+ }
+ }
+ }
+#if QT_CONFIG(quick_draganddrop)
+ delete dragGrabber;
+ dragGrabber = nullptr;
+#endif
+ delete sceneTransform;
+}
+
/*! \internal
Make a copy of any type of QPointerEvent, and optionally localize it
by setting its first point's local position() if \a transformedLocalPos is given.
@@ -741,7 +858,7 @@ void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e)
\note some subclasses of QSinglePointEvent, such as QWheelEvent, add extra storage.
This function doesn't yet support cloning all of those; it can be extended if needed.
*/
-QPointerEvent *QQuickWindowPrivate::clonePointerEvent(QPointerEvent *event, std::optional<QPointF> transformedLocalPos)
+QPointerEvent *QQuickDeliveryAgentPrivate::clonePointerEvent(QPointerEvent *event, std::optional<QPointF> transformedLocalPos)
{
QPointerEvent *ret = event->clone();
QMutableEventPoint &point = QMutableEventPoint::from(ret->point(0));
@@ -753,8 +870,8 @@ QPointerEvent *QQuickWindowPrivate::clonePointerEvent(QPointerEvent *event, std:
return ret;
}
-void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QObject> > &passiveGrabbers,
- QPointerEvent *pointerEvent)
+void QQuickDeliveryAgentPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QObject> > &passiveGrabbers,
+ QPointerEvent *pointerEvent)
{
const QVector<QObject *> &eventDeliveryTargets =
QQuickPointerHandlerPrivate::deviceDeliveryTargets(pointerEvent->device());
@@ -786,7 +903,7 @@ void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QObje
}
}
-bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
+bool QQuickDeliveryAgentPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp,
bool accepted)
@@ -808,23 +925,21 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
}
// TODO later: specify the device in case of multi-mouse scenario, or mouse and tablet both in use
-bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
- Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted)
+bool QQuickDeliveryAgentPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
+ Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted)
{
- Q_Q(QQuickWindow);
+ Q_Q(QQuickDeliveryAgent);
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ const QPointF itemPos = item->mapFromScene(sceneTransform ? sceneTransform->map(scenePos) : scenePos);
- if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- QPointF p = item->mapFromScene(scenePos);
- if (!item->contains(p))
- return false;
- }
+ if ((itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape || item == rootItem) && !item->contains(itemPos))
+ return false;
if (Q_UNLIKELY(lcHoverTrace().isDebugEnabled())) {
if (lastScenePos == scenePos)
- qCDebug(lcHoverTrace) << scenePos << "(unchanged)" << item << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled << "in window" << windowTitle;
+ qCDebug(lcHoverTrace) << q << scenePos << "(unchanged)" << "," << itemPos << "in" << item << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled;
else
- qCDebug(lcHoverTrace) << lastScenePos << "->" << scenePos << item << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled << "in window" << windowTitle;
+ qCDebug(lcHoverTrace) << q << lastScenePos << "->" << scenePos << "," << itemPos << "in" << item << ", subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled;
}
if (itemPrivate->subtreeHoverEnabled) {
QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
@@ -840,8 +955,9 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
}
if (itemPrivate->hasPointerHandlers()) {
- const QPointF localPos = item->mapFromScene(scenePos);
- QMouseEvent hoverEvent(QEvent::MouseMove, localPos, scenePos, q->mapToGlobal(scenePos), Qt::NoButton, Qt::NoButton, modifiers);
+ QMouseEvent hoverEvent(QEvent::MouseMove, itemPos, scenePos,
+ itemPrivate->window->mapToGlobal(scenePos), Qt::NoButton, Qt::NoButton, modifiers);
+ ensureDeviceConnected(hoverEvent.pointingDevice());
hoverEvent.setTimestamp(timestamp);
hoverEvent.setAccepted(true);
for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
@@ -849,53 +965,50 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
hh->handlePointerEvent(&hoverEvent);
}
- if (itemPrivate->hoverEnabled) {
- QPointF p = item->mapFromScene(scenePos);
- if (item->contains(p)) {
- if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {
- //move
+ if (itemPrivate->hoverEnabled && item->contains(itemPos)) {
+ if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {
+ //move
+ accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
+ } else {
+ QList<QQuickItem *> itemsToHover;
+ QQuickItem* parent = item;
+ itemsToHover << item;
+ while ((parent = parent->parentItem()))
+ itemsToHover << parent;
+
+ // Leaving from previous hovered items until we reach the item or one of its ancestors.
+ while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems.at(0))) {
+ QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
+ sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted);
+ }
+
+ if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {//Not entering a new Item
+ // ### Shouldn't we send moves for the parent items as well?
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
} else {
- QList<QQuickItem *> itemsToHover;
- QQuickItem* parent = item;
- itemsToHover << item;
- while ((parent = parent->parentItem()))
- itemsToHover << parent;
-
- // Leaving from previous hovered items until we reach the item or one of its ancestors.
- while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems.at(0))) {
- QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
- sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted);
- }
-
- if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {//Not entering a new Item
- // ### Shouldn't we send moves for the parent items as well?
- accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
- } else {
- // Enter items that are not entered yet.
- int startIdx = -1;
- if (!hoverItems.isEmpty())
- startIdx = itemsToHover.indexOf(hoverItems.at(0)) - 1;
- if (startIdx == -1)
- startIdx = itemsToHover.count() - 1;
-
- for (int i = startIdx; i >= 0; i--) {
- QQuickItem *itemToHover = itemsToHover.at(i);
- QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(itemToHover);
- // The item may be about to be deleted or reparented to another window
- // due to another hover event delivered in this function. If that is the
- // case, sending a hover event here will cause a crash or other bad
- // behavior when the leave event is generated. Checking
- // itemToHoverPrivate->window here prevents that case.
- if (itemToHoverPrivate->window == q && itemToHoverPrivate->hoverEnabled) {
- hoverItems.prepend(itemToHover);
- sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, timestamp, accepted);
- }
+ // Enter items that are not entered yet.
+ int startIdx = -1;
+ if (!hoverItems.isEmpty())
+ startIdx = itemsToHover.indexOf(hoverItems.at(0)) - 1;
+ if (startIdx == -1)
+ startIdx = itemsToHover.count() - 1;
+
+ for (int i = startIdx; i >= 0; i--) {
+ QQuickItem *itemToHover = itemsToHover.at(i);
+ QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(itemToHover);
+ // The item may be about to be deleted or reparented to another window
+ // due to another hover event delivered in this function. If that is the
+ // case, sending a hover event here will cause a crash or other bad
+ // behavior when the leave event is generated. Checking
+ // itemToHoverPrivate->window here prevents that case.
+ if (itemToHoverPrivate->window == itemPrivate->window && itemToHoverPrivate->hoverEnabled) {
+ hoverItems.prepend(itemToHover);
+ sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, timestamp, accepted);
}
}
}
- return true;
}
+ return true;
}
return false;
@@ -903,12 +1016,12 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
// in the usual reverse-paint-order until propagation is stopped
-bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QPointerEvent *event)
+bool QQuickDeliveryAgentPrivate::deliverSinglePointEventUntilAccepted(QPointerEvent *event)
{
Q_ASSERT(event->points().count() == 1);
QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->pointingDevice()).clear();
QEventPoint &point = event->point(0);
- QVector<QQuickItem *> targetItems = pointerTargets(contentItem, event, point, false, false);
+ QVector<QQuickItem *> targetItems = pointerTargets(rootItem, event, point, false, false);
point.setAccepted(false);
for (QQuickItem *item : targetItems) {
@@ -929,7 +1042,7 @@ bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QPointerEvent *ev
return false; // it wasn't handled
}
-bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
+bool QQuickDeliveryAgentPrivate::deliverTouchCancelEvent(QTouchEvent *event)
{
qCDebug(lcTouch) << event;
@@ -938,11 +1051,11 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
// Deliver it to all items and handlers that have active touches.
const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(event->pointingDevice()))->
sendTouchCancelEvent(event);
- cancelTouchMouseSynthesis();
+
return true;
}
-void QQuickWindowPrivate::deliverDelayedTouchEvent()
+void QQuickDeliveryAgentPrivate::deliverDelayedTouchEvent()
{
// Deliver and delete delayedTouch.
// Set delayedTouch to nullptr before delivery to avoid redelivery in case of
@@ -962,10 +1075,9 @@ void QQuickWindowPrivate::deliverDelayedTouchEvent()
just a plain QEvent with no extra data, and because the application state
change is delivered via a signal rather than an event.
*/
-void QQuickWindowPrivate::handleWindowDeactivate()
+void QQuickDeliveryAgentPrivate::handleWindowDeactivate(QQuickWindow *win)
{
- Q_Q(QQuickWindow);
- qCDebug(lcFocus) << "deactivated" << windowTitle;
+ qCDebug(lcFocus) << "deactivated" << win->title();
const auto inputDevices = QInputDevice::devices();
for (auto device : inputDevices) {
if (auto pointingDevice = qobject_cast<const QPointingDevice *>(device)) {
@@ -974,9 +1086,9 @@ void QQuickWindowPrivate::handleWindowDeactivate()
if (!epd.exclusiveGrabber.isNull()) {
bool relevant = false;
if (QQuickItem *item = qmlobject_cast<QQuickItem *>(epd.exclusiveGrabber.data()))
- relevant = (item->window() == q);
+ relevant = (item->window() == win);
else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(epd.exclusiveGrabber.data()))
- relevant = (handler->parentItem()->window() == q);
+ relevant = (handler->parentItem()->window() == win);
if (relevant)
devPriv->setExclusiveGrabber(nullptr, epd.eventPoint, nullptr);
}
@@ -987,7 +1099,7 @@ void QQuickWindowPrivate::handleWindowDeactivate()
}
}
-bool QQuickWindowPrivate::allUpdatedPointsAccepted(const QPointerEvent *ev)
+bool QQuickDeliveryAgentPrivate::allUpdatedPointsAccepted(const QPointerEvent *ev)
{
for (auto &point : ev->points()) {
if (point.state() != QEventPoint::State::Pressed && !point.isAccepted())
@@ -1002,15 +1114,17 @@ bool QQuickWindowPrivate::allUpdatedPointsAccepted(const QPointerEvent *ev)
Unlike QMutableTouchEvent::localized(), this modifies the QEventPoint
instances in \a ev, which is more efficient than making a copy.
*/
-void QQuickWindowPrivate::localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest)
+void QQuickDeliveryAgentPrivate::localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest)
{
for (int i = 0; i < ev->pointCount(); ++i) {
auto &point = QMutableEventPoint::from(ev->point(i));
QMutableEventPoint::from(point).setPosition(dest->mapFromScene(point.scenePosition()));
+ qCDebug(lcPtrLoc) << ev->type() << "@" << point.scenePosition() << "to"
+ << dest << "@" << dest->mapToScene(QPointF()) << "->" << point;
}
}
-QList<QObject *> QQuickWindowPrivate::exclusiveGrabbers(QPointerEvent *ev)
+QList<QObject *> QQuickDeliveryAgentPrivate::exclusiveGrabbers(QPointerEvent *ev)
{
QList<QObject *> result;
for (const QEventPoint &point : ev->points()) {
@@ -1022,7 +1136,16 @@ QList<QObject *> QQuickWindowPrivate::exclusiveGrabbers(QPointerEvent *ev)
return result;
}
-bool QQuickWindowPrivate::isMouseEvent(const QPointerEvent *ev)
+bool QQuickDeliveryAgentPrivate::anyPointGrabbed(const QPointerEvent *ev)
+{
+ for (const QEventPoint &point : ev->points()) {
+ if (ev->exclusiveGrabber(point) || !ev->passiveGrabbers(point).isEmpty())
+ return true;
+ }
+ return false;
+}
+
+bool QQuickDeliveryAgentPrivate::isMouseEvent(const QPointerEvent *ev)
{
switch (ev->type()) {
case QEvent::MouseButtonPress:
@@ -1035,7 +1158,7 @@ bool QQuickWindowPrivate::isMouseEvent(const QPointerEvent *ev)
}
}
-bool QQuickWindowPrivate::isTouchEvent(const QPointerEvent *ev)
+bool QQuickDeliveryAgentPrivate::isTouchEvent(const QPointerEvent *ev)
{
switch (ev->type()) {
case QEvent::TouchBegin:
@@ -1048,7 +1171,7 @@ bool QQuickWindowPrivate::isTouchEvent(const QPointerEvent *ev)
}
}
-bool QQuickWindowPrivate::isTabletEvent(const QPointerEvent *ev)
+bool QQuickDeliveryAgentPrivate::isTabletEvent(const QPointerEvent *ev)
{
switch (ev->type()) {
case QEvent::TabletPress:
@@ -1062,9 +1185,22 @@ bool QQuickWindowPrivate::isTabletEvent(const QPointerEvent *ev)
}
}
-bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event)
+QQuickPointingDeviceExtra *QQuickDeliveryAgentPrivate::deviceExtra(const QInputDevice *device)
+{
+ QInputDevicePrivate *devPriv = QInputDevicePrivate::get(const_cast<QInputDevice *>(device));
+ if (devPriv->qqExtra)
+ return static_cast<QQuickPointingDeviceExtra *>(devPriv->qqExtra);
+ auto extra = new QQuickPointingDeviceExtra;
+ devPriv->qqExtra = extra;
+ QObject::connect(device, &QObject::destroyed, [devPriv]() {
+ delete static_cast<QQuickPointingDeviceExtra *>(devPriv->qqExtra);
+ devPriv->qqExtra = nullptr;
+ });
+ return extra;
+}
+
+bool QQuickDeliveryAgentPrivate::compressTouchEvent(QTouchEvent *event)
{
- Q_Q(QQuickWindow);
QEventPoint::States states = event->touchPointStates();
if (states.testFlag(QEventPoint::State::Pressed) || states.testFlag(QEventPoint::State::Released)) {
// we can only compress an event that doesn't include any pressed or released points
@@ -1075,10 +1211,8 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event)
delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), event->points()));
delayedTouch->setTimestamp(event->timestamp());
qCDebug(lcTouchCmprs) << "delayed" << delayedTouch.data();
- if (renderControl)
- QQuickRenderControlPrivate::get(renderControl)->maybeUpdate();
- else if (windowManager)
- windowManager->maybeUpdate(q);
+ if (QQuickWindow *window = rootItem->window())
+ window->maybeUpdate();
return true;
}
@@ -1127,8 +1261,9 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event)
// - translate the event to window coordinates
// - compress the event instead of delivering it if applicable
// - call deliverTouchPoints to actually dispatch the points
-void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event)
+void QQuickDeliveryAgentPrivate::handleTouchEvent(QTouchEvent *event)
{
+ Q_Q(QQuickDeliveryAgent);
translateTouchEvent(event);
// TODO remove: touch and mouse should be independent until we come to touch->mouse synth
if (event->pointCount()) {
@@ -1140,7 +1275,7 @@ void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event)
}
}
- qCDebug(lcTouch) << event;
+ qCDebug(lcTouch) << q << event;
static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION");
@@ -1158,13 +1293,14 @@ void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event)
}
}
-void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
+void QQuickDeliveryAgentPrivate::handleMouseEvent(QMouseEvent *event)
{
+ Q_Q(QQuickDeliveryAgent);
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
event->accept();
return;
}
- qCDebug(lcMouse) << event;
+ qCDebug(lcMouse) << q << event;
switch (event->type()) {
case QEvent::MouseButtonPress:
@@ -1177,7 +1313,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
event->buttons());
deliverPointerEvent(event);
#if QT_CONFIG(cursor)
- updateCursor(event->scenePosition());
+ QQuickWindowPrivate::get(rootItem->window())->updateCursor(event->scenePosition());
#endif
break;
case QEvent::MouseButtonDblClick:
@@ -1190,17 +1326,15 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
event->position().x(), event->position().y());
- qCDebug(lcHoverTrace) << this;
-
- #if QT_CONFIG(cursor)
- updateCursor(event->scenePosition());
- #endif
+#if QT_CONFIG(cursor)
+ QQuickWindowPrivate::get(rootItem->window())->updateCursor(event->scenePosition());
+#endif
if (!event->points().count() || !event->exclusiveGrabber(event->point(0))) {
QPointF last = lastMousePosition.isNull() ? event->scenePosition() : lastMousePosition;
lastMousePosition = event->scenePosition();
-
+ qCDebug(lcHoverTrace) << q << "mouse pos" << last << "->" << lastMousePosition << event;
bool accepted = event->isAccepted();
- bool delivered = deliverHoverEvent(contentItem, event->scenePosition(), last, event->modifiers(), event->timestamp(), accepted);
+ bool delivered = deliverHoverEvent(rootItem, event->scenePosition(), last, event->modifiers(), event->timestamp(), accepted);
if (!delivered) {
//take care of any exits
accepted = clearHover(event->timestamp());
@@ -1216,9 +1350,9 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
}
}
-void QQuickWindowPrivate::flushFrameSynchronousEvents()
+void QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents(QQuickWindow *win)
{
- Q_Q(QQuickWindow);
+ Q_Q(QQuickDeliveryAgent);
if (delayedTouch) {
deliverDelayedTouchEvent();
@@ -1239,23 +1373,39 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents()
// whose delegates contain MouseAreas), a MouseArea needs to know
// whether it has moved into a position where it is now under the cursor.
// TODO do this for each known mouse device or come up with a different strategy
- if (!q->mouseGrabberItem() && !lastMousePosition.isNull() && dirtyItemList) {
+ if (frameSynchronousHoverEnabled && !win->mouseGrabberItem() &&
+ !lastMousePosition.isNull() && QQuickWindowPrivate::get(win)->dirtyItemList) {
bool accepted = false;
- bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted);
+ qCDebug(lcHoverTrace) << q << "delivering frame-sync hover to root";
+ bool delivered = deliverHoverEvent(rootItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted);
if (!delivered)
clearHover(); // take care of any exits
+ qCDebug(lcHoverTrace) << q << "frame-sync hover delivery done";
}
#endif
}
-void QQuickWindowPrivate::onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition,
- const QPointerEvent *event, const QEventPoint &point)
+void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition,
+ const QPointerEvent *event, const QEventPoint &point)
{
- qCDebug(lcPtrGrab) << grabber << transition << event << point;
+ Q_Q(QQuickDeliveryAgent);
+ const bool grabGained = (transition == QPointingDevice::GrabTransition::GrabExclusive ||
+ transition == QPointingDevice::GrabTransition::GrabPassive);
+
+ QQuickDeliveryAgent *subsceneAgent = nullptr;
+
// note: event can be null, if the signal was emitted from QPointingDevicePrivate::removeGrabber(grabber)
if (auto *handler = qmlobject_cast<QQuickPointerHandler *>(grabber)) {
handler->onGrabChanged(handler, transition, const_cast<QPointerEvent *>(event),
const_cast<QEventPoint &>(point));
+ if (isSubsceneAgent) {
+ auto itemPriv = QQuickItemPrivate::get(handler->parentItem());
+ // An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
+ // whereas the subscene root item already knows it has its own DA.
+ if (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent)
+ itemPriv->hasSubsceneDeliveryAgent = grabGained;
+ subsceneAgent = itemPriv->deliveryAgent();
+ }
} else {
switch (transition) {
case QPointingDevice::CancelGrabExclusive:
@@ -1291,28 +1441,77 @@ void QQuickWindowPrivate::onGrabChanged(QObject *grabber, QPointingDevice::GrabT
default:
break;
}
+ auto grabberItem = static_cast<QQuickItem *>(grabber); // cannot be a handler: we checked above
+ if (isSubsceneAgent && grabberItem) {
+ auto itemPriv = QQuickItemPrivate::get(grabberItem);
+ // An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
+ // whereas the subscene root item already knows it has its own DA.
+ if (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent)
+ itemPriv->hasSubsceneDeliveryAgent = grabGained;
+ subsceneAgent = itemPriv->deliveryAgent();
+ }
+ }
+
+ if (subsceneAgent == q && event && event->device()) {
+ auto devExtra = QQuickDeliveryAgentPrivate::deviceExtra(event->device());
+ QFlatMap<int, QQuickDeliveryAgent*> &agentMap = devExtra->grabbedEventPointDeliveryAgents;
+ // workaround for QFlatMap error: somehow having a local copy of id makes insert() happy (move semantics?) otherwise we get
+ // no matching function for call to ‘QList<int>::insert(QFlatMap<int, QQuickDeliveryAgent*>::iterator&, std::remove_reference<int&>::type)’
+ const int id = point.id();
+ if (grabGained) {
+ // If any grab is gained while a subscene agent is delivering an event,
+ // the same agent should keep delivering all subsequent events containing that QEventPoint.
+ qCDebug(lcPtr) << "remembering that" << q << "handles point" << id << "after" << transition;
+ agentMap.insert(id, q);
+ } else if (!event->exclusiveGrabber(point) && event->passiveGrabbers(point).isEmpty()) {
+ // If all grabs are lost, we can forget the fact that a particular agent was handling a particular point.
+ // If the event point ID appears again in a later event, it will be delivered via the main window's delivery agent by default.
+ qCDebug(lcPtr) << "dissociating" << q << "from point" << id << "after" << transition;
+ agentMap.remove(id);
+ }
}
}
-void QQuickWindowPrivate::ensureDeviceConnected(const QPointingDevice *dev)
+void QQuickDeliveryAgentPrivate::ensureDeviceConnected(const QPointingDevice *dev)
{
if (knownPointingDevices.contains(dev))
return;
knownPointingDevices.append(dev);
- connect(dev, &QPointingDevice::grabChanged, this, &QQuickWindowPrivate::onGrabChanged);
+ connect(dev, &QPointingDevice::grabChanged, this, &QQuickDeliveryAgentPrivate::onGrabChanged);
}
-void QQuickWindowPrivate::deliverPointerEvent(QPointerEvent *event)
+void QQuickDeliveryAgentPrivate::deliverPointerEvent(QPointerEvent *event)
{
+ Q_Q(QQuickDeliveryAgent);
+ if (isTabletEvent(event))
+ qCDebug(lcTablet) << q << event;
+
// If users spin the eventloop as a result of event delivery, we disable
// event compression and send events directly. This is because we consider
// the usecase a bit evil, but we at least don't want to lose events.
++pointerEventRecursionGuard;
eventsInDelivery.push(event);
+ // So far this is for use in Qt Quick 3D: if a QEventPoint is grabbed,
+ // updates get delivered here pretty directly, bypassing picking; but we need to
+ // be able to map the 2D viewport coordinate to a 2D coordinate within
+ // d->rootItem, a 2D scene that has been arbitrarily mapped onto a 3D object.
+ if (sceneTransform) {
+ for (int i = 0; i < event->pointCount(); ++i) {
+ auto &mut = QMutableEventPoint::from(event->point(i));
+ mut.setScenePosition(sceneTransform->map(mut.scenePosition()));
+ qCDebug(lcPtrLoc) << q << event->type() << mut.id() << "transformed scene pos" << mut.scenePosition();
+ }
+ } else if (isSubsceneAgent) {
+ qCDebug(lcPtrLoc) << q << event->type() << "no scene transform set";
+ }
+
skipDelivery.clear();
QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->pointingDevice()).clear();
- qCDebug(lcPtr) << "delivering" << event;
+ if (sceneTransform)
+ qCDebug(lcPtr) << q << "delivering with" << sceneTransform << event;
+ else
+ qCDebug(lcPtr) << q << "delivering" << event;
for (int i = 0; i < event->pointCount(); ++i)
event->point(i).setAccepted(false);
@@ -1326,14 +1525,16 @@ void QQuickWindowPrivate::deliverPointerEvent(QPointerEvent *event)
if (event->isEndEvent())
deliverPressOrReleaseEvent(event, true);
- // failsafe: never allow any kind of grab to persist after release
+ // failsafe: never allow any kind of grab or point-agent association to persist after release
if (event->isEndEvent()) {
+ auto &agentMap = QQuickDeliveryAgentPrivate::deviceExtra(event->device())->grabbedEventPointDeliveryAgents;
if (isTouchEvent(event)) {
for (int i = 0; i < event->pointCount(); ++i) {
auto &point = event->point(i);
if (point.state() == QEventPoint::State::Released) {
event->setExclusiveGrabber(point, nullptr);
event->clearPassiveGrabbers(point);
+ agentMap.remove(point.id());
}
}
// never allow touch->mouse synthesis to persist either
@@ -1342,6 +1543,7 @@ void QQuickWindowPrivate::deliverPointerEvent(QPointerEvent *event)
auto &firstPt = event->point(0);
event->setExclusiveGrabber(firstPt, nullptr);
event->clearPassiveGrabbers(firstPt);
+ agentMap.remove(firstPt.id());
}
}
@@ -1355,13 +1557,15 @@ void QQuickWindowPrivate::deliverPointerEvent(QPointerEvent *event)
// 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 QPointerEvent *event, const QEventPoint &point,
+QVector<QQuickItem *> QQuickDeliveryAgentPrivate::pointerTargets(QQuickItem *item, const QPointerEvent *event, const QEventPoint &point,
bool checkMouseButtons, bool checkAcceptsTouch) const
{
+ Q_Q(const QQuickDeliveryAgent);
QVector<QQuickItem *> targets;
auto itemPrivate = QQuickItemPrivate::get(item);
QPointF itemPos = item->mapFromScene(point.scenePosition());
bool relevant = item->contains(itemPos);
+ qCDebug(lcPtrLoc) << q << "point" << point.id() << point.scenePosition() << "->" << itemPos << ": relevant?" << relevant << "to" << item << point;
// if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
if (!relevant)
@@ -1403,7 +1607,7 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons
// return the joined lists
// list1 has priority, common items come last
-QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const
+QVector<QQuickItem *> QQuickDeliveryAgentPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const
{
QVector<QQuickItem *> targets = list1;
// start at the end of list2
@@ -1426,7 +1630,7 @@ QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQu
/*! \internal
Deliver updated points to existing grabbers.
*/
-void QQuickWindowPrivate::deliverUpdatedPoints(QPointerEvent *event)
+void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event)
{
bool done = false;
const auto grabbers = exclusiveGrabbers(event);
@@ -1467,7 +1671,7 @@ void QQuickWindowPrivate::deliverUpdatedPoints(QPointerEvent *event)
// Don't find handlers for points that are already grabbed by an Item (such as Flickable).
if (point.state() == QEventPoint::Pressed || qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(point)))
continue;
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, event, point, false, false);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(rootItem, event, point, false, false);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -1487,7 +1691,7 @@ void QQuickWindowPrivate::deliverUpdatedPoints(QPointerEvent *event)
}
// Deliver an event containing newly pressed or released touch points
-bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QPointerEvent *event, bool handlersOnly)
+bool QQuickDeliveryAgentPrivate::deliverPressOrReleaseEvent(QPointerEvent *event, bool handlersOnly)
{
QVector<QQuickItem *> targetItems;
const bool isTouch = isTouchEvent(event);
@@ -1512,7 +1716,7 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QPointerEvent *event, bool
auto &point = event->point(i);
if (point.state() == QEventPoint::Pressed)
event->clearPassiveGrabbers(point);
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, event, point, !isTouch, isTouch);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(rootItem, event, point, !isTouch, isTouch);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -1546,7 +1750,7 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QPointerEvent *event, bool
return event->allPointsAccepted();
}
-void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly)
+void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly)
{
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
#if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
@@ -1596,7 +1800,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isG
auto &point = pointerEvent->point(0);
auto mouseGrabber = pointerEvent->exclusiveGrabber(point);
if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
- // Normally we don't need item->mouseUngrabEvent() here, because QQuickWindowPrivate::onGrabChanged does it.
+ // Normally we don't need item->mouseUngrabEvent() here, because QQuickDeliveryAgentPrivate::onGrabChanged does it.
// However, if one item accepted the mouse event, it expects to have the grab and be in "pressed" state,
// because accepting implies grabbing. But before it actually gets the grab, another item could steal it.
// In that case, onGrabChanged() does NOT notify the item that accepted the event that it's not getting the grab after all.
@@ -1619,7 +1823,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isG
bool eventAccepted = false;
QMutableTouchEvent touchEvent;
- QQuickItemPrivate::get(item)->localizedTouchEvent(static_cast<QTouchEvent *>(pointerEvent), false, &touchEvent);
+ itemPrivate->localizedTouchEvent(static_cast<QTouchEvent *>(pointerEvent), false, &touchEvent);
if (touchEvent.type() == QEvent::None)
return; // no points inside this item
@@ -1638,7 +1842,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isG
} else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
// If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
- // send mouse event
+ // send mouse event
if (deliverTouchAsMouse(item, &touchEvent))
eventAccepted = true;
}
@@ -1652,7 +1856,8 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isG
auto &point = QMutableEventPoint::from(touchEvent.point(i));
// legacy-style delivery: if the item doesn't reject the event, that means it handled ALL the points
point.setAccepted();
- if (isPressOrRelease)
+ // but don't let the root of a subscene implicitly steal the grab from some other item (such as one of its children)
+ if (isPressOrRelease && !(itemPrivate->deliveryAgent() && pointerEvent->exclusiveGrabber(point)))
pointerEvent->setExclusiveGrabber(point, item);
}
} else {
@@ -1670,7 +1875,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isG
}
#if QT_CONFIG(quick_draganddrop)
-void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
+void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
{
grabber->resetTarget();
QQuickDragGrabber::iterator grabItem = grabber->begin();
@@ -1715,7 +1920,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
moveEvent->buttons(),
moveEvent->modifiers());
QQuickDropEventEx::copyActions(&enterEvent, *moveEvent);
- event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent, &currentGrabItems));
+ event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent, &currentGrabItems));
for (grabItem = grabber->begin(); grabItem != grabber->end(); ++grabItem) {
int i = currentGrabItems.indexOf(**grabItem);
@@ -1752,11 +1957,11 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
e->buttons(),
e->modifiers());
QQuickDropEventEx::copyActions(&enterEvent, *e);
- event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent));
+ event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
}
}
-bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event, QVarLengthArray<QQuickItem*, 64> *currentGrabItems)
+bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event, QVarLengthArray<QQuickItem*, 64> *currentGrabItems)
{
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
@@ -1831,12 +2036,12 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte
}
#endif // quick_draganddrop
-bool QQuickWindowPrivate::sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
+bool QQuickDeliveryAgentPrivate::sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem());
}
-bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
+bool QQuickDeliveryAgentPrivate::sendFilteredPointerEventImpl(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
if (!allowChildEventFiltering)
return false;
@@ -1879,7 +2084,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QPointerEvent *event, QQu
qCDebug(lcTouchTarget) << "skipping filtering of synth-mouse event from" << device;
} else if (acceptsTouchEvents || receiver->acceptedMouseButtons()) {
// get a touch event customized for delivery to filteringParent
- // TODO should not be necessary? because QQuickWindowPrivate::deliverMatchingPointsToItem() does it
+ // TODO should not be necessary? because QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem() does it
QMutableTouchEvent filteringParentTouchEvent;
QQuickItemPrivate::get(receiver)->localizedTouchEvent(static_cast<QTouchEvent *>(event), true, &filteringParentTouchEvent);
if (filteringParentTouchEvent.type() != QEvent::None) {
@@ -1955,7 +2160,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QPointerEvent *event, QQu
return sendFilteredPointerEventImpl(event, receiver, filteringParent->parentItem()) || filtered;
}
-bool QQuickWindowPrivate::sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
+bool QQuickDeliveryAgentPrivate::sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
if (!filteringParent)
return false;
@@ -1977,7 +2182,7 @@ bool QQuickWindowPrivate::sendFilteredMouseEvent(QEvent *event, QQuickItem *rece
return sendFilteredMouseEvent(event, receiver, filteringParent->parentItem()) || filtered;
}
-bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold)
+bool QQuickDeliveryAgentPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold)
{
QStyleHints *styleHints = QGuiApplication::styleHints();
bool dragVelocityLimitAvailable = event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
@@ -1991,7 +2196,7 @@ bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent
return overThreshold;
}
-bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold)
+bool QQuickDeliveryAgentPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold)
{
QStyleHints *styleHints = qApp->styleHints();
bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
@@ -2003,10 +2208,38 @@ bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QEvent
return overThreshold;
}
-bool QQuickWindowPrivate::dragOverThreshold(QVector2D delta)
+bool QQuickDeliveryAgentPrivate::dragOverThreshold(QVector2D delta)
{
int threshold = qApp->styleHints()->startDragDistance();
return qAbs(delta.x()) > threshold || qAbs(delta.y()) > threshold;
}
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QQuickDeliveryAgent *da)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ if (!da) {
+ debug << "QQuickDeliveryAgent(0)";
+ return debug;
+ }
+
+ debug << "QQuickDeliveryAgent(";
+ if (!da->objectName().isEmpty())
+ debug << da->objectName() << ' ';
+ auto root = da->rootItem();
+ if (Q_LIKELY(root)) {
+ debug << "root=" << root->metaObject()->className();
+ if (!root->objectName().isEmpty())
+ debug << ' ' << root->objectName();
+ } else {
+ debug << "root=0";
+ }
+ debug << ')';
+ return debug;
+}
+#endif
+
QT_END_NAMESPACE
+
+#include "moc_qquickdeliveryagent_p.cpp"
diff --git a/src/quick/util/qquickdeliveryagent_p.h b/src/quick/util/qquickdeliveryagent_p.h
new file mode 100644
index 0000000000..41b3a5390b
--- /dev/null
+++ b/src/quick/util/qquickdeliveryagent_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKDELIVERYAGENT_P_H
+#define QQUICKDELIVERYAGENT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+
+#include <QtQml/qqml.h>
+#include <QtQml/private/qqmlglobal_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtGui/qevent.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickItem;
+class QQuickDeliveryAgentPrivate;
+
+class Q_QUICK_EXPORT QQuickDeliveryAgent : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQuickDeliveryAgent)
+
+public:
+ struct Q_QUICK_EXPORT Transform
+ {
+ virtual ~Transform();
+ virtual QPointF map(const QPointF &point) = 0;
+ };
+
+ explicit QQuickDeliveryAgent(QQuickItem *rootItem);
+ virtual ~QQuickDeliveryAgent();
+
+ static QQuickDeliveryAgent *grabberAgent(QPointerEvent *pe, const QEventPoint &pt);
+
+ QQuickItem *rootItem() const;
+
+ void setSceneTransform(Transform *transform);
+ bool event(QEvent *ev) override;
+
+Q_SIGNALS:
+
+private:
+ Q_DISABLE_COPY(QQuickDeliveryAgent)
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug Q_QUICK_EXPORT operator<<(QDebug debug, const QQuickDeliveryAgent *da);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QQUICKDELIVERYAGENT_P_H
diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h
new file mode 100644
index 0000000000..123eea1191
--- /dev/null
+++ b/src/quick/util/qquickdeliveryagent_p_p.h
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKDELIVERYAGENT_P_P_H
+#define QQUICKDELIVERYAGENT_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qquickdeliveryagent_p.h>
+#include <QtGui/qevent.h>
+#include <QtCore/qstack.h>
+
+#include <private/qevent_p.h>
+#include <private/qpointingdevice_p.h>
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickDragGrabber;
+class QQuickItem;
+class QQuickPointerHandler;
+class QQuickWindow;
+
+/*! \internal
+ Extra device-specific data to be stored in QInputDevicePrivate::qqExtra
+*/
+struct QQuickPointingDeviceExtra {
+ // used in QQuickPointerHandlerPrivate::deviceDeliveryTargets
+ QVector<QObject *> deliveryTargets;
+ // memory of which agent was delivering when each QEventPoint was grabbed
+ // TODO maybe add QEventPointPrivate::qqExtra, or sth in QPointingDevicePrivate::EventPointData
+ QFlatMap<int, QQuickDeliveryAgent*> grabbedEventPointDeliveryAgents;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickDeliveryAgentPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickDeliveryAgent)
+public:
+ QQuickDeliveryAgentPrivate(QQuickItem *root);
+ ~QQuickDeliveryAgentPrivate();
+
+ QQuickItem *rootItem = nullptr;
+
+ QQuickItem *activeFocusItem = nullptr;
+
+ void deliverKeyEvent(QKeyEvent *e);
+
+ enum FocusOption {
+ DontChangeFocusProperty = 0x01,
+ DontChangeSubFocusItem = 0x02
+ };
+ Q_DECLARE_FLAGS(FocusOptions, FocusOption)
+
+ void setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions = { });
+ void clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions = { });
+ static void notifyFocusChangesRecur(QQuickItem **item, int remaining);
+ void clearFocusObject();
+ void updateFocusItemTransform();
+
+ // Keeps track of the item currently receiving mouse events
+#if QT_CONFIG(quick_draganddrop)
+ QQuickDragGrabber *dragGrabber = nullptr;
+#endif
+ QQuickItem *lastUngrabbed = nullptr;
+ QStack<QPointerEvent *> eventsInDelivery;
+ QList<QPointer<QQuickItem>> hoverItems;
+ QVector<QQuickItem *> hasFiltered; // during event delivery to a single receiver, the filtering parents for which childMouseEventFilter was already called
+ QVector<QQuickItem *> skipDelivery; // during delivery of one event to all receivers, Items to which we know delivery is no longer necessary
+
+ QScopedPointer<QMutableTouchEvent> delayedTouch;
+ QList<const QPointingDevice *> knownPointingDevices;
+
+ uint lastWheelEventAccepted = 0;
+ bool allowChildEventFiltering = true;
+ bool allowDoubleClick = true;
+ bool frameSynchronousHoverEnabled = true;
+
+ bool isSubsceneAgent = false;
+
+ Qt::FocusReason lastFocusReason = Qt::OtherFocusReason;
+ int pointerEventRecursionGuard = 0;
+
+ int touchMouseId = -1; // only for obsolete stuff like QQuickItem::grabMouse()
+ // TODO get rid of these
+ const QPointingDevice *touchMouseDevice = nullptr;
+ ulong touchMousePressTimestamp = 0;
+ QPoint touchMousePressPos; // in screen coordinates
+
+ QQuickDeliveryAgent::Transform *sceneTransform = nullptr;
+
+ bool isDeliveringTouchAsMouse() const { return touchMouseId != -1 && touchMouseDevice; }
+ void cancelTouchMouseSynthesis();
+
+ bool checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos);
+ QPointingDevicePrivate::EventPointData *mousePointData();
+ QPointerEvent *eventInDelivery() const;
+
+ // Mouse positions are saved in widget coordinates
+ QPointF lastMousePosition;
+ bool deliverTouchAsMouse(QQuickItem *item, QTouchEvent *pointerEvent);
+ void translateTouchEvent(QTouchEvent *touchEvent);
+ void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true, bool cancel = false);
+ void onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, const QPointerEvent *event, const QEventPoint &point);
+ static QPointerEvent *clonePointerEvent(QPointerEvent *event, std::optional<QPointF> transformedLocalPos = std::nullopt);
+ void deliverToPassiveGrabbers(const QVector<QPointer<QObject> > &passiveGrabbers, QPointerEvent *pointerEvent);
+ bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
+ bool sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr);
+ bool sendFilteredPointerEventImpl(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
+ bool deliverSinglePointEventUntilAccepted(QPointerEvent *);
+
+ // entry point of events to the window
+ void handleTouchEvent(QTouchEvent *);
+ void handleMouseEvent(QMouseEvent *);
+ bool compressTouchEvent(QTouchEvent *);
+ void flushFrameSynchronousEvents(QQuickWindow *win);
+ void deliverDelayedTouchEvent();
+ void handleWindowDeactivate(QQuickWindow *win);
+
+ // utility functions that used to be in QQuickPointerEvent et al.
+ bool allUpdatedPointsAccepted(const QPointerEvent *ev);
+ static void localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest);
+ QList<QObject *> exclusiveGrabbers(QPointerEvent *ev);
+ static bool anyPointGrabbed(const QPointerEvent *ev);
+ static bool isMouseEvent(const QPointerEvent *ev);
+ static bool isTouchEvent(const QPointerEvent *ev);
+ static bool isTabletEvent(const QPointerEvent *ev);
+ static QQuickPointingDeviceExtra *deviceExtra(const QInputDevice *device);
+
+ // delivery of pointer events:
+ void touchToMouseEvent(QEvent::Type type, const QEventPoint &p, const QTouchEvent *touchEvent, QMutableSinglePointEvent *mouseEvent);
+ void ensureDeviceConnected(const QPointingDevice *dev);
+ void deliverPointerEvent(QPointerEvent *);
+ bool deliverTouchCancelEvent(QTouchEvent *);
+ bool deliverPressOrReleaseEvent(QPointerEvent *, bool handlersOnly = false);
+ void deliverUpdatedPoints(QPointerEvent *event);
+ void deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly = false);
+
+ QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointerEvent *event, const QEventPoint &point,
+ bool checkMouseButtons, bool checkAcceptsTouch) const;
+ QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
+
+ // hover delivery
+ bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted);
+ bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos,
+ Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted);
+ bool clearHover(ulong timestamp = 0);
+
+#if QT_CONFIG(quick_draganddrop)
+ void deliverDragEvent(QQuickDragGrabber *, QEvent *);
+ bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *, QVarLengthArray<QQuickItem*, 64> *currentGrabItems = nullptr);
+#endif
+
+ static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1);
+
+ static bool dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold = -1);
+
+ static bool dragOverThreshold(QVector2D delta);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickDeliveryAgentPrivate::FocusOptions)
+
+QT_END_NAMESPACE
+
+#endif // QQUICKDELIVERYAGENT_P_P_H
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index 6951dccf62..46da85630b 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -846,9 +846,15 @@ void QQuickWidgetPrivate::updateSize()
if (resizeMode == QQuickWidget::SizeViewToRootObject) {
QSize newSize = QSize(root->width(), root->height());
- if (newSize.isValid() && newSize != q->size()) {
- q->resize(newSize);
- q->updateGeometry();
+ if (newSize.isValid()) {
+ if (newSize != q->size()) {
+ q->resize(newSize);
+ q->updateGeometry();
+ } else if (offscreenWindow->size().isEmpty()) {
+ // QQuickDeliveryAgentPrivate::deliverHoverEvent() ignores events that
+ // occur outside of QQuickRootItem's geometry, so we need it to match root's size.
+ offscreenWindow->contentItem()->setSize(newSize);
+ }
}
} else if (resizeMode == QQuickWidget::SizeRootObjectToView) {
const bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
index 7860f5677a..3d6044ee9b 100644
--- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
@@ -1600,6 +1600,10 @@ void tst_qquickitem::hoverEvent()
QQuickWindow *window = new QQuickWindow();
window->resize(200, 200);
window->show();
+ QTest::qWaitForWindowExposed(window);
+#if QT_CONFIG(cursor) // Get the cursor out of the way.
+ QCursor::setPos(window->geometry().topRight() + QPoint(100, 100));
+#endif
HoverItem *item = new HoverItem;
item->setSize(QSizeF(100, 100));
@@ -1641,6 +1645,10 @@ void tst_qquickitem::hoverEventInParent()
QQuickWindow window;
window.resize(200, 200);
window.show();
+ QTest::qWaitForWindowExposed(&window);
+#if QT_CONFIG(cursor) // Get the cursor out of the way.
+ QCursor::setPos(window.geometry().topRight() + QPoint(100, 100));
+#endif
HoverItem *parentItem = new HoverItem(window.contentItem());
parentItem->setSize(QSizeF(200, 200));
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index 7fe8d94c74..05fda64654 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -1137,7 +1137,7 @@ void tst_qquickwindow::mergeTouchPointLists()
for (const auto &item : list2)
b.append(item.get());
- auto targetList = windowPrivate->mergePointerTargets(a, b);
+ auto targetList = windowPrivate->deliveryAgentPrivate()->mergePointerTargets(a, b);
QCOMPARE(targetList, expected);
}
@@ -1760,13 +1760,13 @@ void tst_qquickwindow::focusReason()
secondItem->setParentItem(window->contentItem());
firstItem->forceActiveFocus(Qt::OtherFocusReason);
- QCOMPARE(QQuickWindowPrivate::get(window)->lastFocusReason, Qt::OtherFocusReason);
+ QCOMPARE(QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->lastFocusReason, Qt::OtherFocusReason);
secondItem->forceActiveFocus(Qt::TabFocusReason);
- QCOMPARE(QQuickWindowPrivate::get(window)->lastFocusReason, Qt::TabFocusReason);
+ QCOMPARE(QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->lastFocusReason, Qt::TabFocusReason);
firstItem->forceActiveFocus(Qt::BacktabFocusReason);
- QCOMPARE(QQuickWindowPrivate::get(window)->lastFocusReason, Qt::BacktabFocusReason);
+ QCOMPARE(QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->lastFocusReason, Qt::BacktabFocusReason);
}
diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp
index 5f84147792..5d9e6ba8dc 100644
--- a/tests/auto/quick/shared/viewtestutil.cpp
+++ b/tests/auto/quick/shared/viewtestutil.cpp
@@ -35,8 +35,9 @@
#include <QtTest/QTest>
-#include <private/qquickwindow_p.h>
-#include <private/qquickitemview_p_p.h>
+#include <QtQuick/private/qquickdeliveryagent_p_p.h>
+#include <QtQuick/private/qquickitemview_p_p.h>
+#include <QtQuick/private/qquickwindow_p.h>
QT_BEGIN_NAMESPACE
@@ -460,10 +461,10 @@ namespace QQuickTouchUtils {
void flush(QQuickWindow *window) {
if (!window)
return;
- QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
- if (!wd || !wd->delayedTouch)
+ QQuickDeliveryAgentPrivate *da = QQuickWindowPrivate::get(window)->deliveryAgentPrivate();
+ if (!da || !da->delayedTouch)
return;
- wd->deliverDelayedTouchEvent();
+ da->deliverDelayedTouchEvent();
}
}
diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
index 33698b4141..ef0df0cddb 100644
--- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp
+++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
@@ -194,7 +194,7 @@ public:
switch (transition) {
case QPointingDevice::GrabTransition::GrabExclusive:
exclusiveGrabber = grabber;
- fromMouseEvent = event && QQuickWindowPrivate::isMouseEvent(event);
+ fromMouseEvent = event && QQuickDeliveryAgentPrivate::isMouseEvent(event);
canceled = false;
break;
case QPointingDevice::GrabTransition::UngrabExclusive:
@@ -641,7 +641,7 @@ void tst_TouchMouse::buttonOnFlickable()
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress);
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(&window);
- QVERIFY(windowPriv->touchMouseId != -1);
+ QVERIFY(windowPriv->deliveryAgentPrivate()->touchMouseId != -1);
auto devPriv = QPointingDevicePrivate::get(device);
QCOMPARE(devPriv->pointById(0)->exclusiveGrabber, eventItem1);
QCOMPARE(grabMonitor.exclusiveGrabber, eventItem1);
@@ -662,7 +662,7 @@ void tst_TouchMouse::buttonOnFlickable()
QCOMPARE(eventItem1->eventList.at(3).type, QEvent::UngrabMouse);
QCOMPARE(grabMonitor.exclusiveGrabber, flickable);
- QVERIFY(windowPriv->touchMouseId != -1);
+ QVERIFY(windowPriv->deliveryAgentPrivate()->touchMouseId != -1);
QCOMPARE(devPriv->pointById(0)->exclusiveGrabber, flickable);
QTest::touchEvent(&window, device).release(0, p1, &window);
@@ -696,7 +696,7 @@ void tst_TouchMouse::touchButtonOnFlickable()
QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin);
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(&window);
- QVERIFY(windowPriv->touchMouseId == -1);
+ QVERIFY(windowPriv->deliveryAgentPrivate()->touchMouseId == -1);
auto devPriv = QPointingDevicePrivate::get(device);
QCOMPARE(devPriv->pointById(0)->exclusiveGrabber, eventItem2);
QCOMPARE(grabMonitor.exclusiveGrabber, eventItem2);
@@ -720,7 +720,7 @@ void tst_TouchMouse::touchButtonOnFlickable()
QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate);
QCOMPARE(grabMonitor.exclusiveGrabber, flickable);
// both EventItem and Flickable handled the actual touch, so synth-mouse doesn't happen
- QCOMPARE(windowPriv->touchMouseId, -1);
+ QCOMPARE(windowPriv->deliveryAgentPrivate()->touchMouseId, -1);
QCOMPARE(devPriv->pointById(0)->exclusiveGrabber, flickable);
QVERIFY(flickable->isMovingVertically());
@@ -783,7 +783,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable()
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(&window);
- QCOMPARE(windowPriv->touchMouseId, -1); // no grabber
+ QCOMPARE(windowPriv->deliveryAgentPrivate()->touchMouseId, -1); // no grabber
// touch press
QPoint p1 = QPoint(10, 110);
@@ -1535,7 +1535,7 @@ void tst_TouchMouse::implicitUngrab()
QVERIFY(!eventItem->eventList.isEmpty());
QCOMPARE(eventItem->eventList.at(0).type, QEvent::UngrabMouse);
QTest::touchEvent(&window, device).release(0, p1); // clean up potential state
- QCOMPARE(windowPriv->touchMouseId, -1);
+ QCOMPARE(windowPriv->deliveryAgentPrivate()->touchMouseId, -1);
eventItem->setEnabled(true);
QTest::touchEvent(&window, device).press(0, p1);