diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index 36e51eb423..809adfd043 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -99,8 +99,10 @@ QQuickDragHandler::QQuickDragHandler(QQuickItem *parent) QPointF QQuickDragHandler::targetCentroidPosition() { QPointF pos = centroid().position(); - if (target() != parentItem()) - pos = parentItem()->mapToItem(target(), pos); + if (auto par = parentItem()) { + if (target() != par) + pos = par->mapToItem(target(), pos); + } return pos; } @@ -111,7 +113,7 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDe // In case the grab got handed over from another grabber, we might not get the Press. auto isDescendant = [](QQuickItem *parent, QQuickItem *target) { - return (target != parent) && !target->isAncestorOf(parent); + return parent && (target != parent) && !target->isAncestorOf(parent); }; if (m_snapMode == SnapAlways || (m_snapMode == SnapIfPressedOutsideTarget && !m_pressedInsideTarget) diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp index b934940126..020afc647a 100644 --- a/src/quick/handlers/qquickhoverhandler.cpp +++ b/src/quick/handlers/qquickhoverhandler.cpp @@ -101,8 +101,10 @@ bool QQuickHoverHandler::event(QEvent *event) void QQuickHoverHandler::componentComplete() { - parentItem()->setAcceptHoverEvents(true); - QQuickItemPrivate::get(parentItem())->setHasHoverInChild(true); + if (auto par = parentItem()) { + par->setAcceptHoverEvents(true); + QQuickItemPrivate::get(par)->setHasHoverInChild(true); + } } bool QQuickHoverHandler::wantsPointerEvent(QPointerEvent *event) diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp index 85040a1a12..8c13c84914 100644 --- a/src/quick/handlers/qquickmultipointhandler.cpp +++ b/src/quick/handlers/qquickmultipointhandler.cpp @@ -104,7 +104,8 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QPointerEvent *event) d->currentPoints.resize(c); for (int i = 0; i < c; ++i) { d->currentPoints[i].reset(event, candidatePoints[i]); - d->currentPoints[i].localize(parentItem()); + if (auto par = parentItem()) + d->currentPoints[i].localize(par); } } else { d->currentPoints.clear(); diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index c5524fc107..495eb8b7d3 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -360,7 +360,9 @@ bool QQuickPointerHandler::approveGrabTransition(QPointerEvent *event, const QEv } else if ((d->grabPermissions & CanTakeOverFromItems)) { allowed = true; QQuickItem * existingItemGrabber = qobject_cast(event->exclusiveGrabber(point)); - auto da = QQuickItemPrivate::get(parentItem())->deliveryAgentPrivate(); + auto da = parentItem() ? QQuickItemPrivate::get(parentItem())->deliveryAgentPrivate() + : QQuickDeliveryAgentPrivate::currentEventDeliveryAgent ? static_cast( + QQuickDeliveryAgentPrivate::get(QQuickDeliveryAgentPrivate::currentEventDeliveryAgent)) : nullptr; if (existingItemGrabber && ((existingItemGrabber->keepMouseGrab() && (QQuickDeliveryAgentPrivate::isMouseEvent(event) || da->isDeliveringTouchAsMouse())) || @@ -546,6 +548,10 @@ bool QQuickPointerHandler::parentContains(const QPointF &scenePosition) const if (m > 0) return p.x() >= -m && p.y() >= -m && p.x() <= par->width() + m && p.y() <= par->height() + m; return par->contains(p); + } else if (parent() && parent()->inherits("QQuick3DModel")) { + // If the parent is from Qt Quick 3D, assume that + // bounds checking was already done, as part of picking. + return true; } return false; } @@ -604,7 +610,7 @@ void QQuickPointerHandler::setTarget(QQuickItem *target) QQuickItem *QQuickPointerHandler::parentItem() const { - return static_cast(QObject::parent()); + return qmlobject_cast(QObject::parent()); } QQuickItem *QQuickPointerHandler::target() const @@ -641,7 +647,7 @@ void QQuickPointerHandler::handlePointerEvent(QPointerEvent *event) { bool wants = wantsPointerEvent(event); qCDebug(lcPointerHandlerDispatch) << metaObject()->className() << objectName() - << "on" << parentItem()->metaObject()->className() << parentItem()->objectName() + << "on" << parent()->metaObject()->className() << parent()->objectName() << (wants ? "WANTS" : "DECLINES") << event; if (wants) { handlePointerEventImpl(event); @@ -715,6 +721,9 @@ void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *event) the Item's interior. Initially \l [QML] {target} {target()} is the same, but it can be reassigned. + \note When a handler is declared in a \l QtQuick3D.Model object, the parent + is not an Item, therefore this property is \c null. + \sa {target}, QObject::parent() */ diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 707a750f3b..d16c9e23cb 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -342,10 +342,13 @@ void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDev void QQuickTapHandler::connectPreRenderSignal(bool conn) { + auto par = parentItem(); + if (!par) + return; if (conn) - connect(parentItem()->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld); + connect(par->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld); else - disconnect(parentItem()->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld); + disconnect(par->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld); } void QQuickTapHandler::updateTimeHeld() diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index 4fe3e9db6e..2b649d01cf 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -916,12 +916,13 @@ void QQuickDeliveryAgentPrivate::deliverToPassiveGrabbers(const QVectorsecond; - } else { + } else if (par) { alreadyFiltered = sendFilteredPointerEvent(pointerEvent, par); sendFilteredPointerEventResult << qMakePair(par, alreadyFiltered); } if (!alreadyFiltered) { - localizePointerEvent(pointerEvent, handler->parentItem()); + if (par) + localizePointerEvent(pointerEvent, par); handler->handlePointerEvent(pointerEvent); } } @@ -1113,8 +1114,14 @@ void QQuickDeliveryAgentPrivate::handleWindowDeactivate(QQuickWindow *win) bool relevant = false; if (QQuickItem *item = qmlobject_cast(epd.exclusiveGrabber.data())) relevant = (item->window() == win); - else if (QQuickPointerHandler *handler = qmlobject_cast(epd.exclusiveGrabber.data())) - relevant = (handler->parentItem()->window() == win && epd.exclusiveGrabberContext.data() == q); + else if (QQuickPointerHandler *handler = qmlobject_cast(epd.exclusiveGrabber.data())) { + if (handler->parentItem()) + relevant = (handler->parentItem()->window() == win && epd.exclusiveGrabberContext.data() == q); + else + // a handler with no Item parent probably has a 3D Model parent. + // TODO actually check the window somehow + relevant = true; + } if (relevant) devPriv->setExclusiveGrabber(nullptr, epd.eventPoint, nullptr); } @@ -1391,6 +1398,8 @@ void QQuickDeliveryAgentPrivate::handleMouseEvent(QMouseEvent *event) void QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents(QQuickWindow *win) { Q_Q(QQuickDeliveryAgent); + QQuickDeliveryAgent *deliveringAgent = QQuickDeliveryAgentPrivate::currentEventDeliveryAgent; + QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = q; if (delayedTouch) { deliverDelayedTouchEvent(); @@ -1421,6 +1430,9 @@ void QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents(QQuickWindow *win) qCDebug(lcHoverTrace) << q << "frame-sync hover delivery done"; } #endif + if (Q_UNLIKELY(QQuickDeliveryAgentPrivate::currentEventDeliveryAgent != q)) + qCWarning(lcPtr, "detected interleaved frame-sync and actual events"); + QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = deliveringAgent; } void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, @@ -1434,18 +1446,23 @@ void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice // note: event can be null, if the signal was emitted from QPointingDevicePrivate::removeGrabber(grabber) if (auto *handler = qmlobject_cast(grabber)) { - auto itemPriv = QQuickItemPrivate::get(handler->parentItem()); - deliveryAgent = itemPriv->deliveryAgent(); - if (deliveryAgent == q) { + if (handler->parentItem()) { + auto itemPriv = QQuickItemPrivate::get(handler->parentItem()); + deliveryAgent = itemPriv->deliveryAgent(); + if (deliveryAgent == q) { + handler->onGrabChanged(handler, transition, const_cast(event), + const_cast(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 (isSubsceneAgent && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent)) + itemPriv->maybeHasSubsceneDeliveryAgent = true; + } + } else if (!isSubsceneAgent) { handler->onGrabChanged(handler, transition, const_cast(event), const_cast(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 (isSubsceneAgent && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent)) - itemPriv->maybeHasSubsceneDeliveryAgent = true; - } } else { switch (transition) { case QPointingDevice::CancelGrabExclusive: @@ -1681,10 +1698,14 @@ void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event) // The grabber is not an item? It's a handler then. Let it have the event first. QQuickPointerHandler *handler = static_cast(grabber); receiver = static_cast(grabber)->parentItem(); - hasFiltered.clear(); - if (sendFilteredPointerEvent(event, receiver)) - done = true; - localizePointerEvent(event, receiver); + // Filtering via QQuickItem::childMouseEventFilter() is only possible + // if the handler's parent is an Item. It could be a QQ3D object. + if (receiver) { + hasFiltered.clear(); + if (sendFilteredPointerEvent(event, receiver)) + done = true; + localizePointerEvent(event, receiver); + } handler->handlePointerEvent(event); } if (done) @@ -1692,7 +1713,8 @@ void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event) // If the grabber is an item or the grabbing handler didn't handle it, // then deliver the event to the item (which may have multiple handlers). hasFiltered.clear(); - deliverMatchingPointsToItem(receiver, true, event); + if (receiver) + deliverMatchingPointsToItem(receiver, true, event); } // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once)