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