| 1 | // Copyright (C) 2018 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "qquickpointerhandler_p.h" |
| 5 | #include "qquickpointerhandler_p_p.h" |
| 6 | #include <QtQuick/private/qquickitem_p.h> |
| 7 | #include <QtQuick/private/qquickhandlerpoint_p.h> |
| 8 | #include <QtQuick/private/qquickdeliveryagent_p_p.h> |
| 9 | #include <QtGui/private/qinputdevice_p.h> |
| 10 | |
| 11 | #include <QtCore/qpointer.h> |
| 12 | |
| 13 | QT_BEGIN_NAMESPACE |
| 14 | |
| 15 | Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch" ) |
| 16 | Q_LOGGING_CATEGORY(lcPointerHandlerGrab, "qt.quick.handler.grab" ) |
| 17 | Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active" ) |
| 18 | Q_DECLARE_LOGGING_CATEGORY(lcHandlerParent) |
| 19 | |
| 20 | /*! |
| 21 | \qmltype PointerHandler |
| 22 | \qmlabstract |
| 23 | \since 5.10 |
| 24 | \nativetype QQuickPointerHandler |
| 25 | \inqmlmodule QtQuick |
| 26 | \brief Abstract handler for pointer events. |
| 27 | |
| 28 | PointerHandler is the base class Input Handler (not registered as a QML type) for |
| 29 | events from any kind of pointing device (touch, mouse or graphics tablet). |
| 30 | */ |
| 31 | |
| 32 | /*! \internal |
| 33 | So far we only offer public QML API for Pointer Handlers, but we expect |
| 34 | in some future version of Qt to have public C++ API as well. This will open |
| 35 | up the possibility to instantiate handlers in custom items (which we should |
| 36 | begin doing in Qt Quick Controls in the near future), and to subclass to make |
| 37 | custom handlers (as TableView is already doing). |
| 38 | |
| 39 | To make a custom Pointer Handler, first try to choose the parent class |
| 40 | according to your needs. If the gesture that you want to recognize could |
| 41 | involve multiple touchpoints (even if it could start with only one point), |
| 42 | subclass QQuickMultiPointHandler. If you are sure that you never want to |
| 43 | handle more than one QEventPoint, subclass QQuickSinglePointHandler. |
| 44 | */ |
| 45 | QQuickPointerHandler::QQuickPointerHandler(QQuickItem *parent) |
| 46 | : QQuickPointerHandler(*(new QQuickPointerHandlerPrivate), parent) |
| 47 | { |
| 48 | } |
| 49 | |
| 50 | QQuickPointerHandler::QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QQuickItem *parent) |
| 51 | : QObject(dd, parent) |
| 52 | { |
| 53 | // When a handler is created in QML, the given parent is null, and we |
| 54 | // depend on QQuickItemPrivate::data_append() later when it's added to an |
| 55 | // item's DefaultProperty data property. But when a handler is created in |
| 56 | // C++ with a parent item, data_append() won't be called, and the caller |
| 57 | // shouldn't have to worry about it either. |
| 58 | if (parent) |
| 59 | QQuickItemPrivate::get(item: parent)->addPointerHandler(h: this); |
| 60 | } |
| 61 | |
| 62 | QQuickPointerHandler::~QQuickPointerHandler() |
| 63 | { |
| 64 | QQuickItem *parItem = parentItem(); |
| 65 | if (parItem) { |
| 66 | QQuickItemPrivate *p = QQuickItemPrivate::get(item: parItem); |
| 67 | p->extra.value().pointerHandlers.removeOne(t: this); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /*! |
| 72 | \qmlproperty real PointerHandler::margin |
| 73 | |
| 74 | The margin beyond the bounds of the \l {PointerHandler::parent}{parent} |
| 75 | item within which an \l eventPoint can activate this handler. For example, on |
| 76 | a PinchHandler where the \l {PointerHandler::target}{target} is also the |
| 77 | \c parent, it's useful to set this to a distance at least half the width |
| 78 | of a typical user's finger, so that if the \c parent has been scaled down |
| 79 | to a very small size, the pinch gesture is still possible. Or, if a |
| 80 | TapHandler-based button is placed near the screen edge, it can be used |
| 81 | to comply with Fitts's Law: react to mouse clicks at the screen edge |
| 82 | even though the button is visually spaced away from the edge by a few pixels. |
| 83 | |
| 84 | The default value is 0. |
| 85 | |
| 86 | \image pointerHandlerMargin.png |
| 87 | */ |
| 88 | qreal QQuickPointerHandler::margin() const |
| 89 | { |
| 90 | Q_D(const QQuickPointerHandler); |
| 91 | return d->m_margin; |
| 92 | } |
| 93 | |
| 94 | void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold) |
| 95 | { |
| 96 | Q_D(QQuickPointerHandler); |
| 97 | if (d->m_margin == pointDistanceThreshold) |
| 98 | return; |
| 99 | |
| 100 | d->m_margin = pointDistanceThreshold; |
| 101 | emit marginChanged(); |
| 102 | } |
| 103 | |
| 104 | /*! |
| 105 | \qmlproperty int PointerHandler::dragThreshold |
| 106 | \since 5.15 |
| 107 | |
| 108 | The distance in pixels that the user must drag an \l eventPoint in order to |
| 109 | have it treated as a drag gesture. |
| 110 | |
| 111 | The default value depends on the platform and screen resolution. |
| 112 | It can be reset back to the default value by setting it to undefined. |
| 113 | The behavior when a drag gesture begins varies in different handlers. |
| 114 | */ |
| 115 | int QQuickPointerHandler::dragThreshold() const |
| 116 | { |
| 117 | Q_D(const QQuickPointerHandler); |
| 118 | if (d->dragThreshold < 0) |
| 119 | return qApp->styleHints()->startDragDistance(); |
| 120 | return d->dragThreshold; |
| 121 | } |
| 122 | |
| 123 | void QQuickPointerHandler::setDragThreshold(int t) |
| 124 | { |
| 125 | Q_D(QQuickPointerHandler); |
| 126 | if (d->dragThreshold == t) |
| 127 | return; |
| 128 | |
| 129 | if (t > std::numeric_limits<qint16>::max()) |
| 130 | qWarning() << "drag threshold cannot exceed" << std::numeric_limits<qint16>::max(); |
| 131 | d->dragThreshold = qint16(t); |
| 132 | emit dragThresholdChanged(); |
| 133 | } |
| 134 | |
| 135 | void QQuickPointerHandler::resetDragThreshold() |
| 136 | { |
| 137 | Q_D(QQuickPointerHandler); |
| 138 | if (d->dragThreshold < 0) |
| 139 | return; |
| 140 | |
| 141 | d->dragThreshold = -1; |
| 142 | emit dragThresholdChanged(); |
| 143 | } |
| 144 | |
| 145 | /*! |
| 146 | \since 5.15 |
| 147 | \qmlproperty Qt::CursorShape PointerHandler::cursorShape |
| 148 | This property holds the cursor shape that will appear whenever the mouse is |
| 149 | hovering over the \l parent item while \l active is \c true. |
| 150 | |
| 151 | The available cursor shapes are: |
| 152 | \list |
| 153 | \li Qt.ArrowCursor |
| 154 | \li Qt.UpArrowCursor |
| 155 | \li Qt.CrossCursor |
| 156 | \li Qt.WaitCursor |
| 157 | \li Qt.IBeamCursor |
| 158 | \li Qt.SizeVerCursor |
| 159 | \li Qt.SizeHorCursor |
| 160 | \li Qt.SizeBDiagCursor |
| 161 | \li Qt.SizeFDiagCursor |
| 162 | \li Qt.SizeAllCursor |
| 163 | \li Qt.BlankCursor |
| 164 | \li Qt.SplitVCursor |
| 165 | \li Qt.SplitHCursor |
| 166 | \li Qt.PointingHandCursor |
| 167 | \li Qt.ForbiddenCursor |
| 168 | \li Qt.WhatsThisCursor |
| 169 | \li Qt.BusyCursor |
| 170 | \li Qt.OpenHandCursor |
| 171 | \li Qt.ClosedHandCursor |
| 172 | \li Qt.DragCopyCursor |
| 173 | \li Qt.DragMoveCursor |
| 174 | \li Qt.DragLinkCursor |
| 175 | \endlist |
| 176 | |
| 177 | The default value is not set, which allows the \l {QQuickItem::cursor()}{cursor} |
| 178 | of \l parent item to appear. This property can be reset to the same initial |
| 179 | condition by setting it to undefined. |
| 180 | |
| 181 | \note When this property has not been set, or has been set to \c undefined, |
| 182 | if you read the value it will return \c Qt.ArrowCursor. |
| 183 | |
| 184 | \sa Qt::CursorShape, QQuickItem::cursor(), HoverHandler::cursorShape |
| 185 | */ |
| 186 | #if QT_CONFIG(cursor) |
| 187 | Qt::CursorShape QQuickPointerHandler::cursorShape() const |
| 188 | { |
| 189 | Q_D(const QQuickPointerHandler); |
| 190 | return d->cursorShape; |
| 191 | } |
| 192 | |
| 193 | void QQuickPointerHandler::setCursorShape(Qt::CursorShape shape) |
| 194 | { |
| 195 | Q_D(QQuickPointerHandler); |
| 196 | if (d->cursorSet && shape == d->cursorShape) |
| 197 | return; |
| 198 | d->cursorShape = shape; |
| 199 | d->cursorSet = true; |
| 200 | d->cursorDirty = true; |
| 201 | if (auto *parent = parentItem()) { |
| 202 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item: parent); |
| 203 | itemPriv->hasCursorHandler = true; |
| 204 | itemPriv->setHasCursorInChild(true); |
| 205 | } |
| 206 | |
| 207 | emit cursorShapeChanged(); |
| 208 | } |
| 209 | |
| 210 | void QQuickPointerHandler::resetCursorShape() |
| 211 | { |
| 212 | Q_D(QQuickPointerHandler); |
| 213 | if (!d->cursorSet) |
| 214 | return; |
| 215 | d->cursorShape = Qt::ArrowCursor; |
| 216 | d->cursorSet = false; |
| 217 | if (auto *parent = parentItem()) { |
| 218 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item: parent); |
| 219 | itemPriv->hasCursorHandler = false; |
| 220 | itemPriv->setHasCursorInChild(itemPriv->hasCursor); |
| 221 | } |
| 222 | emit cursorShapeChanged(); |
| 223 | } |
| 224 | |
| 225 | bool QQuickPointerHandler::isCursorShapeExplicitlySet() const |
| 226 | { |
| 227 | Q_D(const QQuickPointerHandler); |
| 228 | return d->cursorSet; |
| 229 | } |
| 230 | #endif |
| 231 | |
| 232 | /*! |
| 233 | Notification that the grab has changed in some way which is relevant to this handler. |
| 234 | The \a grabber (subject) will be the Input Handler whose state is changing, |
| 235 | or null if the state change regards an Item. |
| 236 | The \a transition (verb) tells what happened. |
| 237 | The \a point (object) is the \l eventPoint that was grabbed or ungrabbed. |
| 238 | QQuickDeliveryAgent calls this function. |
| 239 | The Input Handler must react in whatever way is appropriate, and must |
| 240 | emit the relevant signals (for the benefit of QML code). |
| 241 | A subclass is allowed to override this virtual function, but must always |
| 242 | call its parent class's implementation in addition to (usually after) |
| 243 | whatever custom behavior it implements. |
| 244 | */ |
| 245 | void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition, |
| 246 | QPointerEvent *event, QEventPoint &point) |
| 247 | { |
| 248 | Q_UNUSED(event); |
| 249 | qCDebug(lcPointerHandlerGrab) << point << transition << grabber; |
| 250 | if (grabber == this) { |
| 251 | bool wasCanceled = false; |
| 252 | switch (transition) { |
| 253 | case QPointingDevice::GrabPassive: |
| 254 | case QPointingDevice::GrabExclusive: |
| 255 | break; |
| 256 | case QPointingDevice::CancelGrabPassive: |
| 257 | case QPointingDevice::CancelGrabExclusive: |
| 258 | wasCanceled = true; // the grab was stolen by something else |
| 259 | Q_FALLTHROUGH(); |
| 260 | case QPointingDevice::UngrabPassive: |
| 261 | case QPointingDevice::UngrabExclusive: |
| 262 | setActive(false); |
| 263 | point.setAccepted(false); |
| 264 | if (auto par = parentItem()) { |
| 265 | Q_D(const QQuickPointerHandler); |
| 266 | par->setKeepMouseGrab(d->hadKeepMouseGrab); |
| 267 | par->setKeepTouchGrab(d->hadKeepTouchGrab); |
| 268 | } |
| 269 | break; |
| 270 | case QPointingDevice::OverrideGrabPassive: |
| 271 | // Passive grab is still there, but we won't receive point updates right now. |
| 272 | // No need to notify about this. |
| 273 | return; |
| 274 | } |
| 275 | if (wasCanceled) |
| 276 | emit canceled(point); |
| 277 | emit grabChanged(transition, point); |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | /*! |
| 282 | Acquire or give up a passive grab of the given \a point, according to the \a grab state. |
| 283 | |
| 284 | Unlike the exclusive grab, multiple Input Handlers can have passive grabs |
| 285 | simultaneously. This means that each of them will receive further events |
| 286 | when the \a point moves, and when it is finally released. Typically an |
| 287 | Input Handler should acquire a passive grab as soon as a point is pressed, |
| 288 | if the handler's constraints do not clearly rule out any interest in that |
| 289 | point. For example, DragHandler needs a passive grab in order to watch the |
| 290 | movement of a point to see whether it will be dragged past the drag |
| 291 | threshold. When a handler is actively manipulating its \l target (that is, |
| 292 | when \l active is true), it may be able to do its work with only a passive |
| 293 | grab, or it may acquire an exclusive grab if the gesture clearly must not |
| 294 | be interpreted in another way by another handler. |
| 295 | */ |
| 296 | void QQuickPointerHandler::setPassiveGrab(QPointerEvent *event, const QEventPoint &point, bool grab) |
| 297 | { |
| 298 | qCDebug(lcPointerHandlerGrab) << this << point << grab << "via" |
| 299 | << QQuickDeliveryAgentPrivate::currentOrItemDeliveryAgent(item: parentItem()); |
| 300 | if (grab) { |
| 301 | event->addPassiveGrabber(point, grabber: this); |
| 302 | } else { |
| 303 | event->removePassiveGrabber(point, grabber: this); |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | /*! |
| 308 | Check whether it's OK to take an exclusive grab of the \a point. |
| 309 | |
| 310 | The default implementation will call approveGrabTransition() to check this |
| 311 | handler's \l grabPermissions. If grabbing can be done only by taking over |
| 312 | the exclusive grab from an Item, approveGrabTransition() checks the Item's |
| 313 | \l keepMouseGrab or \l keepTouchGrab flags appropriately. If grabbing can |
| 314 | be done only by taking over another handler's exclusive grab, canGrab() |
| 315 | also calls approveGrabTransition() on the handler which is about to lose |
| 316 | its grab. Either one can deny the takeover. |
| 317 | */ |
| 318 | bool QQuickPointerHandler::canGrab(QPointerEvent *event, const QEventPoint &point) |
| 319 | { |
| 320 | QQuickPointerHandler *existingPhGrabber = qobject_cast<QQuickPointerHandler *>(object: event->exclusiveGrabber(point)); |
| 321 | return approveGrabTransition(event, point, proposedGrabber: this) && |
| 322 | (existingPhGrabber ? existingPhGrabber->approveGrabTransition(event, point, proposedGrabber: this) : true); |
| 323 | } |
| 324 | |
| 325 | /*! |
| 326 | Check this handler's rules to see if \l proposedGrabber will be allowed to take |
| 327 | the exclusive grab. This function may be called twice: once on the instance which |
| 328 | will take the grab, and once on the instance which would thereby lose its grab, |
| 329 | in case of a takeover scenario. |
| 330 | */ |
| 331 | bool QQuickPointerHandler::approveGrabTransition(QPointerEvent *event, const QEventPoint &point, QObject *proposedGrabber) |
| 332 | { |
| 333 | Q_D(const QQuickPointerHandler); |
| 334 | bool allowed = false; |
| 335 | QObject* existingGrabber = event->exclusiveGrabber(point); |
| 336 | if (proposedGrabber == this) { |
| 337 | allowed = (existingGrabber == nullptr) || ((d->grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything); |
| 338 | if (existingGrabber) { |
| 339 | if (QQuickPointerHandler *existingPhGrabber = qobject_cast<QQuickPointerHandler *>(object: event->exclusiveGrabber(point))) { |
| 340 | if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfDifferentType) && |
| 341 | existingPhGrabber->metaObject()->className() != metaObject()->className()) |
| 342 | allowed = true; |
| 343 | if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfSameType) && |
| 344 | existingPhGrabber->metaObject()->className() == metaObject()->className()) |
| 345 | allowed = true; |
| 346 | } else if ((d->grabPermissions & CanTakeOverFromItems)) { |
| 347 | allowed = true; |
| 348 | QQuickItem * existingItemGrabber = qobject_cast<QQuickItem *>(o: event->exclusiveGrabber(point)); |
| 349 | auto da = parentItem() ? QQuickItemPrivate::get(item: parentItem())->deliveryAgentPrivate() |
| 350 | : QQuickDeliveryAgentPrivate::currentEventDeliveryAgent ? static_cast<QQuickDeliveryAgentPrivate *>( |
| 351 | QQuickDeliveryAgentPrivate::get(o: QQuickDeliveryAgentPrivate::currentEventDeliveryAgent)) : nullptr; |
| 352 | const bool isTouchMouse = (da && da->isDeliveringTouchAsMouse()); |
| 353 | if (existingItemGrabber && |
| 354 | ((existingItemGrabber->keepMouseGrab() && |
| 355 | (QQuickDeliveryAgentPrivate::isMouseEvent(ev: event) || isTouchMouse)) || |
| 356 | (existingItemGrabber->keepTouchGrab() && QQuickDeliveryAgentPrivate::isTouchEvent(ev: event)))) { |
| 357 | allowed = false; |
| 358 | // If the handler wants to steal the exclusive grab from an Item, the Item can usually veto |
| 359 | // by having its keepMouseGrab flag set. But an exception is if that Item is a parent that |
| 360 | // normally filters events (such as a Flickable): it needs to be possible for e.g. a |
| 361 | // DragHandler to operate on an Item inside a Flickable. Flickable is aggressive about |
| 362 | // grabbing on press (for fear of missing updates), but DragHandler uses a passive grab |
| 363 | // at first and then expects to be able to steal the grab later on. It cannot respect |
| 364 | // Flickable's wishes in that case, because then it would never have a chance. |
| 365 | if (existingItemGrabber->keepMouseGrab() && |
| 366 | existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(child: parentItem())) { |
| 367 | Q_ASSERT(da); |
| 368 | if (isTouchMouse && point.id() == da->touchMouseId) { |
| 369 | qCDebug(lcPointerHandlerGrab) << this << "steals touchpoint" << point.id() |
| 370 | << "despite parent touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber; |
| 371 | allowed = true; |
| 372 | } |
| 373 | } |
| 374 | if (!allowed) { |
| 375 | qCDebug(lcPointerHandlerGrab) << this << "wants to grab point" << point.id() |
| 376 | << "but declines to steal from grabber" << existingItemGrabber |
| 377 | << "with keepMouseGrab=" << existingItemGrabber->keepMouseGrab() |
| 378 | << "keepTouchGrab=" << existingItemGrabber->keepTouchGrab(); |
| 379 | } |
| 380 | } |
| 381 | } |
| 382 | } |
| 383 | } else { |
| 384 | // proposedGrabber is different: that means this instance will lose its grab |
| 385 | if (proposedGrabber) { |
| 386 | if ((d->grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything) |
| 387 | allowed = true; |
| 388 | if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) && |
| 389 | proposedGrabber->metaObject()->className() != metaObject()->className()) |
| 390 | allowed = true; |
| 391 | if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfSameType) && |
| 392 | proposedGrabber->metaObject()->className() == metaObject()->className()) |
| 393 | allowed = true; |
| 394 | if (!allowed && (d->grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits(classname: "QQuickItem" )) |
| 395 | allowed = true; |
| 396 | } else { |
| 397 | if (d->grabPermissions & ApprovesCancellation) |
| 398 | allowed = true; |
| 399 | } |
| 400 | } |
| 401 | qCDebug(lcPointerHandlerGrab) << "point" << Qt::hex << point.id() << "permission" << |
| 402 | QMetaEnum::fromType<GrabPermissions>().valueToKeys(value: grabPermissions()) << |
| 403 | ':' << this << (allowed ? "approved from" : "denied from" ) << |
| 404 | existingGrabber << "to" << proposedGrabber; |
| 405 | return allowed; |
| 406 | } |
| 407 | |
| 408 | /*! |
| 409 | \qmlproperty flags QtQuick::PointerHandler::grabPermissions |
| 410 | |
| 411 | This property specifies the permissions when this handler's logic decides |
| 412 | to take over the exclusive grab, or when it is asked to approve grab |
| 413 | takeover or cancellation by another handler. |
| 414 | |
| 415 | \value PointerHandler.TakeOverForbidden |
| 416 | This handler neither takes from nor gives grab permission to any type of Item or Handler. |
| 417 | \value PointerHandler.CanTakeOverFromHandlersOfSameType |
| 418 | This handler can take the exclusive grab from another handler of the same class. |
| 419 | \value PointerHandler.CanTakeOverFromHandlersOfDifferentType |
| 420 | This handler can take the exclusive grab from any kind of handler. |
| 421 | \value PointerHandler.CanTakeOverFromItems |
| 422 | This handler can take the exclusive grab from any type of Item. |
| 423 | \value PointerHandler.CanTakeOverFromAnything |
| 424 | This handler can take the exclusive grab from any type of Item or Handler. |
| 425 | \value PointerHandler.ApprovesTakeOverByHandlersOfSameType |
| 426 | This handler gives permission for another handler of the same class to take the grab. |
| 427 | \value PointerHandler.ApprovesTakeOverByHandlersOfDifferentType |
| 428 | This handler gives permission for any kind of handler to take the grab. |
| 429 | \value PointerHandler.ApprovesTakeOverByItems |
| 430 | This handler gives permission for any kind of Item to take the grab. |
| 431 | \value PointerHandler.ApprovesCancellation |
| 432 | This handler will allow its grab to be set to null. |
| 433 | \value PointerHandler.ApprovesTakeOverByAnything |
| 434 | This handler gives permission for any type of Item or Handler to take the grab. |
| 435 | |
| 436 | The default is |
| 437 | \c {PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything} |
| 438 | which allows most takeover scenarios but avoids e.g. two PinchHandlers fighting |
| 439 | over the same touchpoints. |
| 440 | */ |
| 441 | QQuickPointerHandler::GrabPermissions QQuickPointerHandler::grabPermissions() const |
| 442 | { |
| 443 | Q_D(const QQuickPointerHandler); |
| 444 | return static_cast<QQuickPointerHandler::GrabPermissions>(d->grabPermissions); |
| 445 | } |
| 446 | |
| 447 | void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission) |
| 448 | { |
| 449 | Q_D(QQuickPointerHandler); |
| 450 | if (d->grabPermissions == grabPermission) |
| 451 | return; |
| 452 | |
| 453 | d->grabPermissions = grabPermission; |
| 454 | emit grabPermissionChanged(); |
| 455 | } |
| 456 | |
| 457 | /*! |
| 458 | Overridden only because QQmlParserStatus requires it. |
| 459 | */ |
| 460 | void QQuickPointerHandler::classBegin() |
| 461 | { |
| 462 | } |
| 463 | |
| 464 | /*! |
| 465 | Overridden from QQmlParserStatus to ensure that parentItem() sets its |
| 466 | cursor if this handler's \l cursorShape property has been set. |
| 467 | */ |
| 468 | void QQuickPointerHandler::componentComplete() |
| 469 | { |
| 470 | Q_D(const QQuickPointerHandler); |
| 471 | if (d->cursorSet) { |
| 472 | if (auto *parent = parentItem()) { |
| 473 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item: parent); |
| 474 | itemPriv->hasCursorHandler = true; |
| 475 | itemPriv->setHasCursorInChild(true); |
| 476 | } |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | /*! \internal |
| 481 | \deprecated You should handle the event during delivery by overriding |
| 482 | handlePointerEventImpl() or QQuickSinglePointHandler::handleEventPoint(). |
| 483 | Therefore currentEvent() should not be needed. It is here only because |
| 484 | onActiveChanged() does not take the event as an argument. |
| 485 | */ |
| 486 | QPointerEvent *QQuickPointerHandler::currentEvent() |
| 487 | { |
| 488 | Q_D(const QQuickPointerHandler); |
| 489 | return d->currentEvent; |
| 490 | } |
| 491 | |
| 492 | /*! |
| 493 | Acquire or give up the exclusive grab of the given \a point, according to |
| 494 | the \a grab state, and subject to the rules: canGrab(), and the rule not to |
| 495 | relinquish another handler's grab. Returns true if permission is granted, |
| 496 | or if the exclusive grab has already been acquired or relinquished as |
| 497 | specified. Returns false if permission is denied either by this handler or |
| 498 | by the handler or item from which this handler would take over |
| 499 | */ |
| 500 | bool QQuickPointerHandler::setExclusiveGrab(QPointerEvent *ev, const QEventPoint &point, bool grab) |
| 501 | { |
| 502 | if ((grab && ev->exclusiveGrabber(point) == this) || (!grab && ev->exclusiveGrabber(point) != this)) |
| 503 | return true; |
| 504 | // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab |
| 505 | bool allowed = true; |
| 506 | if (grab) { |
| 507 | allowed = canGrab(event: ev, point); |
| 508 | } else { |
| 509 | QQuickPointerHandler *existingPhGrabber = qobject_cast<QQuickPointerHandler *>(object: ev->exclusiveGrabber(point)); |
| 510 | // Ask before allowing one handler to cancel another's grab |
| 511 | if (existingPhGrabber && existingPhGrabber != this && !existingPhGrabber->approveGrabTransition(event: ev, point, proposedGrabber: nullptr)) |
| 512 | allowed = false; |
| 513 | } |
| 514 | qCDebug(lcPointerHandlerGrab) << point << (grab ? "grab" : "ungrab" ) << (allowed ? "allowed" : "forbidden" ) << |
| 515 | ev->exclusiveGrabber(point) << "->" << (grab ? this : nullptr); |
| 516 | if (allowed) |
| 517 | ev->setExclusiveGrabber(point, exclusiveGrabber: grab ? this : nullptr); |
| 518 | return allowed; |
| 519 | } |
| 520 | |
| 521 | /*! |
| 522 | Cancel any existing grab of the given \a point. |
| 523 | */ |
| 524 | void QQuickPointerHandler::cancelAllGrabs(QPointerEvent *event, QEventPoint &point) |
| 525 | { |
| 526 | qCDebug(lcPointerHandlerGrab) << point; |
| 527 | if (event->exclusiveGrabber(point) == this) { |
| 528 | event->setExclusiveGrabber(point, exclusiveGrabber: nullptr); |
| 529 | onGrabChanged(grabber: this, transition: QPointingDevice::CancelGrabExclusive, event, point); |
| 530 | } |
| 531 | if (event->removePassiveGrabber(point, grabber: this)) |
| 532 | onGrabChanged(grabber: this, transition: QPointingDevice::CancelGrabPassive, event, point); |
| 533 | } |
| 534 | |
| 535 | QPointF QQuickPointerHandler::eventPos(const QEventPoint &point) const |
| 536 | { |
| 537 | return (target() ? target()->mapFromScene(point: point.scenePosition()) : point.scenePosition()); |
| 538 | } |
| 539 | |
| 540 | /*! |
| 541 | Returns \c true if margin() > 0 and \a point is within the margin beyond |
| 542 | QQuickItem::boundingRect(), or else returns QQuickItem::contains() |
| 543 | QEventPoint::position() effectively (because parentContains(scenePosition) |
| 544 | calls QQuickItem::mapFromScene()). |
| 545 | */ |
| 546 | bool QQuickPointerHandler::parentContains(const QEventPoint &point) const |
| 547 | { |
| 548 | return parentContains(scenePosition: point.scenePosition()); |
| 549 | } |
| 550 | |
| 551 | /*! |
| 552 | Returns \c true if \a scenePosition is within the margin() beyond |
| 553 | QQuickItem::boundingRect() (if margin > 0), or parentItem() contains |
| 554 | \a scenePosition according to QQuickItem::contains(). (So if the \l margin |
| 555 | property is set, that overrides the bounds-check, and QQuickItem::contains() |
| 556 | is not called.) As a precheck, it's also required that the window contains |
| 557 | \a scenePosition mapped to global coordinates, if parentItem() is in a window. |
| 558 | */ |
| 559 | bool QQuickPointerHandler::parentContains(const QPointF &scenePosition) const |
| 560 | { |
| 561 | if (QQuickItem *par = parentItem()) { |
| 562 | if (par->window()) { |
| 563 | QRectF windowGeometry = par->window()->geometry(); |
| 564 | if (!par->window()->isTopLevel()) |
| 565 | windowGeometry = QRectF(QWindowPrivate::get(window: par->window())->globalPosition(), par->window()->size()); |
| 566 | QPointF screenPosition = par->window()->mapToGlobal(pos: scenePosition); |
| 567 | if (!windowGeometry.contains(p: screenPosition)) |
| 568 | return false; |
| 569 | } |
| 570 | QPointF p = par->mapFromScene(point: scenePosition); |
| 571 | qreal m = margin(); |
| 572 | if (m > 0) |
| 573 | return p.x() >= -m && p.y() >= -m && p.x() <= par->width() + m && p.y() <= par->height() + m; |
| 574 | return par->contains(point: p); |
| 575 | } else if (parent() && parent()->inherits(classname: "QQuick3DModel" )) { |
| 576 | // If the parent is from Qt Quick 3D, assume that |
| 577 | // bounds checking was already done, as part of picking. |
| 578 | return true; |
| 579 | } |
| 580 | return false; |
| 581 | } |
| 582 | |
| 583 | /*! |
| 584 | \qmlproperty bool QtQuick::PointerHandler::enabled |
| 585 | |
| 586 | If a PointerHandler is disabled, it will reject all events |
| 587 | and no signals will be emitted. |
| 588 | */ |
| 589 | bool QQuickPointerHandler::enabled() const |
| 590 | { |
| 591 | Q_D(const QQuickPointerHandler); |
| 592 | return d->enabled; |
| 593 | } |
| 594 | |
| 595 | void QQuickPointerHandler::setEnabled(bool enabled) |
| 596 | { |
| 597 | Q_D(QQuickPointerHandler); |
| 598 | if (d->enabled == enabled) |
| 599 | return; |
| 600 | |
| 601 | d->enabled = enabled; |
| 602 | d->onEnabledChanged(); |
| 603 | |
| 604 | emit enabledChanged(); |
| 605 | } |
| 606 | |
| 607 | /*! |
| 608 | \qmlproperty Item QtQuick::PointerHandler::target |
| 609 | |
| 610 | The Item which this handler will manipulate. |
| 611 | |
| 612 | By default, it is the same as the \l [QML] {parent}, the Item within which |
| 613 | the handler is declared. However, it can sometimes be useful to set the |
| 614 | target to a different Item, in order to handle events within one item |
| 615 | but manipulate another; or to \c null, to disable the default behavior |
| 616 | and do something else instead. |
| 617 | */ |
| 618 | QQuickItem *QQuickPointerHandler::target() const |
| 619 | { |
| 620 | Q_D(const QQuickPointerHandler); |
| 621 | if (!d->targetExplicitlySet) |
| 622 | return parentItem(); |
| 623 | return d->target; |
| 624 | } |
| 625 | |
| 626 | void QQuickPointerHandler::setTarget(QQuickItem *target) |
| 627 | { |
| 628 | Q_D(QQuickPointerHandler); |
| 629 | d->targetExplicitlySet = true; |
| 630 | if (d->target == target) |
| 631 | return; |
| 632 | |
| 633 | QQuickItem *oldTarget = d->target; |
| 634 | d->target = target; |
| 635 | onTargetChanged(oldTarget); |
| 636 | emit targetChanged(); |
| 637 | } |
| 638 | |
| 639 | /*! |
| 640 | \qmlproperty Item QtQuick::PointerHandler::parent |
| 641 | |
| 642 | The \l Item which is the scope of the handler; the Item in which it was |
| 643 | declared. The handler will handle events on behalf of this Item, which |
| 644 | means a pointer event is relevant if at least one of its |
| 645 | \l {eventPoint}{eventPoints} occurs within the Item's interior. Initially |
| 646 | \l [QML] {target} {target()} is the same, but it can be reassigned. |
| 647 | |
| 648 | \sa {target}, QObject::parent() |
| 649 | */ |
| 650 | /*! \internal |
| 651 | We still haven't shipped official support for declaring handlers in |
| 652 | QtQuick3D.Model objects. Many prerequisites are in place for that, so we |
| 653 | should try to keep it working; but there are issues with getting |
| 654 | DragHandler to drag its target intuitively in 3D space, for example. |
| 655 | TapHandler would work well enough. |
| 656 | |
| 657 | \note When a handler is declared in a \l [QtQuick3D] {Model}{QtQuick3D.Model} |
| 658 | object, the parent is not an Item, therefore this property is \c null. |
| 659 | */ |
| 660 | QQuickItem *QQuickPointerHandler::parentItem() const |
| 661 | { |
| 662 | return qmlobject_cast<QQuickItem *>(object: QObject::parent()); |
| 663 | } |
| 664 | |
| 665 | void QQuickPointerHandler::setParentItem(QQuickItem *p) |
| 666 | { |
| 667 | Q_D(QQuickPointerHandler); |
| 668 | if (QObject::parent() == p) |
| 669 | return; |
| 670 | |
| 671 | qCDebug(lcHandlerParent) << "reparenting handler" << this << ":" << parent() << "->" << p; |
| 672 | auto *oldParent = static_cast<QQuickItem *>(QObject::parent()); |
| 673 | if (oldParent) |
| 674 | QQuickItemPrivate::get(item: oldParent)->removePointerHandler(h: this); |
| 675 | setParent(p); |
| 676 | if (p) |
| 677 | QQuickItemPrivate::get(item: p)->addPointerHandler(h: this); |
| 678 | d->onParentChanged(oldParent, p); |
| 679 | emit parentChanged(); |
| 680 | } |
| 681 | |
| 682 | /*! \internal |
| 683 | Pointer Handlers do most of their work in implementations of virtual functions |
| 684 | that are called directly from QQuickItem, not by direct event handling. |
| 685 | But it's convenient to deliver TouchCancel events via QCoreApplication::sendEvent(). |
| 686 | Perhaps it will turn out that more events could be delivered this way. |
| 687 | */ |
| 688 | bool QQuickPointerHandler::event(QEvent *e) |
| 689 | { |
| 690 | switch (e->type()) { |
| 691 | case QEvent::TouchCancel: { |
| 692 | auto te = static_cast<QTouchEvent *>(e); |
| 693 | for (int i = 0; i < te->pointCount(); ++i) |
| 694 | onGrabChanged(grabber: this, transition: QPointingDevice::CancelGrabExclusive, event: te, point&: te->point(i)); |
| 695 | return true; |
| 696 | break; |
| 697 | } |
| 698 | default: |
| 699 | return QObject::event(event: e); |
| 700 | break; |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | /*! \internal |
| 705 | The entry point to handle the \a event: it's called from |
| 706 | QQuickItemPrivate::handlePointerEvent(), begins with wantsPointerEvent(), |
| 707 | and calls handlePointerEventImpl() if that returns \c true. |
| 708 | */ |
| 709 | void QQuickPointerHandler::handlePointerEvent(QPointerEvent *event) |
| 710 | { |
| 711 | Q_D(QQuickPointerHandler); |
| 712 | bool wants = wantsPointerEvent(event); |
| 713 | qCDebug(lcPointerHandlerDispatch) << metaObject()->className() << objectName() |
| 714 | << "on" << parent()->metaObject()->className() << parent()->objectName() |
| 715 | << (wants ? "WANTS" : "DECLINES" ) << event; |
| 716 | d->currentEvent = event; |
| 717 | if (wants) { |
| 718 | handlePointerEventImpl(event); |
| 719 | d->lastEventTime = event->timestamp(); |
| 720 | } else { |
| 721 | #if QT_CONFIG(gestures) |
| 722 | if (event->type() != QEvent::NativeGesture) |
| 723 | #endif |
| 724 | setActive(false); |
| 725 | for (int i = 0; i < event->pointCount(); ++i) { |
| 726 | auto &pt = event->point(i); |
| 727 | if (event->exclusiveGrabber(point: pt) == this && pt.state() != QEventPoint::Stationary) |
| 728 | event->setExclusiveGrabber(point: pt, exclusiveGrabber: nullptr); |
| 729 | } |
| 730 | } |
| 731 | d->currentEvent = nullptr; |
| 732 | QQuickPointerHandlerPrivate::deviceDeliveryTargets(device: event->device()).append(t: this); |
| 733 | } |
| 734 | |
| 735 | /*! |
| 736 | It is the responsibility of this function to decide whether the \a event |
| 737 | could be relevant at all to this handler, as a preliminary check. |
| 738 | |
| 739 | Returns \c true if this handler would like handlePointerEventImpl() to be called. |
| 740 | If it returns \c false, the handler will be deactivated: \c setActive(false) |
| 741 | will be called, and any remaining exclusive grab will be relinquished, |
| 742 | as a fail-safe. |
| 743 | |
| 744 | If you override this function, you should call the immediate parent class |
| 745 | implementation (and return \c false if it returns \c false); that in turn |
| 746 | calls its parent class implementation, and so on. |
| 747 | QQuickSinglePointHandler::wantsPointerEvent() and |
| 748 | QQuickMultiPointHandler::wantsPointerEvent() call wantsEventPoint(), which |
| 749 | is also virtual. You usually can get the behavior you want by subclassing |
| 750 | the appropriate handler type, overriding |
| 751 | QQuickSinglePointHandler::handleEventPoint() or handlePointerEventImpl(), |
| 752 | and perhaps overriding wantsEventPoint() if needed. |
| 753 | |
| 754 | \sa wantsEventPoint(), QQuickPointerDeviceHandler::wantsPointerEvent(), |
| 755 | QQuickMultiPointHandler::wantsPointerEvent(), QQuickSinglePointHandler::wantsPointerEvent() |
| 756 | */ |
| 757 | bool QQuickPointerHandler::wantsPointerEvent(QPointerEvent *event) |
| 758 | { |
| 759 | Q_D(const QQuickPointerHandler); |
| 760 | Q_UNUSED(event); |
| 761 | return d->enabled; |
| 762 | } |
| 763 | |
| 764 | /*! |
| 765 | Returns \c true if the given \a point (as part of \a event) could be |
| 766 | relevant at all to this handler, as a preliminary check. |
| 767 | |
| 768 | If you override this function, you should call the immediate parent class |
| 769 | implementation (and return \c false if it returns \c false); that in turn |
| 770 | calls its parent class implementation, and so on. |
| 771 | |
| 772 | In particular, the bounds checking is done here: the base class |
| 773 | QQuickPointerHandler::wantsEventPoint() calls parentContains(point) |
| 774 | (which allows the flexibility promised by margin(), QQuickItem::contains() |
| 775 | and QQuickItem::containmentMask()). Pointer Handlers can receive |
| 776 | QEventPoints that are outside the parent item's bounds: this allows some |
| 777 | flexibility for dealing with multi-point gestures in which one or more |
| 778 | fingers have strayed outside the bounds, and yet the gesture is still |
| 779 | unambiguously intended for the target() item. |
| 780 | |
| 781 | You should not generally react to the \a event or \a point here, but it's |
| 782 | ok to set state to remember what needs to be done in your overridden |
| 783 | handlePointerEventImpl() or QQuickSinglePointHandler::handleEventPoint(). |
| 784 | */ |
| 785 | bool QQuickPointerHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point) |
| 786 | { |
| 787 | Q_UNUSED(event); |
| 788 | bool ret = event->exclusiveGrabber(point) == this || |
| 789 | event->passiveGrabbers(point).contains(t: this) || parentContains(point); |
| 790 | qCDebug(lcPointerHandlerDispatch) << Qt::hex << point.id() << "@" << point.scenePosition() |
| 791 | << metaObject()->className() << objectName() << ret; |
| 792 | return ret; |
| 793 | } |
| 794 | |
| 795 | /*! |
| 796 | \readonly |
| 797 | \qmlproperty bool QtQuick::PointerHandler::active |
| 798 | |
| 799 | This holds \c true whenever this Input Handler has taken sole responsibility |
| 800 | for handing one or more \l {eventPoint}{eventPoints}, by successfully taking an |
| 801 | exclusive grab of those points. This means that it is keeping its properties |
| 802 | up-to-date according to the movements of those eventPoints and actively |
| 803 | manipulating its \l target (if any). |
| 804 | */ |
| 805 | bool QQuickPointerHandler::active() const |
| 806 | { |
| 807 | Q_D(const QQuickPointerHandler); |
| 808 | return d->active; |
| 809 | } |
| 810 | |
| 811 | void QQuickPointerHandler::setActive(bool active) |
| 812 | { |
| 813 | Q_D(QQuickPointerHandler); |
| 814 | if (d->active != active) { |
| 815 | qCDebug(lcPointerHandlerActive) << this << d->active << "->" << active; |
| 816 | d->active = active; |
| 817 | onActiveChanged(); |
| 818 | emit activeChanged(); |
| 819 | } |
| 820 | } |
| 821 | |
| 822 | /*! |
| 823 | This function can be overridden to implement whatever behavior a specific |
| 824 | subclass is intended to have: |
| 825 | \list |
| 826 | \li Handle all the event's QPointerEvent::points() for which |
| 827 | wantsEventPoint() already returned \c true. |
| 828 | \li Call setPassiveGrab() setExclusiveGrab() or cancelAllGrabs() as |
| 829 | necessary. |
| 830 | \li Call QEvent::accept() to stop propagation, or ignore() to allow it |
| 831 | to keep going. |
| 832 | \endlist |
| 833 | */ |
| 834 | void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *event) |
| 835 | { |
| 836 | Q_UNUSED(event); |
| 837 | } |
| 838 | |
| 839 | /*! |
| 840 | \qmlsignal QtQuick::PointerHandler::grabChanged(PointerDevice::GrabTransition transition, eventPoint point) |
| 841 | |
| 842 | This signal is emitted when the grab has changed in some way which is |
| 843 | relevant to this handler. |
| 844 | |
| 845 | The \a transition (verb) tells what happened. |
| 846 | The \a point (object) is the point that was grabbed or ungrabbed. |
| 847 | |
| 848 | Valid values for \a transition are: |
| 849 | |
| 850 | \value PointerDevice.GrabExclusive |
| 851 | This handler has taken primary responsibility for handling the \a point. |
| 852 | \value PointerDevice.UngrabExclusive |
| 853 | This handler has given up its previous exclusive grab. |
| 854 | \value PointerDevice.CancelGrabExclusive |
| 855 | This handler's exclusive grab has been taken over or cancelled. |
| 856 | \value PointerDevice.GrabPassive |
| 857 | This handler has acquired a passive grab, to monitor the \a point. |
| 858 | \value PointerDevice.UngrabPassive |
| 859 | This handler has given up its previous passive grab. |
| 860 | \value PointerDevice.CancelGrabPassive |
| 861 | This handler's previous passive grab has terminated abnormally. |
| 862 | */ |
| 863 | |
| 864 | /*! |
| 865 | \qmlsignal QtQuick::PointerHandler::canceled(eventPoint point) |
| 866 | |
| 867 | If this handler has already grabbed the given \a point, this signal is |
| 868 | emitted when the grab is stolen by a different Pointer Handler or Item. |
| 869 | */ |
| 870 | |
| 871 | QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate() |
| 872 | : grabPermissions(QQuickPointerHandler::CanTakeOverFromItems | |
| 873 | QQuickPointerHandler::CanTakeOverFromHandlersOfDifferentType | |
| 874 | QQuickPointerHandler::ApprovesTakeOverByAnything) |
| 875 | , cursorShape(Qt::ArrowCursor) |
| 876 | , enabled(true) |
| 877 | , active(false) |
| 878 | , targetExplicitlySet(false) |
| 879 | , hadKeepMouseGrab(false) |
| 880 | , hadKeepTouchGrab(false) |
| 881 | , cursorSet(false) |
| 882 | , cursorDirty(false) |
| 883 | { |
| 884 | } |
| 885 | |
| 886 | /*! \internal |
| 887 | Returns \c true if the movement delta \a d in pixels along the \a axis |
| 888 | exceeds QQuickPointerHandler::dragThreshold() \e or QEventPoint::velocity() |
| 889 | exceeds QStyleHints::startDragVelocity(). |
| 890 | |
| 891 | \sa QQuickDeliveryAgentPrivate::dragOverThreshold() |
| 892 | */ |
| 893 | template <typename TEventPoint> |
| 894 | bool QQuickPointerHandlerPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const TEventPoint &p) const |
| 895 | { |
| 896 | Q_Q(const QQuickPointerHandler); |
| 897 | QStyleHints *styleHints = qApp->styleHints(); |
| 898 | bool overThreshold = qAbs(t: d) > q->dragThreshold(); |
| 899 | const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0); |
| 900 | if (!overThreshold && dragVelocityLimitAvailable) { |
| 901 | qreal velocity = qreal(axis == Qt::XAxis ? p.velocity().x() : p.velocity().y()); |
| 902 | overThreshold |= qAbs(t: velocity) > styleHints->startDragVelocity(); |
| 903 | } |
| 904 | return overThreshold; |
| 905 | } |
| 906 | |
| 907 | /*! |
| 908 | Returns \c true if the movement \a delta in pixels exceeds |
| 909 | QQuickPointerHandler::dragThreshold(). |
| 910 | |
| 911 | \sa QQuickDeliveryAgentPrivate::dragOverThreshold() |
| 912 | */ |
| 913 | bool QQuickPointerHandlerPrivate::dragOverThreshold(QVector2D delta) const |
| 914 | { |
| 915 | Q_Q(const QQuickPointerHandler); |
| 916 | const float threshold = q->dragThreshold(); |
| 917 | return qAbs(t: delta.x()) > threshold || qAbs(t: delta.y()) > threshold; |
| 918 | } |
| 919 | |
| 920 | /*! |
| 921 | Returns \c true if the movement delta of \a point in pixels |
| 922 | (calculated as QEventPoint::scenePosition() - QEventPoint::scenePressPosition()) |
| 923 | exceeds QQuickPointerHandler::dragThreshold(). |
| 924 | |
| 925 | \sa QQuickDeliveryAgentPrivate::dragOverThreshold() |
| 926 | */ |
| 927 | bool QQuickPointerHandlerPrivate::dragOverThreshold(const QEventPoint &point) const |
| 928 | { |
| 929 | QPointF delta = point.scenePosition() - point.scenePressPosition(); |
| 930 | return (dragOverThreshold(d: delta.x(), axis: Qt::XAxis, p: point) || |
| 931 | dragOverThreshold(d: delta.y(), axis: Qt::YAxis, p: point)); |
| 932 | } |
| 933 | |
| 934 | QVector<QObject *> &QQuickPointerHandlerPrivate::deviceDeliveryTargets(const QInputDevice *device) |
| 935 | { |
| 936 | return QQuickDeliveryAgentPrivate::deviceExtra(device)->deliveryTargets; |
| 937 | } |
| 938 | |
| 939 | QT_END_NAMESPACE |
| 940 | |
| 941 | #include "moc_qquickpointerhandler_p.cpp" |
| 942 | |