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

source code of qtdeclarative/src/quick/handlers/qquickpointerhandler.cpp