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