| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2018 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtQuick module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| 21 | ** packaging of this file. Please review the following information to |
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements |
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| 24 | ** |
| 25 | ** GNU General Public License Usage |
| 26 | ** Alternatively, this file may be used under the terms of the GNU |
| 27 | ** General Public License version 2.0 or (at your option) the GNU General |
| 28 | ** Public license version 3 or any later version approved by the KDE Free |
| 29 | ** Qt Foundation. The licenses are as published by the Free Software |
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| 31 | ** included in the packaging of this file. Please review the following |
| 32 | ** information to ensure the GNU General Public License requirements will |
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
| 35 | ** |
| 36 | ** $QT_END_LICENSE$ |
| 37 | ** |
| 38 | ****************************************************************************/ |
| 39 | |
| 40 | #include "qquickpointerhandler_p.h" |
| 41 | #include "qquickpointerhandler_p_p.h" |
| 42 | #include <QtQuick/private/qquickitem_p.h> |
| 43 | |
| 44 | QT_BEGIN_NAMESPACE |
| 45 | |
| 46 | Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch" ) |
| 47 | Q_LOGGING_CATEGORY(lcPointerHandlerGrab, "qt.quick.handler.grab" ) |
| 48 | Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active" ) |
| 49 | |
| 50 | /*! |
| 51 | \qmltype PointerHandler |
| 52 | \qmlabstract |
| 53 | \since 5.10 |
| 54 | \instantiates QQuickPointerHandler |
| 55 | \inqmlmodule QtQuick |
| 56 | \brief Abstract handler for pointer events. |
| 57 | |
| 58 | PointerHandler is the base class Input Handler (not registered as a QML type) for |
| 59 | events from any kind of pointing device (touch, mouse or graphics tablet). |
| 60 | */ |
| 61 | |
| 62 | QQuickPointerHandler::QQuickPointerHandler(QQuickItem *parent) |
| 63 | : QObject(*(new QQuickPointerHandlerPrivate), parent) |
| 64 | { |
| 65 | } |
| 66 | |
| 67 | QQuickPointerHandler::QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QQuickItem *parent) |
| 68 | : QObject(dd, parent) |
| 69 | { |
| 70 | } |
| 71 | |
| 72 | QQuickPointerHandler::~QQuickPointerHandler() |
| 73 | { |
| 74 | QQuickItem *parItem = parentItem(); |
| 75 | if (parItem) { |
| 76 | QQuickItemPrivate *p = QQuickItemPrivate::get(item: parItem); |
| 77 | p->extra.value().pointerHandlers.removeOne(t: this); |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | /*! |
| 82 | \qmlproperty real PointerHandler::margin |
| 83 | |
| 84 | The margin beyond the bounds of the \l {PointerHandler::parent}{parent} |
| 85 | item within which an event point can activate this handler. For example, on |
| 86 | a PinchHandler where the \l {PointerHandler::target}{target} is also the |
| 87 | \c parent, it's useful to set this to a distance at least half the width |
| 88 | of a typical user's finger, so that if the \c parent has been scaled down |
| 89 | to a very small size, the pinch gesture is still possible. Or, if a |
| 90 | TapHandler-based button is placed near the screen edge, it can be used |
| 91 | to comply with Fitts's Law: react to mouse clicks at the screen edge |
| 92 | even though the button is visually spaced away from the edge by a few pixels. |
| 93 | |
| 94 | The default value is 0. |
| 95 | |
| 96 | \image pointerHandlerMargin.png |
| 97 | */ |
| 98 | qreal QQuickPointerHandler::margin() const |
| 99 | { |
| 100 | Q_D(const QQuickPointerHandler); |
| 101 | return d->m_margin; |
| 102 | } |
| 103 | |
| 104 | void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold) |
| 105 | { |
| 106 | Q_D(QQuickPointerHandler); |
| 107 | if (d->m_margin == pointDistanceThreshold) |
| 108 | return; |
| 109 | |
| 110 | d->m_margin = pointDistanceThreshold; |
| 111 | emit marginChanged(); |
| 112 | } |
| 113 | |
| 114 | /*! |
| 115 | \qmlproperty int PointerHandler::dragThreshold |
| 116 | \since 5.15 |
| 117 | |
| 118 | The distance in pixels that the user must drag an event point in order to |
| 119 | have it treated as a drag gesture. |
| 120 | |
| 121 | The default value depends on the platform and screen resolution. |
| 122 | It can be reset back to the default value by setting it to undefined. |
| 123 | The behavior when a drag gesture begins varies in different handlers. |
| 124 | */ |
| 125 | int QQuickPointerHandler::dragThreshold() const |
| 126 | { |
| 127 | Q_D(const QQuickPointerHandler); |
| 128 | if (d->dragThreshold < 0) |
| 129 | return qApp->styleHints()->startDragDistance(); |
| 130 | return d->dragThreshold; |
| 131 | } |
| 132 | |
| 133 | void QQuickPointerHandler::setDragThreshold(int t) |
| 134 | { |
| 135 | Q_D(QQuickPointerHandler); |
| 136 | if (d->dragThreshold == t) |
| 137 | return; |
| 138 | |
| 139 | if (t > std::numeric_limits<qint16>::max()) |
| 140 | qWarning() << "drag threshold cannot exceed" << std::numeric_limits<qint16>::max(); |
| 141 | d->dragThreshold = qint16(t); |
| 142 | emit dragThresholdChanged(); |
| 143 | } |
| 144 | |
| 145 | void QQuickPointerHandler::resetDragThreshold() |
| 146 | { |
| 147 | Q_D(QQuickPointerHandler); |
| 148 | if (d->dragThreshold < 0) |
| 149 | return; |
| 150 | |
| 151 | d->dragThreshold = -1; |
| 152 | emit dragThresholdChanged(); |
| 153 | } |
| 154 | |
| 155 | /*! |
| 156 | \since 5.15 |
| 157 | \qmlproperty Qt::CursorShape PointerHandler::cursorShape |
| 158 | This property holds the cursor shape that will appear whenever the mouse is |
| 159 | hovering over the \l parentItem while \l active is \c true. |
| 160 | |
| 161 | The available cursor shapes are: |
| 162 | \list |
| 163 | \li Qt.ArrowCursor |
| 164 | \li Qt.UpArrowCursor |
| 165 | \li Qt.CrossCursor |
| 166 | \li Qt.WaitCursor |
| 167 | \li Qt.IBeamCursor |
| 168 | \li Qt.SizeVerCursor |
| 169 | \li Qt.SizeHorCursor |
| 170 | \li Qt.SizeBDiagCursor |
| 171 | \li Qt.SizeFDiagCursor |
| 172 | \li Qt.SizeAllCursor |
| 173 | \li Qt.BlankCursor |
| 174 | \li Qt.SplitVCursor |
| 175 | \li Qt.SplitHCursor |
| 176 | \li Qt.PointingHandCursor |
| 177 | \li Qt.ForbiddenCursor |
| 178 | \li Qt.WhatsThisCursor |
| 179 | \li Qt.BusyCursor |
| 180 | \li Qt.OpenHandCursor |
| 181 | \li Qt.ClosedHandCursor |
| 182 | \li Qt.DragCopyCursor |
| 183 | \li Qt.DragMoveCursor |
| 184 | \li Qt.DragLinkCursor |
| 185 | \endlist |
| 186 | |
| 187 | The default value is not set, which allows the \l {QQuickItem::cursor()}{cursor} |
| 188 | of \l parentItem to appear. This property can be reset to the same initial |
| 189 | condition by setting it to undefined. |
| 190 | |
| 191 | \note When this property has not been set, or has been set to \c undefined, |
| 192 | if you read the value it will return \c Qt.ArrowCursor. |
| 193 | |
| 194 | \sa Qt::CursorShape, QQuickItem::cursor(), HoverHandler::cursorShape |
| 195 | */ |
| 196 | #if QT_CONFIG(cursor) |
| 197 | Qt::CursorShape QQuickPointerHandler::cursorShape() const |
| 198 | { |
| 199 | Q_D(const QQuickPointerHandler); |
| 200 | return d->cursorShape; |
| 201 | } |
| 202 | |
| 203 | void QQuickPointerHandler::setCursorShape(Qt::CursorShape shape) |
| 204 | { |
| 205 | Q_D(QQuickPointerHandler); |
| 206 | if (d->cursorSet && shape == d->cursorShape) |
| 207 | return; |
| 208 | d->cursorShape = shape; |
| 209 | d->cursorSet = true; |
| 210 | if (auto *par = qmlobject_cast<QQuickItem *>(object: parent())) { |
| 211 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item: par); |
| 212 | itemPriv->hasCursorHandler = true; |
| 213 | itemPriv->setHasCursorInChild(true); |
| 214 | } |
| 215 | emit cursorShapeChanged(); |
| 216 | } |
| 217 | |
| 218 | void QQuickPointerHandler::resetCursorShape() |
| 219 | { |
| 220 | Q_D(QQuickPointerHandler); |
| 221 | if (!d->cursorSet) |
| 222 | return; |
| 223 | d->cursorShape = Qt::ArrowCursor; |
| 224 | d->cursorSet = false; |
| 225 | if (auto *parent = parentItem()) { |
| 226 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item: parent); |
| 227 | itemPriv->hasCursorHandler = false; |
| 228 | itemPriv->setHasCursorInChild(itemPriv->hasCursor); |
| 229 | } |
| 230 | emit cursorShapeChanged(); |
| 231 | } |
| 232 | |
| 233 | bool QQuickPointerHandler::isCursorShapeExplicitlySet() const |
| 234 | { |
| 235 | Q_D(const QQuickPointerHandler); |
| 236 | return d->cursorSet; |
| 237 | } |
| 238 | #endif |
| 239 | |
| 240 | /*! |
| 241 | Notification that the grab has changed in some way which is relevant to this handler. |
| 242 | The \a grabber (subject) will be the Input Handler whose state is changing, |
| 243 | or null if the state change regards an Item. |
| 244 | The \a transition (verb) tells what happened. |
| 245 | The \a point (object) is the point that was grabbed or ungrabbed. |
| 246 | EventPoint has the sole responsibility to call this function. |
| 247 | The Input Handler must react in whatever way is appropriate, and must |
| 248 | emit the relevant signals (for the benefit of QML code). |
| 249 | A subclass is allowed to override this virtual function, but must always |
| 250 | call its parent class's implementation in addition to (usually after) |
| 251 | whatever custom behavior it implements. |
| 252 | */ |
| 253 | void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) |
| 254 | { |
| 255 | qCDebug(lcPointerHandlerGrab) << point << transition << grabber; |
| 256 | Q_ASSERT(point); |
| 257 | if (grabber == this) { |
| 258 | bool wasCanceled = false; |
| 259 | switch (transition) { |
| 260 | case QQuickEventPoint::GrabPassive: |
| 261 | case QQuickEventPoint::GrabExclusive: |
| 262 | break; |
| 263 | case QQuickEventPoint::CancelGrabPassive: |
| 264 | case QQuickEventPoint::CancelGrabExclusive: |
| 265 | wasCanceled = true; // the grab was stolen by something else |
| 266 | Q_FALLTHROUGH(); |
| 267 | case QQuickEventPoint::UngrabPassive: |
| 268 | case QQuickEventPoint::UngrabExclusive: |
| 269 | setActive(false); |
| 270 | point->setAccepted(false); |
| 271 | if (auto par = parentItem()) { |
| 272 | Q_D(const QQuickPointerHandler); |
| 273 | par->setKeepMouseGrab(d->hadKeepMouseGrab); |
| 274 | par->setKeepTouchGrab(d->hadKeepTouchGrab); |
| 275 | } |
| 276 | break; |
| 277 | case QQuickEventPoint::OverrideGrabPassive: |
| 278 | // Passive grab is still there, but we won't receive point updates right now. |
| 279 | // No need to notify about this. |
| 280 | return; |
| 281 | } |
| 282 | if (wasCanceled) |
| 283 | emit canceled(point); |
| 284 | emit grabChanged(transition, point); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | /*! |
| 289 | Acquire or give up a passive grab of the given \a point, according to the \a grab state. |
| 290 | |
| 291 | Unlike the exclusive grab, multiple Input Handlers can have passive grabs |
| 292 | simultaneously. This means that each of them will receive further events |
| 293 | when the \a point moves, and when it is finally released. Typically an |
| 294 | Input Handler should acquire a passive grab as soon as a point is pressed, |
| 295 | if the handler's constraints do not clearly rule out any interest in that |
| 296 | point. For example, DragHandler needs a passive grab in order to watch the |
| 297 | movement of a point to see whether it will be dragged past the drag |
| 298 | threshold. When a handler is actively manipulating its \l target (that is, |
| 299 | when \l active is true), it may be able to do its work with only a passive |
| 300 | grab, or it may acquire an exclusive grab if the gesture clearly must not |
| 301 | be interpreted in another way by another handler. |
| 302 | */ |
| 303 | void QQuickPointerHandler::setPassiveGrab(QQuickEventPoint *point, bool grab) |
| 304 | { |
| 305 | qCDebug(lcPointerHandlerGrab) << point << grab; |
| 306 | if (grab) { |
| 307 | point->setGrabberPointerHandler(exclusiveGrabber: this, exclusive: false); |
| 308 | } else { |
| 309 | point->removePassiveGrabber(handler: this); |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | /*! |
| 314 | Check whether it's OK to take an exclusive grab of the \a point. |
| 315 | |
| 316 | The default implementation will call approveGrabTransition() to check this |
| 317 | handler's \l grabPermissions. If grabbing can be done only by taking over |
| 318 | the exclusive grab from an Item, approveGrabTransition() checks the Item's |
| 319 | \l keepMouseGrab or \l keepTouchGrab flags appropriately. If grabbing can |
| 320 | be done only by taking over another handler's exclusive grab, canGrab() |
| 321 | also calls approveGrabTransition() on the handler which is about to lose |
| 322 | its grab. Either one can deny the takeover. |
| 323 | */ |
| 324 | bool QQuickPointerHandler::canGrab(QQuickEventPoint *point) |
| 325 | { |
| 326 | QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler(); |
| 327 | return approveGrabTransition(point, proposedGrabber: this) && |
| 328 | (existingPhGrabber ? existingPhGrabber->approveGrabTransition(point, proposedGrabber: this) : true); |
| 329 | } |
| 330 | |
| 331 | /*! |
| 332 | Check this handler's rules to see if \l proposedGrabber will be allowed to take |
| 333 | the exclusive grab. This function may be called twice: once on the instance which |
| 334 | will take the grab, and once on the instance which would thereby lose its grab, |
| 335 | in case of a takeover scenario. |
| 336 | */ |
| 337 | bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObject *proposedGrabber) |
| 338 | { |
| 339 | Q_D(const QQuickPointerHandler); |
| 340 | bool allowed = false; |
| 341 | if (proposedGrabber == this) { |
| 342 | QObject* existingGrabber = point->exclusiveGrabber(); |
| 343 | allowed = (existingGrabber == nullptr) || ((d->grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything); |
| 344 | if (existingGrabber) { |
| 345 | if (QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler()) { |
| 346 | if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfDifferentType) && |
| 347 | existingPhGrabber->metaObject()->className() != metaObject()->className()) |
| 348 | allowed = true; |
| 349 | if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfSameType) && |
| 350 | existingPhGrabber->metaObject()->className() == metaObject()->className()) |
| 351 | allowed = true; |
| 352 | } else if ((d->grabPermissions & CanTakeOverFromItems)) { |
| 353 | allowed = true; |
| 354 | QQuickItem * existingItemGrabber = point->grabberItem(); |
| 355 | QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(c: parentItem()->window()); |
| 356 | const bool isMouse = point->pointerEvent()->asPointerMouseEvent(); |
| 357 | const bool isTouch = point->pointerEvent()->asPointerTouchEvent(); |
| 358 | if (existingItemGrabber && |
| 359 | ((existingItemGrabber->keepMouseGrab() && |
| 360 | (isMouse || winPriv->isDeliveringTouchAsMouse())) || |
| 361 | (existingItemGrabber->keepTouchGrab() && isTouch))) { |
| 362 | allowed = false; |
| 363 | // If the handler wants to steal the exclusive grab from an Item, the Item can usually veto |
| 364 | // by having its keepMouseGrab flag set. But an exception is if that Item is a parent that |
| 365 | // normally filters events (such as a Flickable): it needs to be possible for e.g. a |
| 366 | // DragHandler to operate on an Item inside a Flickable. Flickable is aggressive about |
| 367 | // grabbing on press (for fear of missing updates), but DragHandler uses a passive grab |
| 368 | // at first and then expects to be able to steal the grab later on. It cannot respect |
| 369 | // Flickable's wishes in that case, because then it would never have a chance. |
| 370 | if (existingItemGrabber->keepMouseGrab() && |
| 371 | existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(child: parentItem())) { |
| 372 | if (winPriv->isDeliveringTouchAsMouse() && point->pointId() == winPriv->touchMouseId) { |
| 373 | qCDebug(lcPointerHandlerGrab) << this << "steals touchpoint" << point->pointId() |
| 374 | << "despite parent touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber; |
| 375 | allowed = true; |
| 376 | } |
| 377 | } |
| 378 | if (!allowed) { |
| 379 | qCDebug(lcPointerHandlerGrab) << this << "wants to grab point" << point->pointId() |
| 380 | << "but declines to steal from grabber" << existingItemGrabber |
| 381 | << "with keepMouseGrab=" << existingItemGrabber->keepMouseGrab() |
| 382 | << "keepTouchGrab=" << existingItemGrabber->keepTouchGrab(); |
| 383 | } |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | } else { |
| 388 | // proposedGrabber is different: that means this instance will lose its grab |
| 389 | if (proposedGrabber) { |
| 390 | if ((d->grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything) |
| 391 | allowed = true; |
| 392 | if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) && |
| 393 | proposedGrabber->metaObject()->className() != metaObject()->className()) |
| 394 | allowed = true; |
| 395 | if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfSameType) && |
| 396 | proposedGrabber->metaObject()->className() == metaObject()->className()) |
| 397 | allowed = true; |
| 398 | if (!allowed && (d->grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits(classname: "QQuickItem" )) |
| 399 | allowed = true; |
| 400 | } else { |
| 401 | if (!allowed && (d->grabPermissions & ApprovesCancellation)) |
| 402 | allowed = true; |
| 403 | } |
| 404 | } |
| 405 | qCDebug(lcPointerHandlerGrab) << "point" << Qt::hex << point->pointId() << "permission" << |
| 406 | QMetaEnum::fromType<GrabPermissions>().valueToKeys(value: grabPermissions()) << |
| 407 | ':' << this << (allowed ? "approved to" : "denied to" ) << proposedGrabber; |
| 408 | return allowed; |
| 409 | } |
| 410 | |
| 411 | /*! |
| 412 | \qmlproperty flags QtQuick::PointerHandler::grabPermissions |
| 413 | |
| 414 | This property specifies the permissions when this handler's logic decides |
| 415 | to take over the exclusive grab, or when it is asked to approve grab |
| 416 | takeover or cancellation by another handler. |
| 417 | |
| 418 | \value PointerHandler.TakeOverForbidden |
| 419 | This handler neither takes from nor gives grab permission to any type of Item or Handler. |
| 420 | \value PointerHandler.CanTakeOverFromHandlersOfSameType |
| 421 | This handler can take the exclusive grab from another handler of the same class. |
| 422 | \value PointerHandler.CanTakeOverFromHandlersOfDifferentType |
| 423 | This handler can take the exclusive grab from any kind of handler. |
| 424 | \value PointerHandler.CanTakeOverFromAnything |
| 425 | This handler can take the exclusive grab from any type of Item or Handler. |
| 426 | \value PointerHandler.ApprovesTakeOverByHandlersOfSameType |
| 427 | This handler gives permission for another handler of the same class to take the grab. |
| 428 | \value PointerHandler.ApprovesTakeOverByHandlersOfDifferentType |
| 429 | This handler gives permission for any kind of handler to take the grab. |
| 430 | \value PointerHandler.ApprovesTakeOverByItems |
| 431 | This handler gives permission for any kind of Item to take the grab. |
| 432 | \value PointerHandler.ApprovesCancellation |
| 433 | This handler will allow its grab to be set to null. |
| 434 | \value PointerHandler.ApprovesTakeOverByAnything |
| 435 | This handler gives permission for any any type of Item or Handler to take the grab. |
| 436 | |
| 437 | The default is |
| 438 | \c {PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything} |
| 439 | which allows most takeover scenarios but avoids e.g. two PinchHandlers fighting |
| 440 | over the same touchpoints. |
| 441 | */ |
| 442 | QQuickPointerHandler::GrabPermissions QQuickPointerHandler::grabPermissions() const |
| 443 | { |
| 444 | Q_D(const QQuickPointerHandler); |
| 445 | return static_cast<QQuickPointerHandler::GrabPermissions>(d->grabPermissions); |
| 446 | } |
| 447 | |
| 448 | void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission) |
| 449 | { |
| 450 | Q_D(QQuickPointerHandler); |
| 451 | if (d->grabPermissions == grabPermission) |
| 452 | return; |
| 453 | |
| 454 | d->grabPermissions = grabPermission; |
| 455 | emit grabPermissionChanged(); |
| 456 | } |
| 457 | |
| 458 | void QQuickPointerHandler::classBegin() |
| 459 | { |
| 460 | } |
| 461 | |
| 462 | void QQuickPointerHandler::componentComplete() |
| 463 | { |
| 464 | Q_D(const QQuickPointerHandler); |
| 465 | if (d->cursorSet) { |
| 466 | if (auto *parent = parentItem()) { |
| 467 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item: parent); |
| 468 | itemPriv->hasCursorHandler = true; |
| 469 | itemPriv->setHasCursorInChild(true); |
| 470 | } |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | QQuickPointerEvent *QQuickPointerHandler::currentEvent() |
| 475 | { |
| 476 | Q_D(const QQuickPointerHandler); |
| 477 | return d->currentEvent; |
| 478 | } |
| 479 | |
| 480 | /*! |
| 481 | Acquire or give up the exclusive grab of the given \a point, according to |
| 482 | the \a grab state, and subject to the rules: canGrab(), and the rule not to |
| 483 | relinquish another handler's grab. Returns true if permission is granted, |
| 484 | or if the exclusive grab has already been acquired or relinquished as |
| 485 | specified. Returns false if permission is denied either by this handler or |
| 486 | by the handler or item from which this handler would take over |
| 487 | */ |
| 488 | bool QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab) |
| 489 | { |
| 490 | if ((grab && point->exclusiveGrabber() == this) || (!grab && point->exclusiveGrabber() != this)) |
| 491 | return true; |
| 492 | // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab |
| 493 | bool allowed = true; |
| 494 | if (grab) { |
| 495 | allowed = canGrab(point); |
| 496 | } else { |
| 497 | QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler(); |
| 498 | // Ask before allowing one handler to cancel another's grab |
| 499 | if (existingPhGrabber && existingPhGrabber != this && !existingPhGrabber->approveGrabTransition(point, proposedGrabber: nullptr)) |
| 500 | allowed = false; |
| 501 | } |
| 502 | qCDebug(lcPointerHandlerGrab) << point << (grab ? "grab" : "ungrab" ) << (allowed ? "allowed" : "forbidden" ) << |
| 503 | point->exclusiveGrabber() << "->" << (grab ? this : nullptr); |
| 504 | if (allowed) |
| 505 | point->setGrabberPointerHandler(exclusiveGrabber: grab ? this : nullptr, exclusive: true); |
| 506 | return allowed; |
| 507 | } |
| 508 | |
| 509 | /*! |
| 510 | Cancel any existing grab of the given \a point. |
| 511 | */ |
| 512 | void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point) |
| 513 | { |
| 514 | qCDebug(lcPointerHandlerGrab) << point; |
| 515 | point->cancelAllGrabs(handler: this); |
| 516 | } |
| 517 | |
| 518 | QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const |
| 519 | { |
| 520 | return (target() ? target()->mapFromScene(point: point->scenePosition()) : point->scenePosition()); |
| 521 | } |
| 522 | |
| 523 | bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const |
| 524 | { |
| 525 | if (!point) |
| 526 | return false; |
| 527 | if (QQuickItem *par = parentItem()) { |
| 528 | if (par->window()) { |
| 529 | QRect windowGeometry = par->window()->geometry(); |
| 530 | if (!par->window()->isTopLevel()) |
| 531 | windowGeometry = QRect(QWindowPrivate::get(window: par->window())->globalPosition(), par->window()->size()); |
| 532 | QPoint screenPosition = par->window()->mapToGlobal(pos: point->scenePosition().toPoint()); |
| 533 | if (!windowGeometry.contains(p: screenPosition)) |
| 534 | return false; |
| 535 | } |
| 536 | QPointF p = par->mapFromScene(point: point->scenePosition()); |
| 537 | qreal m = margin(); |
| 538 | if (m > 0) |
| 539 | return p.x() >= -m && p.y() >= -m && p.x() <= par->width() + m && p.y() <= par->height() + m; |
| 540 | return par->contains(point: p); |
| 541 | } |
| 542 | return false; |
| 543 | } |
| 544 | |
| 545 | /*! |
| 546 | \qmlproperty bool QtQuick::PointerHandler::enabled |
| 547 | |
| 548 | If a PointerHandler is disabled, it will reject all events |
| 549 | and no signals will be emitted. |
| 550 | */ |
| 551 | bool QQuickPointerHandler::enabled() const |
| 552 | { |
| 553 | Q_D(const QQuickPointerHandler); |
| 554 | return d->enabled; |
| 555 | } |
| 556 | |
| 557 | void QQuickPointerHandler::setEnabled(bool enabled) |
| 558 | { |
| 559 | Q_D(QQuickPointerHandler); |
| 560 | if (d->enabled == enabled) |
| 561 | return; |
| 562 | |
| 563 | d->enabled = enabled; |
| 564 | emit enabledChanged(); |
| 565 | } |
| 566 | |
| 567 | bool QQuickPointerHandler::active() const |
| 568 | { |
| 569 | Q_D(const QQuickPointerHandler); |
| 570 | return d->active; |
| 571 | } |
| 572 | |
| 573 | /*! |
| 574 | \qmlproperty Item QtQuick::PointerHandler::target |
| 575 | |
| 576 | The Item which this handler will manipulate. |
| 577 | |
| 578 | By default, it is the same as the \l [QML] {parent}, the Item within which |
| 579 | the handler is declared. However, it can sometimes be useful to set the |
| 580 | target to a different Item, in order to handle events within one item |
| 581 | but manipulate another; or to \c null, to disable the default behavior |
| 582 | and do something else instead. |
| 583 | */ |
| 584 | void QQuickPointerHandler::setTarget(QQuickItem *target) |
| 585 | { |
| 586 | Q_D(QQuickPointerHandler); |
| 587 | d->targetExplicitlySet = true; |
| 588 | if (d->target == target) |
| 589 | return; |
| 590 | |
| 591 | QQuickItem *oldTarget = d->target; |
| 592 | d->target = target; |
| 593 | onTargetChanged(oldTarget); |
| 594 | emit targetChanged(); |
| 595 | } |
| 596 | |
| 597 | QQuickItem *QQuickPointerHandler::parentItem() const |
| 598 | { |
| 599 | return static_cast<QQuickItem *>(QObject::parent()); |
| 600 | } |
| 601 | |
| 602 | QQuickItem *QQuickPointerHandler::target() const |
| 603 | { |
| 604 | Q_D(const QQuickPointerHandler); |
| 605 | if (!d->targetExplicitlySet) |
| 606 | return parentItem(); |
| 607 | return d->target; |
| 608 | } |
| 609 | |
| 610 | void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) |
| 611 | { |
| 612 | bool wants = wantsPointerEvent(event); |
| 613 | qCDebug(lcPointerHandlerDispatch) << metaObject()->className() << objectName() |
| 614 | << "on" << parentItem()->metaObject()->className() << parentItem()->objectName() |
| 615 | << (wants ? "WANTS" : "DECLINES" ) << event; |
| 616 | if (wants) { |
| 617 | handlePointerEventImpl(event); |
| 618 | } else { |
| 619 | setActive(false); |
| 620 | int pCount = event->pointCount(); |
| 621 | for (int i = 0; i < pCount; ++i) { |
| 622 | QQuickEventPoint *pt = event->point(i); |
| 623 | if (pt->grabberPointerHandler() == this && pt->state() != QQuickEventPoint::Stationary) |
| 624 | pt->cancelExclusiveGrab(); |
| 625 | } |
| 626 | } |
| 627 | event->device()->eventDeliveryTargets().append(t: this); |
| 628 | } |
| 629 | |
| 630 | bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) |
| 631 | { |
| 632 | Q_D(const QQuickPointerHandler); |
| 633 | Q_UNUSED(event) |
| 634 | return d->enabled; |
| 635 | } |
| 636 | |
| 637 | bool QQuickPointerHandler::wantsEventPoint(QQuickEventPoint *point) |
| 638 | { |
| 639 | bool ret = point->exclusiveGrabber() == this || point->passiveGrabbers().contains(t: this) || parentContains(point); |
| 640 | qCDebug(lcPointerHandlerDispatch) << Qt::hex << point->pointId() << "@" << point->scenePosition() |
| 641 | << metaObject()->className() << objectName() << ret; |
| 642 | return ret; |
| 643 | } |
| 644 | |
| 645 | /*! |
| 646 | \readonly |
| 647 | \qmlproperty bool QtQuick::PointerHandler::active |
| 648 | |
| 649 | This holds true whenever this Input Handler has taken sole responsibility |
| 650 | for handing one or more EventPoints, by successfully taking an exclusive |
| 651 | grab of those points. This means that it is keeping its properties |
| 652 | up-to-date according to the movements of those Event Points and actively |
| 653 | manipulating its \l target (if any). |
| 654 | */ |
| 655 | void QQuickPointerHandler::setActive(bool active) |
| 656 | { |
| 657 | Q_D(QQuickPointerHandler); |
| 658 | if (d->active != active) { |
| 659 | qCDebug(lcPointerHandlerActive) << this << d->active << "->" << active; |
| 660 | d->active = active; |
| 661 | onActiveChanged(); |
| 662 | emit activeChanged(); |
| 663 | } |
| 664 | } |
| 665 | |
| 666 | void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event) |
| 667 | { |
| 668 | Q_D(QQuickPointerHandler); |
| 669 | d->currentEvent = event; |
| 670 | } |
| 671 | |
| 672 | /*! |
| 673 | \readonly |
| 674 | \qmlproperty Item QtQuick::PointerHandler::parent |
| 675 | |
| 676 | The \l Item which is the scope of the handler; the Item in which it was declared. |
| 677 | The handler will handle events on behalf of this Item, which means a |
| 678 | pointer event is relevant if at least one of its event points occurs within |
| 679 | the Item's interior. Initially \l [QML] {target} {target()} is the same, but it |
| 680 | can be reassigned. |
| 681 | |
| 682 | \sa {target}, QObject::parent() |
| 683 | */ |
| 684 | |
| 685 | /*! |
| 686 | \qmlsignal QtQuick::PointerHandler::grabChanged(GrabTransition transition, EventPoint point) |
| 687 | |
| 688 | This signal is emitted when the grab has changed in some way which is |
| 689 | relevant to this handler. |
| 690 | |
| 691 | The \a transition (verb) tells what happened. |
| 692 | The \a point (object) is the point that was grabbed or ungrabbed. |
| 693 | */ |
| 694 | |
| 695 | /*! |
| 696 | \qmlsignal QtQuick::PointerHandler::canceled(EventPoint point) |
| 697 | |
| 698 | If this handler has already grabbed the given \a point, this signal is |
| 699 | emitted when the grab is stolen by a different Pointer Handler or Item. |
| 700 | */ |
| 701 | |
| 702 | QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate() |
| 703 | : grabPermissions(QQuickPointerHandler::CanTakeOverFromItems | |
| 704 | QQuickPointerHandler::CanTakeOverFromHandlersOfDifferentType | |
| 705 | QQuickPointerHandler::ApprovesTakeOverByAnything) |
| 706 | , cursorShape(Qt::ArrowCursor) |
| 707 | , enabled(true) |
| 708 | , active(false) |
| 709 | , targetExplicitlySet(false) |
| 710 | , hadKeepMouseGrab(false) |
| 711 | , hadKeepTouchGrab(false) |
| 712 | , cursorSet(false) |
| 713 | { |
| 714 | } |
| 715 | |
| 716 | template <typename TEventPoint> |
| 717 | bool QQuickPointerHandlerPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const TEventPoint *p) const |
| 718 | { |
| 719 | Q_Q(const QQuickPointerHandler); |
| 720 | QStyleHints *styleHints = qApp->styleHints(); |
| 721 | bool overThreshold = qAbs(t: d) > q->dragThreshold(); |
| 722 | const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0); |
| 723 | if (!overThreshold && dragVelocityLimitAvailable) { |
| 724 | qreal velocity = qreal(axis == Qt::XAxis ? p->velocity().x() : p->velocity().y()); |
| 725 | overThreshold |= qAbs(t: velocity) > styleHints->startDragVelocity(); |
| 726 | } |
| 727 | return overThreshold; |
| 728 | } |
| 729 | |
| 730 | bool QQuickPointerHandlerPrivate::dragOverThreshold(QVector2D delta) const |
| 731 | { |
| 732 | Q_Q(const QQuickPointerHandler); |
| 733 | const float threshold = q->dragThreshold(); |
| 734 | return qAbs(t: delta.x()) > threshold || qAbs(t: delta.y()) > threshold; |
| 735 | } |
| 736 | |
| 737 | bool QQuickPointerHandlerPrivate::dragOverThreshold(const QQuickEventPoint *point) const |
| 738 | { |
| 739 | QPointF delta = point->scenePosition() - point->scenePressPosition(); |
| 740 | return (dragOverThreshold(d: delta.x(), axis: Qt::XAxis, p: point) || |
| 741 | dragOverThreshold(d: delta.y(), axis: Qt::YAxis, p: point)); |
| 742 | } |
| 743 | |
| 744 | QT_END_NAMESPACE |
| 745 | |