diff options
Diffstat (limited to 'src/quick/handlers/qquickpointerhandler.cpp')
-rw-r--r-- | src/quick/handlers/qquickpointerhandler.cpp | 130 |
1 files changed, 121 insertions, 9 deletions
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index faebdf3621..64bf1a8a8b 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch") +Q_LOGGING_CATEGORY(lcPointerHandlerGrab, "qt.quick.handler.grab") Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active") /*! @@ -67,6 +68,7 @@ QQuickPointerHandler::QQuickPointerHandler(QObject *parent) , m_targetExplicitlySet(false) , m_hadKeepMouseGrab(false) , m_hadKeepTouchGrab(false) + , m_grabPermissions(CanTakeOverFromItems | CanTakeOverFromHandlersOfDifferentType | ApprovesTakeOverByAnything) { } @@ -94,7 +96,7 @@ QQuickPointerHandler::~QQuickPointerHandler() */ void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) { - qCDebug(lcPointerHandlerDispatch) << point << stateChange << grabber; + qCDebug(lcPointerHandlerGrab) << point << stateChange << grabber; Q_ASSERT(point); if (grabber == this) { bool wasCanceled = false; @@ -145,7 +147,7 @@ void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEv */ void QQuickPointerHandler::setPassiveGrab(QQuickEventPoint *point, bool grab) { - qCDebug(lcPointerHandlerDispatch) << point << grab; + qCDebug(lcPointerHandlerGrab) << point << grab; if (grab) { point->setGrabberPointerHandler(this, false); } else { @@ -153,14 +155,124 @@ void QQuickPointerHandler::setPassiveGrab(QQuickEventPoint *point, bool grab) } } -void QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab) +/*! + Check whether it's OK to take an exclusive grab of the \a point. + + The default implementation will call approveGrabTransition() to check this + handler's \l grabPermissions. If grabbing can be done only by taking over + the exclusive grab from an Item, approveGrabTransition() checks the Item's + \l keepMouseGrab or \l keepTouchGrab flags appropriately. If grabbing can + be done only by taking over another handler's exclusive grab, canGrab() + also calls approveGrabTransition() on the handler which is about to lose + its grab. Either one can deny the takeover. +*/ +bool QQuickPointerHandler::canGrab(QQuickEventPoint *point) { - // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab - qCDebug(lcPointerHandlerDispatch) << point << grab; - // Don't allow one handler to cancel another's grab, unless it is stealing it for itself - if (!grab && point->grabberPointerHandler() != this) + QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler(); + return approveGrabTransition(point, this) && + (existingPhGrabber ? existingPhGrabber->approveGrabTransition(point, this) : true); +} + +/*! + Check this handler's rules to see if \l proposedGrabber will be allowed to take + the exclusive grab. This function may be called twice: once on the instance which + will take the grab, and once on the instance which would thereby lose its grab, + in case of a takeover scenario. +*/ +bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObject *proposedGrabber) +{ + bool allowed = false; + if (proposedGrabber == this) { + QObject* existingGrabber = point->exclusiveGrabber(); + allowed = (existingGrabber == nullptr) || ((m_grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything); + if (existingGrabber) { + if (QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler()) { + if (!allowed && (m_grabPermissions & CanTakeOverFromHandlersOfDifferentType) && + existingPhGrabber->metaObject()->className() != metaObject()->className()) + allowed = true; + if (!allowed && (m_grabPermissions & CanTakeOverFromHandlersOfSameType) && + existingPhGrabber->metaObject()->className() == metaObject()->className()) + allowed = true; + } else if ((m_grabPermissions & CanTakeOverFromItems)) { + QQuickItem * existingItemGrabber = point->grabberItem(); + if (existingItemGrabber && !((existingItemGrabber->keepMouseGrab() && point->pointerEvent()->asPointerMouseEvent()) || + (existingItemGrabber->keepTouchGrab() && point->pointerEvent()->asPointerTouchEvent()))) + allowed = true; + } + } + } else { + // proposedGrabber is different: that means this instance will lose its grab + if (proposedGrabber) { + if ((m_grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything) + allowed = true; + if (!allowed && (m_grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) && + proposedGrabber->metaObject()->className() != metaObject()->className()) + allowed = true; + if (!allowed && (m_grabPermissions & ApprovesTakeOverByHandlersOfSameType) && + proposedGrabber->metaObject()->className() == metaObject()->className()) + allowed = true; + if (!allowed && (m_grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits("QQuickItem")) + allowed = true; + } else { + if (!allowed && (m_grabPermissions & ApprovesCancellation)) + allowed = true; + } + } + qCDebug(lcPointerHandlerGrab) << "point" << hex << point->pointId() << "permission" << + QMetaEnum::fromType<GrabPermissions>().valueToKeys(grabPermissions()) << + ':' << this << (allowed ? "approved to" : "denied to") << proposedGrabber; + return allowed; +} + +/*! + \qmlproperty bool QtQuick::PointerHandler::grabPermission + + This property specifies the permissions when this handler's logic decides + to take over the exclusive grab, or when it is asked to approve grab + takeover or cancellation by another handler. + + The default is + \c {CanTakeOverFromItems | CanTakeOverFromHandlersOfDifferentType | ApprovesTakeOverByAnything} + which allows most takeover scenarios but avoids e.g. two PinchHandlers fighting + over the same touchpoints. +*/ +void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission) +{ + if (m_grabPermissions == grabPermission) return; - point->setGrabberPointerHandler(grab ? this : nullptr, true); + + m_grabPermissions = grabPermission; + emit grabPermissionChanged(); +} + +/*! + \internal + Acquire or give up the exclusive grab of the given \a point, according to + the \a grab state, and subject to the rules: canGrab(), and the rule not to + relinquish another handler's grab. Returns true if permission is granted, + or if the exclusive grab has already been acquired or relinquished as + specified. Returns false if permission is denied either by this handler or + by the handler or item from which this handler would take over +*/ +bool QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab) +{ + if ((grab && point->exclusiveGrabber() == this) || (!grab && point->exclusiveGrabber() != this)) + return true; + // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab + bool allowed = true; + if (grab) { + allowed = canGrab(point); + } else { + QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler(); + // Ask before allowing one handler to cancel another's grab + if (existingPhGrabber && existingPhGrabber != this && !existingPhGrabber->approveGrabTransition(point, nullptr)) + allowed = false; + } + qCDebug(lcPointerHandlerGrab) << point << (grab ? "grab" : "ungrab") << (allowed ? "allowed" : "forbidden") << + point->exclusiveGrabber() << "->" << (grab ? this : nullptr); + if (allowed) + point->setGrabberPointerHandler(grab ? this : nullptr, true); + return allowed; } /*! @@ -169,7 +281,7 @@ void QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab) */ void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point) { - qCDebug(lcPointerHandlerDispatch) << point; + qCDebug(lcPointerHandlerGrab) << point; point->cancelAllGrabs(this); } |