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 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch") |
16 | Q_LOGGING_CATEGORY(lcPointerHandlerGrab, "qt.quick.handler.grab") |
17 | Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active") |
18 | Q_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 | */ |
45 | QQuickPointerHandler::QQuickPointerHandler(QQuickItem *parent) |
46 | : QQuickPointerHandler(*(new QQuickPointerHandlerPrivate), parent) |
47 | { |
48 | } |
49 | |
50 | QQuickPointerHandler::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 | |
62 | QQuickPointerHandler::~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 | */ |
88 | qreal QQuickPointerHandler::margin() const |
89 | { |
90 | Q_D(const QQuickPointerHandler); |
91 | return d->m_margin; |
92 | } |
93 | |
94 | void 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 | */ |
115 | int 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 | |
123 | void 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 | |
135 | void 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) |
187 | Qt::CursorShape QQuickPointerHandler::cursorShape() const |
188 | { |
189 | Q_D(const QQuickPointerHandler); |
190 | return d->cursorShape; |
191 | } |
192 | |
193 | void 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 | |
210 | void 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 | |
225 | bool 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 | */ |
245 | void 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 | */ |
296 | void 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 | */ |
318 | bool 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 | */ |
331 | bool 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 | */ |
441 | QQuickPointerHandler::GrabPermissions QQuickPointerHandler::grabPermissions() const |
442 | { |
443 | Q_D(const QQuickPointerHandler); |
444 | return static_cast<QQuickPointerHandler::GrabPermissions>(d->grabPermissions); |
445 | } |
446 | |
447 | void 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 | */ |
460 | void 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 | */ |
468 | void 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 | */ |
486 | QPointerEvent *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 | */ |
500 | bool 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 | */ |
524 | void 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 | |
535 | QPointF 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 | */ |
546 | bool 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 | */ |
559 | bool 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 | */ |
589 | bool QQuickPointerHandler::enabled() const |
590 | { |
591 | Q_D(const QQuickPointerHandler); |
592 | return d->enabled; |
593 | } |
594 | |
595 | void 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 | */ |
618 | QQuickItem *QQuickPointerHandler::target() const |
619 | { |
620 | Q_D(const QQuickPointerHandler); |
621 | if (!d->targetExplicitlySet) |
622 | return parentItem(); |
623 | return d->target; |
624 | } |
625 | |
626 | void 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 | */ |
660 | QQuickItem *QQuickPointerHandler::parentItem() const |
661 | { |
662 | return qmlobject_cast<QQuickItem *>(object: QObject::parent()); |
663 | } |
664 | |
665 | void 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 | */ |
688 | bool 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 | */ |
709 | void 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 | */ |
757 | bool 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 | */ |
785 | bool 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 | */ |
805 | bool QQuickPointerHandler::active() const |
806 | { |
807 | Q_D(const QQuickPointerHandler); |
808 | return d->active; |
809 | } |
810 | |
811 | void 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 | */ |
834 | void 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 | |
871 | QQuickPointerHandlerPrivate::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 | */ |
893 | template <typename TEventPoint> |
894 | bool 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 | */ |
913 | bool 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 | */ |
927 | bool 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 | |
934 | QVector<QObject *> &QQuickPointerHandlerPrivate::deviceDeliveryTargets(const QInputDevice *device) |
935 | { |
936 | return QQuickDeliveryAgentPrivate::deviceExtra(device)->deliveryTargets; |
937 | } |
938 | |
939 | QT_END_NAMESPACE |
940 | |
941 | #include "moc_qquickpointerhandler_p.cpp" |
942 |
Definitions
- lcPointerHandlerDispatch
- lcPointerHandlerGrab
- lcPointerHandlerActive
- QQuickPointerHandler
- QQuickPointerHandler
- ~QQuickPointerHandler
- margin
- setMargin
- dragThreshold
- setDragThreshold
- resetDragThreshold
- cursorShape
- setCursorShape
- resetCursorShape
- isCursorShapeExplicitlySet
- onGrabChanged
- setPassiveGrab
- canGrab
- approveGrabTransition
- grabPermissions
- setGrabPermissions
- classBegin
- componentComplete
- currentEvent
- setExclusiveGrab
- cancelAllGrabs
- eventPos
- parentContains
- parentContains
- enabled
- setEnabled
- target
- setTarget
- parentItem
- setParentItem
- event
- handlePointerEvent
- wantsPointerEvent
- wantsEventPoint
- active
- setActive
- handlePointerEventImpl
- QQuickPointerHandlerPrivate
- dragOverThreshold
- dragOverThreshold
- dragOverThreshold
Learn Advanced QML with KDAB
Find out more