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 "qquicksinglepointhandler_p.h"
5#include "qquicksinglepointhandler_p_p.h"
6#include <private/qquickdeliveryagent_p_p.h>
7
8#include <private/qquickdeliveryagent_p.h>
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \qmltype SinglePointHandler
14 \qmlabstract
15 \preliminary
16 \nativetype QQuickSinglePointHandler
17 \inherits PointerDeviceHandler
18 \inqmlmodule QtQuick
19 \brief Abstract handler for single-point Pointer Events.
20
21 An intermediate class (not registered as a QML type)
22 for the most common handlers: those which expect only a single point.
23 wantsPointerEvent() will choose the first point which is inside the
24 \l target item, and return true as long as the event contains that point.
25 Override handleEventPoint() to implement a single-point handler.
26*/
27
28QQuickSinglePointHandler::QQuickSinglePointHandler(QQuickItem *parent)
29 : QQuickPointerDeviceHandler(*(new QQuickSinglePointHandlerPrivate), parent)
30{
31}
32
33QQuickSinglePointHandler::QQuickSinglePointHandler(QQuickSinglePointHandlerPrivate &dd, QQuickItem *parent)
34 : QQuickPointerDeviceHandler(dd, parent)
35{
36}
37
38bool QQuickSinglePointHandler::wantsPointerEvent(QPointerEvent *event)
39{
40 Q_D(QQuickSinglePointHandler);
41 if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
42 return false;
43
44 if (d->pointInfo.id() != -1) {
45 // We already know which one we want, so check whether it's there.
46 // It's expected to be an update or a release.
47 // If we no longer want it, cancel the grab.
48 int candidatePointCount = 0;
49 bool missing = true;
50 QEventPoint *point = nullptr;
51 for (int i = 0; i < event->pointCount(); ++i) {
52 auto &p = event->point(i);
53 const bool found = (p.id() == d->pointInfo.id());
54 if (found)
55 missing = false;
56 if (wantsEventPoint(event, point: p)) {
57 ++candidatePointCount;
58 if (found)
59 point = &p;
60 }
61 }
62 if (missing) {
63 // Received a stray touch begin event => reset and start over.
64 if (event->type() == QEvent::TouchBegin && event->points().count() == 1) {
65 const QEventPoint &point = event->point(i: 0);
66 qCDebug(lcTouchTarget) << this << "pointId" << Qt::hex << point.id()
67 << "was received as a stray TouchBegin event. Canceling existing gesture"
68 " and starting over.";
69 d->pointInfo.reset(event, point);
70 return true;
71 } else {
72 qCWarning(lcTouchTarget) << this << "pointId" << Qt::hex << d->pointInfo.id()
73 << "is missing from current event, but was neither canceled nor released."
74 " Ignoring:" << event->type();
75 }
76 }
77 if (point) {
78 if (candidatePointCount == 1 || (candidatePointCount > 1 && d->ignoreAdditionalPoints)) {
79 point->setAccepted();
80 return true;
81 } else {
82 cancelAllGrabs(event, point&: *point);
83 }
84 } else {
85 return false;
86 }
87 } else {
88 // We have not yet chosen a point; choose the first one for which wantsEventPoint() returns true.
89 int candidatePointCount = 0;
90 QEventPoint *chosen = nullptr;
91 for (int i = 0; i < event->pointCount(); ++i) {
92 auto &p = event->point(i);
93 if (!event->exclusiveGrabber(point: p) && wantsEventPoint(event, point: p)) {
94 ++candidatePointCount;
95 if (!chosen) {
96 chosen = &p;
97 break;
98 }
99 }
100 }
101 if (chosen && candidatePointCount == 1) {
102 setPointId(chosen->id());
103 chosen->setAccepted();
104 }
105 }
106 return d->pointInfo.id() != -1;
107}
108
109void QQuickSinglePointHandler::handlePointerEventImpl(QPointerEvent *event)
110{
111 Q_D(QQuickSinglePointHandler);
112 QQuickPointerDeviceHandler::handlePointerEventImpl(event);
113 QEventPoint *currentPoint = const_cast<QEventPoint *>(event->pointById(id: d->pointInfo.id()));
114 Q_ASSERT(currentPoint);
115 if (!QQuickDeliveryAgentPrivate::isSynthMouse(ev: event))
116 d->pointInfo.reset(event, point: *currentPoint);
117 handleEventPoint(event, point&: *currentPoint);
118 emit pointChanged();
119}
120
121void QQuickSinglePointHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point)
122{
123 if (point.state() == QEventPoint::Released) {
124 // If it's a mouse or tablet event, with buttons,
125 // do not deactivate unless all acceptable buttons are released.
126 if (event->isSinglePointEvent()) {
127 const Qt::MouseButtons releasedButtons = static_cast<QSinglePointEvent *>(event)->buttons();
128 if ((releasedButtons & acceptedButtons()) != Qt::NoButton)
129 return;
130 }
131
132 // Deactivate this handler on release
133 setExclusiveGrab(ev: event, point, grab: false);
134 d_func()->reset();
135 }
136}
137
138void QQuickSinglePointHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition, QPointerEvent *event, QEventPoint &point)
139{
140 Q_D(QQuickSinglePointHandler);
141 if (grabber != this)
142 return;
143 switch (transition) {
144 case QPointingDevice::GrabExclusive:
145 d->pointInfo.m_sceneGrabPosition = point.sceneGrabPosition();
146 setActive(true);
147 QQuickPointerHandler::onGrabChanged(grabber, transition, event, point);
148 break;
149 case QPointingDevice::GrabPassive:
150 d->pointInfo.m_sceneGrabPosition = point.sceneGrabPosition();
151 QQuickPointerHandler::onGrabChanged(grabber, transition, event, point);
152 break;
153 case QPointingDevice::OverrideGrabPassive:
154 return; // don't emit
155 case QPointingDevice::UngrabPassive:
156 case QPointingDevice::UngrabExclusive:
157 case QPointingDevice::CancelGrabPassive:
158 case QPointingDevice::CancelGrabExclusive:
159 // the grab is lost or relinquished, so the point is no longer relevant
160 QQuickPointerHandler::onGrabChanged(grabber, transition, event, point);
161 d->reset();
162 break;
163 }
164}
165
166void QQuickSinglePointHandler::setIgnoreAdditionalPoints(bool v)
167{
168 Q_D(QQuickSinglePointHandler);
169 d->ignoreAdditionalPoints = v;
170}
171
172void QQuickSinglePointHandler::moveTarget(QPointF pos, QEventPoint &point)
173{
174 Q_D(QQuickSinglePointHandler);
175 target()->setPosition(pos);
176 d->pointInfo.m_scenePosition = point.scenePosition();
177 d->pointInfo.m_position = target()->mapFromScene(point: d->pointInfo.m_scenePosition);
178}
179
180void QQuickSinglePointHandler::setPointId(int id)
181{
182 Q_D(QQuickSinglePointHandler);
183 d->pointInfo.m_id = id;
184}
185
186QQuickHandlerPoint QQuickSinglePointHandler::point() const
187{
188 Q_D(const QQuickSinglePointHandler);
189 return d->pointInfo;
190}
191
192/*!
193 \readonly
194 \qmlproperty handlerPoint QtQuick::SinglePointHandler::point
195
196 The \l eventPoint currently being handled. When no point is currently being
197 handled, this object is reset to default values (all coordinates are 0).
198*/
199
200QQuickSinglePointHandlerPrivate::QQuickSinglePointHandlerPrivate()
201 : QQuickPointerDeviceHandlerPrivate()
202{
203}
204
205void QQuickSinglePointHandlerPrivate::reset()
206{
207 Q_Q(QQuickSinglePointHandler);
208 q->setActive(false);
209 pointInfo.reset();
210}
211
212QT_END_NAMESPACE
213
214#include "moc_qquicksinglepointhandler_p.cpp"
215

source code of qtdeclarative/src/quick/handlers/qquicksinglepointhandler.cpp