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
44QT_BEGIN_NAMESPACE
45
46Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch")
47Q_LOGGING_CATEGORY(lcPointerHandlerGrab, "qt.quick.handler.grab")
48Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active")
49
50/*!
51 \qmltype PointerHandler
52 \qmlabstract
53 \since 5.10
54 \preliminary
55 \instantiates QQuickPointerHandler
56 \inqmlmodule QtQuick
57 \brief Abstract handler for pointer events.
58
59 PointerHandler is the base class Input Handler (not registered as a QML type) for
60 events from any kind of pointing device (touch, mouse or graphics tablet).
61*/
62
63QQuickPointerHandler::QQuickPointerHandler(QQuickItem *parent)
64 : QObject(*(new QQuickPointerHandlerPrivate), parent)
65{
66}
67
68QQuickPointerHandler::QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QQuickItem *parent)
69 : QObject(dd, parent)
70{
71}
72
73QQuickPointerHandler::~QQuickPointerHandler()
74{
75 QQuickItem *parItem = parentItem();
76 if (parItem) {
77 QQuickItemPrivate *p = QQuickItemPrivate::get(parItem);
78 p->extra.value().pointerHandlers.removeOne(this);
79 }
80}
81
82/*!
83 \qmlproperty real PointerHandler::margin
84
85 The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
86 item within which an event point can activate this handler. For example, on
87 a PinchHandler where the \l {PointerHandler::target}{target} is also the
88 \c parent, it's useful to set this to a distance at least half the width
89 of a typical user's finger, so that if the \c parent has been scaled down
90 to a very small size, the pinch gesture is still possible. Or, if a
91 TapHandler-based button is placed near the screen edge, it can be used
92 to comply with Fitts's Law: react to mouse clicks at the screen edge
93 even though the button is visually spaced away from the edge by a few pixels.
94
95 The default value is 0.
96
97 \image pointerHandlerMargin.png
98*/
99qreal QQuickPointerHandler::margin() const
100{
101 Q_D(const QQuickPointerHandler);
102 return d->m_margin;
103}
104
105void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
106{
107 Q_D(QQuickPointerHandler);
108 if (d->m_margin == pointDistanceThreshold)
109 return;
110
111 d->m_margin = pointDistanceThreshold;
112 emit marginChanged();
113}
114
115/*!
116 Notification that the grab has changed in some way which is relevant to this handler.
117 The \a grabber (subject) will be the Input Handler whose state is changing,
118 or null if the state change regards an Item.
119 The \a transition (verb) tells what happened.
120 The \a point (object) is the point that was grabbed or ungrabbed.
121 EventPoint has the sole responsibility to call this function.
122 The Input Handler must react in whatever way is appropriate, and must
123 emit the relevant signals (for the benefit of QML code).
124 A subclass is allowed to override this virtual function, but must always
125 call its parent class's implementation in addition to (usually after)
126 whatever custom behavior it implements.
127*/
128void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
129{
130 qCDebug(lcPointerHandlerGrab) << point << transition << grabber;
131 Q_ASSERT(point);
132 if (grabber == this) {
133 bool wasCanceled = false;
134 switch (transition) {
135 case QQuickEventPoint::GrabPassive:
136 case QQuickEventPoint::GrabExclusive:
137 break;
138 case QQuickEventPoint::CancelGrabPassive:
139 case QQuickEventPoint::CancelGrabExclusive:
140 wasCanceled = true; // the grab was stolen by something else
141 Q_FALLTHROUGH();
142 case QQuickEventPoint::UngrabPassive:
143 case QQuickEventPoint::UngrabExclusive:
144 setActive(false);
145 point->setAccepted(false);
146 if (auto par = parentItem()) {
147 Q_D(const QQuickPointerHandler);
148 par->setKeepMouseGrab(d->hadKeepMouseGrab);
149 par->setKeepTouchGrab(d->hadKeepTouchGrab);
150 }
151 break;
152 case QQuickEventPoint::OverrideGrabPassive:
153 // Passive grab is still there, but we won't receive point updates right now.
154 // No need to notify about this.
155 return;
156 }
157 if (wasCanceled)
158 emit canceled(point);
159 emit grabChanged(transition, point);
160 }
161}
162
163/*!
164 Acquire or give up a passive grab of the given \a point, according to the \a grab state.
165
166 Unlike the exclusive grab, multiple Input Handlers can have passive grabs
167 simultaneously. This means that each of them will receive further events
168 when the \a point moves, and when it is finally released. Typically an
169 Input Handler should acquire a passive grab as soon as a point is pressed,
170 if the handler's constraints do not clearly rule out any interest in that
171 point. For example, DragHandler needs a passive grab in order to watch the
172 movement of a point to see whether it will be dragged past the drag
173 threshold. When a handler is actively manipulating its \l target (that is,
174 when \l active is true), it may be able to do its work with only a passive
175 grab, or it may acquire an exclusive grab if the gesture clearly must not
176 be interpreted in another way by another handler.
177*/
178void QQuickPointerHandler::setPassiveGrab(QQuickEventPoint *point, bool grab)
179{
180 qCDebug(lcPointerHandlerGrab) << point << grab;
181 if (grab) {
182 point->setGrabberPointerHandler(this, false);
183 } else {
184 point->removePassiveGrabber(this);
185 }
186}
187
188/*!
189 Check whether it's OK to take an exclusive grab of the \a point.
190
191 The default implementation will call approveGrabTransition() to check this
192 handler's \l grabPermissions. If grabbing can be done only by taking over
193 the exclusive grab from an Item, approveGrabTransition() checks the Item's
194 \l keepMouseGrab or \l keepTouchGrab flags appropriately. If grabbing can
195 be done only by taking over another handler's exclusive grab, canGrab()
196 also calls approveGrabTransition() on the handler which is about to lose
197 its grab. Either one can deny the takeover.
198*/
199bool QQuickPointerHandler::canGrab(QQuickEventPoint *point)
200{
201 QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler();
202 return approveGrabTransition(point, this) &&
203 (existingPhGrabber ? existingPhGrabber->approveGrabTransition(point, this) : true);
204}
205
206/*!
207 Check this handler's rules to see if \l proposedGrabber will be allowed to take
208 the exclusive grab. This function may be called twice: once on the instance which
209 will take the grab, and once on the instance which would thereby lose its grab,
210 in case of a takeover scenario.
211*/
212bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObject *proposedGrabber)
213{
214 Q_D(const QQuickPointerHandler);
215 bool allowed = false;
216 if (proposedGrabber == this) {
217 QObject* existingGrabber = point->exclusiveGrabber();
218 allowed = (existingGrabber == nullptr) || ((d->grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything);
219 if (existingGrabber) {
220 if (QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler()) {
221 if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfDifferentType) &&
222 existingPhGrabber->metaObject()->className() != metaObject()->className())
223 allowed = true;
224 if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfSameType) &&
225 existingPhGrabber->metaObject()->className() == metaObject()->className())
226 allowed = true;
227 } else if ((d->grabPermissions & CanTakeOverFromItems)) {
228 QQuickItem * existingItemGrabber = point->grabberItem();
229 if (existingItemGrabber && !((existingItemGrabber->keepMouseGrab() && point->pointerEvent()->asPointerMouseEvent()) ||
230 (existingItemGrabber->keepTouchGrab() && point->pointerEvent()->asPointerTouchEvent())))
231 allowed = true;
232 }
233 }
234 } else {
235 // proposedGrabber is different: that means this instance will lose its grab
236 if (proposedGrabber) {
237 if ((d->grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything)
238 allowed = true;
239 if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) &&
240 proposedGrabber->metaObject()->className() != metaObject()->className())
241 allowed = true;
242 if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfSameType) &&
243 proposedGrabber->metaObject()->className() == metaObject()->className())
244 allowed = true;
245 if (!allowed && (d->grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits("QQuickItem"))
246 allowed = true;
247 } else {
248 if (!allowed && (d->grabPermissions & ApprovesCancellation))
249 allowed = true;
250 }
251 }
252 qCDebug(lcPointerHandlerGrab) << "point" << hex << point->pointId() << "permission" <<
253 QMetaEnum::fromType<GrabPermissions>().valueToKeys(grabPermissions()) <<
254 ':' << this << (allowed ? "approved to" : "denied to") << proposedGrabber;
255 return allowed;
256}
257
258/*!
259 \qmlproperty flags QtQuick::PointerHandler::grabPermissions
260
261 This property specifies the permissions when this handler's logic decides
262 to take over the exclusive grab, or when it is asked to approve grab
263 takeover or cancellation by another handler.
264
265 \value PointerHandler.TakeOverForbidden
266 This handler neither takes from nor gives grab permission to any type of Item or Handler.
267 \value PointerHandler.CanTakeOverFromHandlersOfSameType
268 This handler can take the exclusive grab from another handler of the same class.
269 \value PointerHandler.CanTakeOverFromHandlersOfDifferentType
270 This handler can take the exclusive grab from any kind of handler.
271 \value PointerHandler.CanTakeOverFromAnything
272 This handler can take the exclusive grab from any type of Item or Handler.
273 \value PointerHandler.ApprovesTakeOverByHandlersOfSameType
274 This handler gives permission for another handler of the same class to take the grab.
275 \value PointerHandler.ApprovesTakeOverByHandlersOfDifferentType
276 This handler gives permission for any kind of handler to take the grab.
277 \value PointerHandler.ApprovesTakeOverByItems
278 This handler gives permission for any kind of Item to take the grab.
279 \value PointerHandler.ApprovesCancellation
280 This handler will allow its grab to be set to null.
281 \value PointerHandler.ApprovesTakeOverByAnything
282 This handler gives permission for any any type of Item or Handler to take the grab.
283
284 The default is
285 \c {PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything}
286 which allows most takeover scenarios but avoids e.g. two PinchHandlers fighting
287 over the same touchpoints.
288*/
289QQuickPointerHandler::GrabPermissions QQuickPointerHandler::grabPermissions() const
290{
291 Q_D(const QQuickPointerHandler);
292 return static_cast<QQuickPointerHandler::GrabPermissions>(d->grabPermissions);
293}
294
295void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission)
296{
297 Q_D(QQuickPointerHandler);
298 if (d->grabPermissions == grabPermission)
299 return;
300
301 d->grabPermissions = grabPermission;
302 emit grabPermissionChanged();
303}
304
305void QQuickPointerHandler::classBegin()
306{
307}
308
309void QQuickPointerHandler::componentComplete()
310{
311}
312
313QQuickPointerEvent *QQuickPointerHandler::currentEvent()
314{
315 Q_D(const QQuickPointerHandler);
316 return d->currentEvent;
317}
318
319/*!
320 Acquire or give up the exclusive grab of the given \a point, according to
321 the \a grab state, and subject to the rules: canGrab(), and the rule not to
322 relinquish another handler's grab. Returns true if permission is granted,
323 or if the exclusive grab has already been acquired or relinquished as
324 specified. Returns false if permission is denied either by this handler or
325 by the handler or item from which this handler would take over
326*/
327bool QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab)
328{
329 if ((grab && point->exclusiveGrabber() == this) || (!grab && point->exclusiveGrabber() != this))
330 return true;
331 // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab
332 bool allowed = true;
333 if (grab) {
334 allowed = canGrab(point);
335 } else {
336 QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler();
337 // Ask before allowing one handler to cancel another's grab
338 if (existingPhGrabber && existingPhGrabber != this && !existingPhGrabber->approveGrabTransition(point, nullptr))
339 allowed = false;
340 }
341 qCDebug(lcPointerHandlerGrab) << point << (grab ? "grab" : "ungrab") << (allowed ? "allowed" : "forbidden") <<
342 point->exclusiveGrabber() << "->" << (grab ? this : nullptr);
343 if (allowed)
344 point->setGrabberPointerHandler(grab ? this : nullptr, true);
345 return allowed;
346}
347
348/*!
349 Cancel any existing grab of the given \a point.
350*/
351void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point)
352{
353 qCDebug(lcPointerHandlerGrab) << point;
354 point->cancelAllGrabs(this);
355}
356
357QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const
358{
359 return (target() ? target()->mapFromScene(point->scenePosition()) : point->scenePosition());
360}
361
362bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const
363{
364 if (!point)
365 return false;
366 if (QQuickItem *par = parentItem()) {
367 if (par->window()) {
368 QPoint screenPosition = par->window()->mapToGlobal(point->scenePosition().toPoint());
369 if (!par->window()->geometry().contains(screenPosition))
370 return false;
371 }
372 QPointF p = par->mapFromScene(point->scenePosition());
373 qreal m = margin();
374 if (m > 0)
375 return p.x() >= -m && p.y() >= -m && p.x() <= par->width() + m && p.y() <= par->height() + m;
376 return par->contains(p);
377 }
378 return false;
379}
380
381/*!
382 \qmlproperty bool QtQuick::PointerHandler::enabled
383
384 If a PointerHandler is disabled, it will reject all events
385 and no signals will be emitted.
386*/
387bool QQuickPointerHandler::enabled() const
388{
389 Q_D(const QQuickPointerHandler);
390 return d->enabled;
391}
392
393void QQuickPointerHandler::setEnabled(bool enabled)
394{
395 Q_D(QQuickPointerHandler);
396 if (d->enabled == enabled)
397 return;
398
399 d->enabled = enabled;
400 emit enabledChanged();
401}
402
403bool QQuickPointerHandler::active() const
404{
405 Q_D(const QQuickPointerHandler);
406 return d->active;
407}
408
409/*!
410 \qmlproperty Item QtQuick::PointerHandler::target
411
412 The Item which this handler will manipulate.
413
414 By default, it is the same as the \l [QML] {parent}, the Item within which
415 the handler is declared. However, it can sometimes be useful to set the
416 target to a different Item, in order to handle events within one item
417 but manipulate another; or to \c null, to disable the default behavior
418 and do something else instead.
419*/
420void QQuickPointerHandler::setTarget(QQuickItem *target)
421{
422 Q_D(QQuickPointerHandler);
423 d->targetExplicitlySet = true;
424 if (d->target == target)
425 return;
426
427 QQuickItem *oldTarget = d->target;
428 d->target = target;
429 onTargetChanged(oldTarget);
430 emit targetChanged();
431}
432
433QQuickItem *QQuickPointerHandler::parentItem() const
434{
435 return static_cast<QQuickItem *>(QObject::parent());
436}
437
438QQuickItem *QQuickPointerHandler::target() const
439{
440 Q_D(const QQuickPointerHandler);
441 if (!d->targetExplicitlySet)
442 return parentItem();
443 return d->target;
444}
445
446void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event)
447{
448 bool wants = wantsPointerEvent(event);
449 qCDebug(lcPointerHandlerDispatch) << metaObject()->className() << objectName()
450 << "on" << parentItem()->metaObject()->className() << parentItem()->objectName()
451 << (wants ? "WANTS" : "DECLINES") << event;
452 if (wants) {
453 handlePointerEventImpl(event);
454 } else {
455 setActive(false);
456 int pCount = event->pointCount();
457 for (int i = 0; i < pCount; ++i) {
458 QQuickEventPoint *pt = event->point(i);
459 if (pt->grabberPointerHandler() == this && pt->state() != QQuickEventPoint::Stationary)
460 pt->cancelExclusiveGrab();
461 }
462 }
463 event->device()->eventDeliveryTargets().append(this);
464}
465
466bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
467{
468 Q_D(const QQuickPointerHandler);
469 Q_UNUSED(event)
470 return d->enabled;
471}
472
473bool QQuickPointerHandler::wantsEventPoint(QQuickEventPoint *point)
474{
475 bool ret = point->exclusiveGrabber() == this || point->passiveGrabbers().contains(this) || parentContains(point);
476 qCDebug(lcPointerHandlerDispatch) << hex << point->pointId() << "@" << point->scenePosition()
477 << metaObject()->className() << objectName() << ret;
478 return ret;
479}
480
481/*!
482 \readonly
483 \qmlproperty bool QtQuick::PointerHandler::active
484
485 This holds true whenever this Input Handler has taken sole responsibility
486 for handing one or more EventPoints, by successfully taking an exclusive
487 grab of those points. This means that it is keeping its properties
488 up-to-date according to the movements of those Event Points and actively
489 manipulating its \l target (if any).
490*/
491void QQuickPointerHandler::setActive(bool active)
492{
493 Q_D(QQuickPointerHandler);
494 if (d->active != active) {
495 qCDebug(lcPointerHandlerActive) << this << d->active << "->" << active;
496 d->active = active;
497 onActiveChanged();
498 emit activeChanged();
499 }
500}
501
502void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
503{
504 Q_D(QQuickPointerHandler);
505 d->currentEvent = event;
506}
507
508/*!
509 \readonly
510 \qmlproperty Item QtQuick::PointerHandler::parent
511
512 The \l Item which is the scope of the handler; the Item in which it was declared.
513 The handler will handle events on behalf of this Item, which means a
514 pointer event is relevant if at least one of its event points occurs within
515 the Item's interior. Initially \l [QML] {target} {target()} is the same, but it
516 can be reassigned.
517
518 \sa {target}, QObject::parent()
519*/
520
521/*!
522 \qmlsignal QtQuick::PointerHandler::grabChanged(GrabTransition transition, EventPoint point)
523
524 This signal is emitted when the grab has changed in some way which is
525 relevant to this handler.
526
527 The \a transition (verb) tells what happened.
528 The \a point (object) is the point that was grabbed or ungrabbed.
529*/
530
531/*!
532 \qmlsignal QtQuick::PointerHandler::canceled(EventPoint point)
533
534 If this handler has already grabbed the given \a point, this signal is
535 emitted when the grab is stolen by a different Pointer Handler or Item.
536*/
537
538QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate()
539 : grabPermissions(QQuickPointerHandler::CanTakeOverFromItems |
540 QQuickPointerHandler::CanTakeOverFromHandlersOfDifferentType |
541 QQuickPointerHandler::ApprovesTakeOverByAnything)
542 , enabled(true)
543 , active(false)
544 , targetExplicitlySet(false)
545 , hadKeepMouseGrab(false)
546 , hadKeepTouchGrab(false)
547{
548}
549
550QT_END_NAMESPACE
551