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

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