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