diff options
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 144 | ||||
-rw-r--r-- | src/quick/util/qquickdeliveryagent.cpp | 143 | ||||
-rw-r--r-- | src/quick/util/qquickdeliveryagent_p.h | 2 | ||||
-rw-r--r-- | src/quick/util/qquickdeliveryagent_p_p.h | 6 |
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; |