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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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