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

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