1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2018 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQuick module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qquickpointerhandler_p.h" |
41 | #include "qquickpointerhandler_p_p.h" |
42 | #include <QtQuick/private/qquickitem_p.h> |
43 | |
44 | QT_BEGIN_NAMESPACE |
45 | |
46 | Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch" ) |
47 | Q_LOGGING_CATEGORY(lcPointerHandlerGrab, "qt.quick.handler.grab" ) |
48 | Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active" ) |
49 | |
50 | /*! |
51 | \qmltype PointerHandler |
52 | \qmlabstract |
53 | \since 5.10 |
54 | \instantiates QQuickPointerHandler |
55 | \inqmlmodule QtQuick |
56 | \brief Abstract handler for pointer events. |
57 | |
58 | PointerHandler is the base class Input Handler (not registered as a QML type) for |
59 | events from any kind of pointing device (touch, mouse or graphics tablet). |
60 | */ |
61 | |
62 | QQuickPointerHandler::QQuickPointerHandler(QQuickItem *parent) |
63 | : QObject(*(new QQuickPointerHandlerPrivate), parent) |
64 | { |
65 | } |
66 | |
67 | QQuickPointerHandler::QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QQuickItem *parent) |
68 | : QObject(dd, parent) |
69 | { |
70 | } |
71 | |
72 | QQuickPointerHandler::~QQuickPointerHandler() |
73 | { |
74 | QQuickItem *parItem = parentItem(); |
75 | if (parItem) { |
76 | QQuickItemPrivate *p = QQuickItemPrivate::get(item: parItem); |
77 | p->extra.value().pointerHandlers.removeOne(t: this); |
78 | } |
79 | } |
80 | |
81 | /*! |
82 | \qmlproperty real PointerHandler::margin |
83 | |
84 | The margin beyond the bounds of the \l {PointerHandler::parent}{parent} |
85 | item within which an event point can activate this handler. For example, on |
86 | a PinchHandler where the \l {PointerHandler::target}{target} is also the |
87 | \c parent, it's useful to set this to a distance at least half the width |
88 | of a typical user's finger, so that if the \c parent has been scaled down |
89 | to a very small size, the pinch gesture is still possible. Or, if a |
90 | TapHandler-based button is placed near the screen edge, it can be used |
91 | to comply with Fitts's Law: react to mouse clicks at the screen edge |
92 | even though the button is visually spaced away from the edge by a few pixels. |
93 | |
94 | The default value is 0. |
95 | |
96 | \image pointerHandlerMargin.png |
97 | */ |
98 | qreal QQuickPointerHandler::margin() const |
99 | { |
100 | Q_D(const QQuickPointerHandler); |
101 | return d->m_margin; |
102 | } |
103 | |
104 | void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold) |
105 | { |
106 | Q_D(QQuickPointerHandler); |
107 | if (d->m_margin == pointDistanceThreshold) |
108 | return; |
109 | |
110 | d->m_margin = pointDistanceThreshold; |
111 | emit marginChanged(); |
112 | } |
113 | |
114 | /*! |
115 | \qmlproperty int PointerHandler::dragThreshold |
116 | \since 5.15 |
117 | |
118 | The distance in pixels that the user must drag an event point in order to |
119 | have it treated as a drag gesture. |
120 | |
121 | The default value depends on the platform and screen resolution. |
122 | It can be reset back to the default value by setting it to undefined. |
123 | The behavior when a drag gesture begins varies in different handlers. |
124 | */ |
125 | int QQuickPointerHandler::dragThreshold() const |
126 | { |
127 | Q_D(const QQuickPointerHandler); |
128 | if (d->dragThreshold < 0) |
129 | return qApp->styleHints()->startDragDistance(); |
130 | return d->dragThreshold; |
131 | } |
132 | |
133 | void QQuickPointerHandler::setDragThreshold(int t) |
134 | { |
135 | Q_D(QQuickPointerHandler); |
136 | if (d->dragThreshold == t) |
137 | return; |
138 | |
139 | if (t > std::numeric_limits<qint16>::max()) |
140 | qWarning() << "drag threshold cannot exceed" << std::numeric_limits<qint16>::max(); |
141 | d->dragThreshold = qint16(t); |
142 | emit dragThresholdChanged(); |
143 | } |
144 | |
145 | void QQuickPointerHandler::resetDragThreshold() |
146 | { |
147 | Q_D(QQuickPointerHandler); |
148 | if (d->dragThreshold < 0) |
149 | return; |
150 | |
151 | d->dragThreshold = -1; |
152 | emit dragThresholdChanged(); |
153 | } |
154 | |
155 | /*! |
156 | \since 5.15 |
157 | \qmlproperty Qt::CursorShape PointerHandler::cursorShape |
158 | This property holds the cursor shape that will appear whenever the mouse is |
159 | hovering over the \l parentItem while \l active is \c true. |
160 | |
161 | The available cursor shapes are: |
162 | \list |
163 | \li Qt.ArrowCursor |
164 | \li Qt.UpArrowCursor |
165 | \li Qt.CrossCursor |
166 | \li Qt.WaitCursor |
167 | \li Qt.IBeamCursor |
168 | \li Qt.SizeVerCursor |
169 | \li Qt.SizeHorCursor |
170 | \li Qt.SizeBDiagCursor |
171 | \li Qt.SizeFDiagCursor |
172 | \li Qt.SizeAllCursor |
173 | \li Qt.BlankCursor |
174 | \li Qt.SplitVCursor |
175 | \li Qt.SplitHCursor |
176 | \li Qt.PointingHandCursor |
177 | \li Qt.ForbiddenCursor |
178 | \li Qt.WhatsThisCursor |
179 | \li Qt.BusyCursor |
180 | \li Qt.OpenHandCursor |
181 | \li Qt.ClosedHandCursor |
182 | \li Qt.DragCopyCursor |
183 | \li Qt.DragMoveCursor |
184 | \li Qt.DragLinkCursor |
185 | \endlist |
186 | |
187 | The default value is not set, which allows the \l {QQuickItem::cursor()}{cursor} |
188 | of \l parentItem to appear. This property can be reset to the same initial |
189 | condition by setting it to undefined. |
190 | |
191 | \note When this property has not been set, or has been set to \c undefined, |
192 | if you read the value it will return \c Qt.ArrowCursor. |
193 | |
194 | \sa Qt::CursorShape, QQuickItem::cursor(), HoverHandler::cursorShape |
195 | */ |
196 | #if QT_CONFIG(cursor) |
197 | Qt::CursorShape QQuickPointerHandler::cursorShape() const |
198 | { |
199 | Q_D(const QQuickPointerHandler); |
200 | return d->cursorShape; |
201 | } |
202 | |
203 | void QQuickPointerHandler::setCursorShape(Qt::CursorShape shape) |
204 | { |
205 | Q_D(QQuickPointerHandler); |
206 | if (d->cursorSet && shape == d->cursorShape) |
207 | return; |
208 | d->cursorShape = shape; |
209 | d->cursorSet = true; |
210 | if (auto *par = qmlobject_cast<QQuickItem *>(object: parent())) { |
211 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item: par); |
212 | itemPriv->hasCursorHandler = true; |
213 | itemPriv->setHasCursorInChild(true); |
214 | } |
215 | emit cursorShapeChanged(); |
216 | } |
217 | |
218 | void QQuickPointerHandler::resetCursorShape() |
219 | { |
220 | Q_D(QQuickPointerHandler); |
221 | if (!d->cursorSet) |
222 | return; |
223 | d->cursorShape = Qt::ArrowCursor; |
224 | d->cursorSet = false; |
225 | if (auto *parent = parentItem()) { |
226 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item: parent); |
227 | itemPriv->hasCursorHandler = false; |
228 | itemPriv->setHasCursorInChild(itemPriv->hasCursor); |
229 | } |
230 | emit cursorShapeChanged(); |
231 | } |
232 | |
233 | bool QQuickPointerHandler::isCursorShapeExplicitlySet() const |
234 | { |
235 | Q_D(const QQuickPointerHandler); |
236 | return d->cursorSet; |
237 | } |
238 | #endif |
239 | |
240 | /*! |
241 | Notification that the grab has changed in some way which is relevant to this handler. |
242 | The \a grabber (subject) will be the Input Handler whose state is changing, |
243 | or null if the state change regards an Item. |
244 | The \a transition (verb) tells what happened. |
245 | The \a point (object) is the point that was grabbed or ungrabbed. |
246 | EventPoint has the sole responsibility to call this function. |
247 | The Input Handler must react in whatever way is appropriate, and must |
248 | emit the relevant signals (for the benefit of QML code). |
249 | A subclass is allowed to override this virtual function, but must always |
250 | call its parent class's implementation in addition to (usually after) |
251 | whatever custom behavior it implements. |
252 | */ |
253 | void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) |
254 | { |
255 | qCDebug(lcPointerHandlerGrab) << point << transition << grabber; |
256 | Q_ASSERT(point); |
257 | if (grabber == this) { |
258 | bool wasCanceled = false; |
259 | switch (transition) { |
260 | case QQuickEventPoint::GrabPassive: |
261 | case QQuickEventPoint::GrabExclusive: |
262 | break; |
263 | case QQuickEventPoint::CancelGrabPassive: |
264 | case QQuickEventPoint::CancelGrabExclusive: |
265 | wasCanceled = true; // the grab was stolen by something else |
266 | Q_FALLTHROUGH(); |
267 | case QQuickEventPoint::UngrabPassive: |
268 | case QQuickEventPoint::UngrabExclusive: |
269 | setActive(false); |
270 | point->setAccepted(false); |
271 | if (auto par = parentItem()) { |
272 | Q_D(const QQuickPointerHandler); |
273 | par->setKeepMouseGrab(d->hadKeepMouseGrab); |
274 | par->setKeepTouchGrab(d->hadKeepTouchGrab); |
275 | } |
276 | break; |
277 | case QQuickEventPoint::OverrideGrabPassive: |
278 | // Passive grab is still there, but we won't receive point updates right now. |
279 | // No need to notify about this. |
280 | return; |
281 | } |
282 | if (wasCanceled) |
283 | emit canceled(point); |
284 | emit grabChanged(transition, point); |
285 | } |
286 | } |
287 | |
288 | /*! |
289 | Acquire or give up a passive grab of the given \a point, according to the \a grab state. |
290 | |
291 | Unlike the exclusive grab, multiple Input Handlers can have passive grabs |
292 | simultaneously. This means that each of them will receive further events |
293 | when the \a point moves, and when it is finally released. Typically an |
294 | Input Handler should acquire a passive grab as soon as a point is pressed, |
295 | if the handler's constraints do not clearly rule out any interest in that |
296 | point. For example, DragHandler needs a passive grab in order to watch the |
297 | movement of a point to see whether it will be dragged past the drag |
298 | threshold. When a handler is actively manipulating its \l target (that is, |
299 | when \l active is true), it may be able to do its work with only a passive |
300 | grab, or it may acquire an exclusive grab if the gesture clearly must not |
301 | be interpreted in another way by another handler. |
302 | */ |
303 | void QQuickPointerHandler::setPassiveGrab(QQuickEventPoint *point, bool grab) |
304 | { |
305 | qCDebug(lcPointerHandlerGrab) << point << grab; |
306 | if (grab) { |
307 | point->setGrabberPointerHandler(exclusiveGrabber: this, exclusive: false); |
308 | } else { |
309 | point->removePassiveGrabber(handler: this); |
310 | } |
311 | } |
312 | |
313 | /*! |
314 | Check whether it's OK to take an exclusive grab of the \a point. |
315 | |
316 | The default implementation will call approveGrabTransition() to check this |
317 | handler's \l grabPermissions. If grabbing can be done only by taking over |
318 | the exclusive grab from an Item, approveGrabTransition() checks the Item's |
319 | \l keepMouseGrab or \l keepTouchGrab flags appropriately. If grabbing can |
320 | be done only by taking over another handler's exclusive grab, canGrab() |
321 | also calls approveGrabTransition() on the handler which is about to lose |
322 | its grab. Either one can deny the takeover. |
323 | */ |
324 | bool QQuickPointerHandler::canGrab(QQuickEventPoint *point) |
325 | { |
326 | QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler(); |
327 | return approveGrabTransition(point, proposedGrabber: this) && |
328 | (existingPhGrabber ? existingPhGrabber->approveGrabTransition(point, proposedGrabber: this) : true); |
329 | } |
330 | |
331 | /*! |
332 | Check this handler's rules to see if \l proposedGrabber will be allowed to take |
333 | the exclusive grab. This function may be called twice: once on the instance which |
334 | will take the grab, and once on the instance which would thereby lose its grab, |
335 | in case of a takeover scenario. |
336 | */ |
337 | bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObject *proposedGrabber) |
338 | { |
339 | Q_D(const QQuickPointerHandler); |
340 | bool allowed = false; |
341 | if (proposedGrabber == this) { |
342 | QObject* existingGrabber = point->exclusiveGrabber(); |
343 | allowed = (existingGrabber == nullptr) || ((d->grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything); |
344 | if (existingGrabber) { |
345 | if (QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler()) { |
346 | if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfDifferentType) && |
347 | existingPhGrabber->metaObject()->className() != metaObject()->className()) |
348 | allowed = true; |
349 | if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfSameType) && |
350 | existingPhGrabber->metaObject()->className() == metaObject()->className()) |
351 | allowed = true; |
352 | } else if ((d->grabPermissions & CanTakeOverFromItems)) { |
353 | allowed = true; |
354 | QQuickItem * existingItemGrabber = point->grabberItem(); |
355 | QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(c: parentItem()->window()); |
356 | const bool isMouse = point->pointerEvent()->asPointerMouseEvent(); |
357 | const bool isTouch = point->pointerEvent()->asPointerTouchEvent(); |
358 | if (existingItemGrabber && |
359 | ((existingItemGrabber->keepMouseGrab() && |
360 | (isMouse || winPriv->isDeliveringTouchAsMouse())) || |
361 | (existingItemGrabber->keepTouchGrab() && isTouch))) { |
362 | allowed = false; |
363 | // If the handler wants to steal the exclusive grab from an Item, the Item can usually veto |
364 | // by having its keepMouseGrab flag set. But an exception is if that Item is a parent that |
365 | // normally filters events (such as a Flickable): it needs to be possible for e.g. a |
366 | // DragHandler to operate on an Item inside a Flickable. Flickable is aggressive about |
367 | // grabbing on press (for fear of missing updates), but DragHandler uses a passive grab |
368 | // at first and then expects to be able to steal the grab later on. It cannot respect |
369 | // Flickable's wishes in that case, because then it would never have a chance. |
370 | if (existingItemGrabber->keepMouseGrab() && |
371 | existingItemGrabber->filtersChildMouseEvents() && existingItemGrabber->isAncestorOf(child: parentItem())) { |
372 | if (winPriv->isDeliveringTouchAsMouse() && point->pointId() == winPriv->touchMouseId) { |
373 | qCDebug(lcPointerHandlerGrab) << this << "steals touchpoint" << point->pointId() |
374 | << "despite parent touch-mouse grabber with keepMouseGrab=true" << existingItemGrabber; |
375 | allowed = true; |
376 | } |
377 | } |
378 | if (!allowed) { |
379 | qCDebug(lcPointerHandlerGrab) << this << "wants to grab point" << point->pointId() |
380 | << "but declines to steal from grabber" << existingItemGrabber |
381 | << "with keepMouseGrab=" << existingItemGrabber->keepMouseGrab() |
382 | << "keepTouchGrab=" << existingItemGrabber->keepTouchGrab(); |
383 | } |
384 | } |
385 | } |
386 | } |
387 | } else { |
388 | // proposedGrabber is different: that means this instance will lose its grab |
389 | if (proposedGrabber) { |
390 | if ((d->grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything) |
391 | allowed = true; |
392 | if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) && |
393 | proposedGrabber->metaObject()->className() != metaObject()->className()) |
394 | allowed = true; |
395 | if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfSameType) && |
396 | proposedGrabber->metaObject()->className() == metaObject()->className()) |
397 | allowed = true; |
398 | if (!allowed && (d->grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits(classname: "QQuickItem" )) |
399 | allowed = true; |
400 | } else { |
401 | if (!allowed && (d->grabPermissions & ApprovesCancellation)) |
402 | allowed = true; |
403 | } |
404 | } |
405 | qCDebug(lcPointerHandlerGrab) << "point" << Qt::hex << point->pointId() << "permission" << |
406 | QMetaEnum::fromType<GrabPermissions>().valueToKeys(value: grabPermissions()) << |
407 | ':' << this << (allowed ? "approved to" : "denied to" ) << proposedGrabber; |
408 | return allowed; |
409 | } |
410 | |
411 | /*! |
412 | \qmlproperty flags QtQuick::PointerHandler::grabPermissions |
413 | |
414 | This property specifies the permissions when this handler's logic decides |
415 | to take over the exclusive grab, or when it is asked to approve grab |
416 | takeover or cancellation by another handler. |
417 | |
418 | \value PointerHandler.TakeOverForbidden |
419 | This handler neither takes from nor gives grab permission to any type of Item or Handler. |
420 | \value PointerHandler.CanTakeOverFromHandlersOfSameType |
421 | This handler can take the exclusive grab from another handler of the same class. |
422 | \value PointerHandler.CanTakeOverFromHandlersOfDifferentType |
423 | This handler can take the exclusive grab from any kind of handler. |
424 | \value PointerHandler.CanTakeOverFromAnything |
425 | This handler can take the exclusive grab from any type of Item or Handler. |
426 | \value PointerHandler.ApprovesTakeOverByHandlersOfSameType |
427 | This handler gives permission for another handler of the same class to take the grab. |
428 | \value PointerHandler.ApprovesTakeOverByHandlersOfDifferentType |
429 | This handler gives permission for any kind of handler to take the grab. |
430 | \value PointerHandler.ApprovesTakeOverByItems |
431 | This handler gives permission for any kind of Item to take the grab. |
432 | \value PointerHandler.ApprovesCancellation |
433 | This handler will allow its grab to be set to null. |
434 | \value PointerHandler.ApprovesTakeOverByAnything |
435 | This handler gives permission for any any type of Item or Handler to take the grab. |
436 | |
437 | The default is |
438 | \c {PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything} |
439 | which allows most takeover scenarios but avoids e.g. two PinchHandlers fighting |
440 | over the same touchpoints. |
441 | */ |
442 | QQuickPointerHandler::GrabPermissions QQuickPointerHandler::grabPermissions() const |
443 | { |
444 | Q_D(const QQuickPointerHandler); |
445 | return static_cast<QQuickPointerHandler::GrabPermissions>(d->grabPermissions); |
446 | } |
447 | |
448 | void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission) |
449 | { |
450 | Q_D(QQuickPointerHandler); |
451 | if (d->grabPermissions == grabPermission) |
452 | return; |
453 | |
454 | d->grabPermissions = grabPermission; |
455 | emit grabPermissionChanged(); |
456 | } |
457 | |
458 | void QQuickPointerHandler::classBegin() |
459 | { |
460 | } |
461 | |
462 | void QQuickPointerHandler::componentComplete() |
463 | { |
464 | Q_D(const QQuickPointerHandler); |
465 | if (d->cursorSet) { |
466 | if (auto *parent = parentItem()) { |
467 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item: parent); |
468 | itemPriv->hasCursorHandler = true; |
469 | itemPriv->setHasCursorInChild(true); |
470 | } |
471 | } |
472 | } |
473 | |
474 | QQuickPointerEvent *QQuickPointerHandler::currentEvent() |
475 | { |
476 | Q_D(const QQuickPointerHandler); |
477 | return d->currentEvent; |
478 | } |
479 | |
480 | /*! |
481 | Acquire or give up the exclusive grab of the given \a point, according to |
482 | the \a grab state, and subject to the rules: canGrab(), and the rule not to |
483 | relinquish another handler's grab. Returns true if permission is granted, |
484 | or if the exclusive grab has already been acquired or relinquished as |
485 | specified. Returns false if permission is denied either by this handler or |
486 | by the handler or item from which this handler would take over |
487 | */ |
488 | bool QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab) |
489 | { |
490 | if ((grab && point->exclusiveGrabber() == this) || (!grab && point->exclusiveGrabber() != this)) |
491 | return true; |
492 | // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab |
493 | bool allowed = true; |
494 | if (grab) { |
495 | allowed = canGrab(point); |
496 | } else { |
497 | QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler(); |
498 | // Ask before allowing one handler to cancel another's grab |
499 | if (existingPhGrabber && existingPhGrabber != this && !existingPhGrabber->approveGrabTransition(point, proposedGrabber: nullptr)) |
500 | allowed = false; |
501 | } |
502 | qCDebug(lcPointerHandlerGrab) << point << (grab ? "grab" : "ungrab" ) << (allowed ? "allowed" : "forbidden" ) << |
503 | point->exclusiveGrabber() << "->" << (grab ? this : nullptr); |
504 | if (allowed) |
505 | point->setGrabberPointerHandler(exclusiveGrabber: grab ? this : nullptr, exclusive: true); |
506 | return allowed; |
507 | } |
508 | |
509 | /*! |
510 | Cancel any existing grab of the given \a point. |
511 | */ |
512 | void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point) |
513 | { |
514 | qCDebug(lcPointerHandlerGrab) << point; |
515 | point->cancelAllGrabs(handler: this); |
516 | } |
517 | |
518 | QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const |
519 | { |
520 | return (target() ? target()->mapFromScene(point: point->scenePosition()) : point->scenePosition()); |
521 | } |
522 | |
523 | bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const |
524 | { |
525 | if (!point) |
526 | return false; |
527 | if (QQuickItem *par = parentItem()) { |
528 | if (par->window()) { |
529 | QRect windowGeometry = par->window()->geometry(); |
530 | if (!par->window()->isTopLevel()) |
531 | windowGeometry = QRect(QWindowPrivate::get(window: par->window())->globalPosition(), par->window()->size()); |
532 | QPoint screenPosition = par->window()->mapToGlobal(pos: point->scenePosition().toPoint()); |
533 | if (!windowGeometry.contains(p: screenPosition)) |
534 | return false; |
535 | } |
536 | QPointF p = par->mapFromScene(point: point->scenePosition()); |
537 | qreal m = margin(); |
538 | if (m > 0) |
539 | return p.x() >= -m && p.y() >= -m && p.x() <= par->width() + m && p.y() <= par->height() + m; |
540 | return par->contains(point: p); |
541 | } |
542 | return false; |
543 | } |
544 | |
545 | /*! |
546 | \qmlproperty bool QtQuick::PointerHandler::enabled |
547 | |
548 | If a PointerHandler is disabled, it will reject all events |
549 | and no signals will be emitted. |
550 | */ |
551 | bool QQuickPointerHandler::enabled() const |
552 | { |
553 | Q_D(const QQuickPointerHandler); |
554 | return d->enabled; |
555 | } |
556 | |
557 | void QQuickPointerHandler::setEnabled(bool enabled) |
558 | { |
559 | Q_D(QQuickPointerHandler); |
560 | if (d->enabled == enabled) |
561 | return; |
562 | |
563 | d->enabled = enabled; |
564 | emit enabledChanged(); |
565 | } |
566 | |
567 | bool QQuickPointerHandler::active() const |
568 | { |
569 | Q_D(const QQuickPointerHandler); |
570 | return d->active; |
571 | } |
572 | |
573 | /*! |
574 | \qmlproperty Item QtQuick::PointerHandler::target |
575 | |
576 | The Item which this handler will manipulate. |
577 | |
578 | By default, it is the same as the \l [QML] {parent}, the Item within which |
579 | the handler is declared. However, it can sometimes be useful to set the |
580 | target to a different Item, in order to handle events within one item |
581 | but manipulate another; or to \c null, to disable the default behavior |
582 | and do something else instead. |
583 | */ |
584 | void QQuickPointerHandler::setTarget(QQuickItem *target) |
585 | { |
586 | Q_D(QQuickPointerHandler); |
587 | d->targetExplicitlySet = true; |
588 | if (d->target == target) |
589 | return; |
590 | |
591 | QQuickItem *oldTarget = d->target; |
592 | d->target = target; |
593 | onTargetChanged(oldTarget); |
594 | emit targetChanged(); |
595 | } |
596 | |
597 | QQuickItem *QQuickPointerHandler::parentItem() const |
598 | { |
599 | return static_cast<QQuickItem *>(QObject::parent()); |
600 | } |
601 | |
602 | QQuickItem *QQuickPointerHandler::target() const |
603 | { |
604 | Q_D(const QQuickPointerHandler); |
605 | if (!d->targetExplicitlySet) |
606 | return parentItem(); |
607 | return d->target; |
608 | } |
609 | |
610 | void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) |
611 | { |
612 | bool wants = wantsPointerEvent(event); |
613 | qCDebug(lcPointerHandlerDispatch) << metaObject()->className() << objectName() |
614 | << "on" << parentItem()->metaObject()->className() << parentItem()->objectName() |
615 | << (wants ? "WANTS" : "DECLINES" ) << event; |
616 | if (wants) { |
617 | handlePointerEventImpl(event); |
618 | } else { |
619 | setActive(false); |
620 | int pCount = event->pointCount(); |
621 | for (int i = 0; i < pCount; ++i) { |
622 | QQuickEventPoint *pt = event->point(i); |
623 | if (pt->grabberPointerHandler() == this && pt->state() != QQuickEventPoint::Stationary) |
624 | pt->cancelExclusiveGrab(); |
625 | } |
626 | } |
627 | event->device()->eventDeliveryTargets().append(t: this); |
628 | } |
629 | |
630 | bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) |
631 | { |
632 | Q_D(const QQuickPointerHandler); |
633 | Q_UNUSED(event) |
634 | return d->enabled; |
635 | } |
636 | |
637 | bool QQuickPointerHandler::wantsEventPoint(QQuickEventPoint *point) |
638 | { |
639 | bool ret = point->exclusiveGrabber() == this || point->passiveGrabbers().contains(t: this) || parentContains(point); |
640 | qCDebug(lcPointerHandlerDispatch) << Qt::hex << point->pointId() << "@" << point->scenePosition() |
641 | << metaObject()->className() << objectName() << ret; |
642 | return ret; |
643 | } |
644 | |
645 | /*! |
646 | \readonly |
647 | \qmlproperty bool QtQuick::PointerHandler::active |
648 | |
649 | This holds true whenever this Input Handler has taken sole responsibility |
650 | for handing one or more EventPoints, by successfully taking an exclusive |
651 | grab of those points. This means that it is keeping its properties |
652 | up-to-date according to the movements of those Event Points and actively |
653 | manipulating its \l target (if any). |
654 | */ |
655 | void QQuickPointerHandler::setActive(bool active) |
656 | { |
657 | Q_D(QQuickPointerHandler); |
658 | if (d->active != active) { |
659 | qCDebug(lcPointerHandlerActive) << this << d->active << "->" << active; |
660 | d->active = active; |
661 | onActiveChanged(); |
662 | emit activeChanged(); |
663 | } |
664 | } |
665 | |
666 | void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event) |
667 | { |
668 | Q_D(QQuickPointerHandler); |
669 | d->currentEvent = event; |
670 | } |
671 | |
672 | /*! |
673 | \readonly |
674 | \qmlproperty Item QtQuick::PointerHandler::parent |
675 | |
676 | The \l Item which is the scope of the handler; the Item in which it was declared. |
677 | The handler will handle events on behalf of this Item, which means a |
678 | pointer event is relevant if at least one of its event points occurs within |
679 | the Item's interior. Initially \l [QML] {target} {target()} is the same, but it |
680 | can be reassigned. |
681 | |
682 | \sa {target}, QObject::parent() |
683 | */ |
684 | |
685 | /*! |
686 | \qmlsignal QtQuick::PointerHandler::grabChanged(GrabTransition transition, EventPoint point) |
687 | |
688 | This signal is emitted when the grab has changed in some way which is |
689 | relevant to this handler. |
690 | |
691 | The \a transition (verb) tells what happened. |
692 | The \a point (object) is the point that was grabbed or ungrabbed. |
693 | */ |
694 | |
695 | /*! |
696 | \qmlsignal QtQuick::PointerHandler::canceled(EventPoint point) |
697 | |
698 | If this handler has already grabbed the given \a point, this signal is |
699 | emitted when the grab is stolen by a different Pointer Handler or Item. |
700 | */ |
701 | |
702 | QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate() |
703 | : grabPermissions(QQuickPointerHandler::CanTakeOverFromItems | |
704 | QQuickPointerHandler::CanTakeOverFromHandlersOfDifferentType | |
705 | QQuickPointerHandler::ApprovesTakeOverByAnything) |
706 | , cursorShape(Qt::ArrowCursor) |
707 | , enabled(true) |
708 | , active(false) |
709 | , targetExplicitlySet(false) |
710 | , hadKeepMouseGrab(false) |
711 | , hadKeepTouchGrab(false) |
712 | , cursorSet(false) |
713 | { |
714 | } |
715 | |
716 | template <typename TEventPoint> |
717 | bool QQuickPointerHandlerPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const TEventPoint *p) const |
718 | { |
719 | Q_Q(const QQuickPointerHandler); |
720 | QStyleHints *styleHints = qApp->styleHints(); |
721 | bool overThreshold = qAbs(t: d) > q->dragThreshold(); |
722 | const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0); |
723 | if (!overThreshold && dragVelocityLimitAvailable) { |
724 | qreal velocity = qreal(axis == Qt::XAxis ? p->velocity().x() : p->velocity().y()); |
725 | overThreshold |= qAbs(t: velocity) > styleHints->startDragVelocity(); |
726 | } |
727 | return overThreshold; |
728 | } |
729 | |
730 | bool QQuickPointerHandlerPrivate::dragOverThreshold(QVector2D delta) const |
731 | { |
732 | Q_Q(const QQuickPointerHandler); |
733 | const float threshold = q->dragThreshold(); |
734 | return qAbs(t: delta.x()) > threshold || qAbs(t: delta.y()) > threshold; |
735 | } |
736 | |
737 | bool QQuickPointerHandlerPrivate::dragOverThreshold(const QQuickEventPoint *point) const |
738 | { |
739 | QPointF delta = point->scenePosition() - point->scenePressPosition(); |
740 | return (dragOverThreshold(d: delta.x(), axis: Qt::XAxis, p: point) || |
741 | dragOverThreshold(d: delta.y(), axis: Qt::YAxis, p: point)); |
742 | } |
743 | |
744 | QT_END_NAMESPACE |
745 | |