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