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