aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquickwindow.cpp144
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp143
-rw-r--r--src/quick/util/qquickdeliveryagent_p.h2
-rw-r--r--src/quick/util/qquickdeliveryagent_p_p.h6
4 files changed, 163 insertions, 132 deletions
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index e0c87b4435..6d602a3810 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -90,6 +90,7 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcMouse)
Q_DECLARE_LOGGING_CATEGORY(lcTouch)
+Q_DECLARE_LOGGING_CATEGORY(lcPtr)
Q_LOGGING_CATEGORY(lcDirty, "qt.quick.dirty")
Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
@@ -1354,19 +1355,15 @@ bool QQuickWindow::event(QEvent *e)
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) {
+ if (pe->pointCount()) {
+ if (QQuickDeliveryAgentPrivate::subsceneAgentsExist) {
+ bool ret = false;
+ // 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;
+
+ auto insert = [&](QQuickDeliveryAgent *ptda, const QEventPoint &pt) {
if (pt.state() == QEventPoint::Pressed)
pe->clearPassiveGrabbers(pt);
auto danpit = deliveryAgentsNeedingPoints.find(ptda);
@@ -1375,47 +1372,98 @@ bool QQuickWindow::event(QEvent *e)
} else {
danpit.value().append(pt);
}
+ };
+
+ for (const auto &pt : pe->points()) {
+ eventStates |= pt.state();
+ auto epd = QPointingDevicePrivate::get(const_cast<QPointingDevice*>(pe->pointingDevice()))->queryPointById(pt.id());
+ Q_ASSERT(epd);
+ bool foundAgent = false;
+ if (!epd->exclusiveGrabber.isNull() && !epd->exclusiveGrabberContext.isNull()) {
+ if (auto ptda = qobject_cast<QQuickDeliveryAgent *>(epd->exclusiveGrabberContext.data())) {
+ insert(ptda, pt);
+ qCDebug(lcPtr) << pe->type() << "point" << pt.id() << pt.state()
+ << "@" << pt.scenePosition() << "will be re-delivered via known grabbing agent" << ptda << "to" << epd->exclusiveGrabber.data();
+ foundAgent = true;
+ }
+ }
+ for (auto pgda : epd->passiveGrabbersContext) {
+ if (auto ptda = qobject_cast<QQuickDeliveryAgent *>(pgda.data())) {
+ insert(ptda, pt);
+ qCDebug(lcPtr) << pe->type() << "point" << pt.id() << pt.state()
+ << "@" << pt.scenePosition() << "will be re-delivered via known passive-grabbing agent" << ptda;
+ foundAgent = true;
+ }
+ }
+ // fallback: if we didn't find remembered/known grabber agent(s), expect the root DA to handle it
+ if (!foundAgent)
+ insert(da, 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;
+ for (auto daAndPoints : deliveryAgentsNeedingPoints) {
+ if (pe->pointCount() > 1) {
+ Q_ASSERT(QQuickDeliveryAgentPrivate::isTouchEvent(pe));
+ // 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;
+ }
+ // Make a new touch event for the subscene, the same way QQuickItemPrivate::localizedTouchEvent() does it
+ QMutableTouchEvent te(eventType, pe->pointingDevice(), pe->modifiers(), daAndPoints.second);
+ te.setTimestamp(pe->timestamp());
+ te.accept();
+ qCDebug(lcTouch) << daAndPoints.first << "shall now receive" << &te;
+ ret = daAndPoints.first->event(&te) || ret;
+ } else {
+ qCDebug(lcPtr) << daAndPoints.first << "shall now receive" << pe;
+ ret = daAndPoints.first->event(pe) || ret;
+ }
+ if (pe->isAccepted())
+ break;
+ }
+
+ if (ret)
+ return true;
+ } else {
+ for (const auto &pt : pe->points()) {
+ if (pt.state() == QEventPoint::Pressed)
+ pe->clearPassiveGrabbers(pt);
}
- 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
- const auto &pt = pe->points().first();
- if (pt.state() == QEventPoint::Pressed)
- pe->clearPassiveGrabbers(pt);
- // it would be nice to just use "else" here, but
- // isBeginEvent() is not quite the same check as pt.state() != Pressed
- if (!pe->isBeginEvent()) {
- 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.
+
+ // If it has no points, it's probably a TouchCancel, and DeliveryAgent needs to handle it.
+ // If we didn't handle it in the block above, handle it now.
// 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))
+ bool ret = (da && da->event(e));
+
+ // failsafe: never allow any kind of grab to persist after release
+ if (pe->isEndEvent()) {
+ if (pe->isSinglePointEvent()) {
+ if (static_cast<QSinglePointEvent *>(pe)->buttons() == Qt::NoButton) {
+ auto &firstPt = pe->point(0);
+ pe->setExclusiveGrabber(firstPt, nullptr);
+ pe->clearPassiveGrabbers(firstPt);
+ }
+ } else {
+ for (auto &point : pe->points()) {
+ if (point.state() == QEventPoint::State::Released) {
+ pe->setExclusiveGrabber(point, nullptr);
+ pe->clearPassiveGrabbers(point);
+ }
+ }
+ }
+ }
+
+ if (ret)
return true;
} else if (e->isInputEvent()) {
if (da && da->event(e))
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index 673bc1a009..a38ac410d4 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -70,6 +70,9 @@ 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);
+bool QQuickDeliveryAgentPrivate::subsceneAgentsExist(false);
+QQuickDeliveryAgent *QQuickDeliveryAgentPrivate::currentEventDeliveryAgent(nullptr);
+
void QQuickDeliveryAgentPrivate::touchToMouseEvent(QEvent::Type type, const QEventPoint &p, const QTouchEvent *touchEvent, QMutableSinglePointEvent *mouseEvent)
{
Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
@@ -267,6 +270,7 @@ bool QQuickDeliveryAgentPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEve
*/
void QQuickDeliveryAgentPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch, bool cancel)
{
+ Q_Q(QQuickDeliveryAgent);
if (eventsInDelivery.isEmpty()) {
// do it the expensive way
for (auto dev : knownPointingDevices) {
@@ -278,7 +282,7 @@ void QQuickDeliveryAgentPrivate::removeGrabber(QQuickItem *grabber, bool mouse,
auto eventInDelivery = eventsInDelivery.top();
if (Q_LIKELY(mouse) && eventInDelivery) {
auto epd = mousePointData();
- if (epd && epd->exclusiveGrabber == grabber) {
+ if (epd && epd->exclusiveGrabber == grabber && epd->exclusiveGrabberContext.data() == q) {
QQuickItem *oldGrabber = qobject_cast<QQuickItem *>(epd->exclusiveGrabber);
qCDebug(lcMouseTarget) << "removeGrabber" << oldGrabber << "-> null";
eventInDelivery->setExclusiveGrabber(epd->eventPoint, nullptr);
@@ -639,21 +643,6 @@ 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);
@@ -678,6 +667,8 @@ void QQuickDeliveryAgent::setSceneTransform(QQuickDeliveryAgent::Transform *tran
bool QQuickDeliveryAgent::event(QEvent *ev)
{
Q_D(QQuickDeliveryAgent);
+ d->currentEventDeliveryAgent = this;
+ auto cleanup = qScopeGuard([d] { d->currentEventDeliveryAgent = nullptr; });
switch (ev->type()) {
case QEvent::MouseButtonPress:
@@ -843,23 +834,12 @@ QQuickDeliveryAgentPrivate::QQuickDeliveryAgentPrivate(QQuickItem *root) :
#if QT_CONFIG(quick_draganddrop)
dragGrabber = new QQuickDragGrabber;
#endif
+ if (isSubsceneAgent)
+ subsceneAgentsExist = true;
}
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;
@@ -1094,6 +1074,7 @@ void QQuickDeliveryAgentPrivate::deliverDelayedTouchEvent()
*/
void QQuickDeliveryAgentPrivate::handleWindowDeactivate(QQuickWindow *win)
{
+ Q_Q(QQuickDeliveryAgent);
qCDebug(lcFocus) << "deactivated" << win->title();
const auto inputDevices = QInputDevice::devices();
for (auto device : inputDevices) {
@@ -1105,7 +1086,7 @@ void QQuickDeliveryAgentPrivate::handleWindowDeactivate(QQuickWindow *win)
if (QQuickItem *item = qmlobject_cast<QQuickItem *>(epd.exclusiveGrabber.data()))
relevant = (item->window() == win);
else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(epd.exclusiveGrabber.data()))
- relevant = (handler->parentItem()->window() == win);
+ relevant = (handler->parentItem()->window() == win && epd.exclusiveGrabberContext.data() == q);
if (relevant)
devPriv->setExclusiveGrabber(nullptr, epd.eventPoint, nullptr);
}
@@ -1421,19 +1402,21 @@ void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice
const bool grabGained = (transition == QPointingDevice::GrabTransition::GrabExclusive ||
transition == QPointingDevice::GrabTransition::GrabPassive);
- QQuickDeliveryAgent *subsceneAgent = nullptr;
+ QQuickDeliveryAgent *deliveryAgent = 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());
+ auto itemPriv = QQuickItemPrivate::get(handler->parentItem());
+ deliveryAgent = itemPriv->deliveryAgent();
+ if (deliveryAgent == q) {
+ handler->onGrabChanged(handler, transition, const_cast<QPointerEvent *>(event),
+ const_cast<QEventPoint &>(point));
+ }
+ if (grabGained) {
// 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 (grabGained && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
+ if (isSubsceneAgent && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
itemPriv->maybeHasSubsceneDeliveryAgent = true;
- subsceneAgent = itemPriv->deliveryAgent();
}
} else {
switch (transition) {
@@ -1471,32 +1454,39 @@ void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice
break;
}
auto grabberItem = static_cast<QQuickItem *>(grabber); // cannot be a handler: we checked above
- if (isSubsceneAgent && grabberItem) {
+ if (grabberItem) {
auto itemPriv = QQuickItemPrivate::get(grabberItem);
+ deliveryAgent = itemPriv->deliveryAgent();
// 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 (grabGained && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
+ if (isSubsceneAgent && grabGained && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
itemPriv->maybeHasSubsceneDeliveryAgent = true;
- 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);
+ if (currentEventDeliveryAgent == q && event && event->device()) {
+ auto epd = QPointingDevicePrivate::get(const_cast<QPointingDevice*>(event->pointingDevice()))->queryPointById(point.id());
+ Q_ASSERT(epd);
+ switch (transition) {
+ case QPointingDevice::GrabPassive: {
+ QPointingDevicePrivate::setPassiveGrabberContext(epd, grabber, q);
+ qCDebug(lcPtr) << "remembering that" << q << "handles point" << point.id() << "after" << transition;
+ } break;
+ case QPointingDevice::GrabExclusive:
+ epd->exclusiveGrabberContext = q;
+ qCDebug(lcPtr) << "remembering that" << q << "handles point" << point.id() << "after" << transition;
+ break;
+ case QPointingDevice::CancelGrabExclusive:
+ case QPointingDevice::UngrabExclusive:
+ // taken care of in QPointingDevicePrivate::setExclusiveGrabber(,,nullptr), removeExclusiveGrabber()
+ break;
+ case QPointingDevice::UngrabPassive:
+ case QPointingDevice::CancelGrabPassive:
+ // taken care of in QPointingDevicePrivate::removePassiveGrabber(), clearPassiveGrabbers()
+ break;
+ case QPointingDevice::OverrideGrabPassive:
+ // not in use at this time
+ break;
}
}
}
@@ -1557,27 +1547,9 @@ void QQuickDeliveryAgentPrivate::deliverPointerEvent(QPointerEvent *event)
if (event->isEndEvent())
deliverPressOrReleaseEvent(event, true);
- // 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
- cancelTouchMouseSynthesis();
- } else if (static_cast<QSinglePointEvent *>(event)->buttons() == Qt::NoButton) {
- auto &firstPt = event->point(0);
- event->setExclusiveGrabber(firstPt, nullptr);
- event->clearPassiveGrabbers(firstPt);
- agentMap.remove(firstPt.id());
- }
- }
+ // failsafe: never allow touch->mouse synthesis to persist after release
+ if (event->isEndEvent() && isTouchEvent(event))
+ cancelTouchMouseSynthesis();
eventsInDelivery.pop();
if (sceneTransform) {
@@ -1668,6 +1640,7 @@ QVector<QQuickItem *> QQuickDeliveryAgentPrivate::mergePointerTargets(const QVec
*/
void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event)
{
+ Q_Q(const QQuickDeliveryAgent);
bool done = false;
const auto grabbers = exclusiveGrabbers(event);
hasFiltered.clear();
@@ -1693,8 +1666,20 @@ void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event)
}
// Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once)
- for (auto &point : event->points())
- deliverToPassiveGrabbers(event->passiveGrabbers(point), event);
+ for (auto &point : event->points()) {
+ auto epd = QPointingDevicePrivate::get(event->pointingDevice())->queryPointById(point.id());
+ if (Q_UNLIKELY(!epd)) {
+ qWarning() << "point is not in activePoints" << point;
+ continue;
+ }
+ QList<QPointer<QObject>> relevantPassiveGrabbers;
+ for (int i = 0; i < epd->passiveGrabbersContext.count(); ++i) {
+ if (epd->passiveGrabbersContext.at(i).data() == q)
+ relevantPassiveGrabbers << epd->passiveGrabbers.at(i);
+ }
+ if (!relevantPassiveGrabbers.isEmpty())
+ deliverToPassiveGrabbers(relevantPassiveGrabbers, event);
+ }
if (done)
return;
diff --git a/src/quick/util/qquickdeliveryagent_p.h b/src/quick/util/qquickdeliveryagent_p.h
index 41b3a5390b..9b73d9b9b9 100644
--- a/src/quick/util/qquickdeliveryagent_p.h
+++ b/src/quick/util/qquickdeliveryagent_p.h
@@ -79,8 +79,6 @@ public:
explicit QQuickDeliveryAgent(QQuickItem *rootItem);
virtual ~QQuickDeliveryAgent();
- static QQuickDeliveryAgent *grabberAgent(QPointerEvent *pe, const QEventPoint &pt);
-
QQuickItem *rootItem() const;
void setSceneTransform(Transform *transform);
diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h
index faf7f6318f..ad1eece36e 100644
--- a/src/quick/util/qquickdeliveryagent_p_p.h
+++ b/src/quick/util/qquickdeliveryagent_p_p.h
@@ -72,9 +72,6 @@ class QQuickWindow;
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
@@ -121,6 +118,9 @@ public:
bool frameSynchronousHoverEnabled = true;
bool isSubsceneAgent = false;
+ static bool subsceneAgentsExist;
+ // QQuickDeliveryAgent::event() sets this to the one that's currently (trying to) handle the event
+ static QQuickDeliveryAgent *currentEventDeliveryAgent;
Qt::FocusReason lastFocusReason = Qt::OtherFocusReason;
int pointerEventRecursionGuard = 0;