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