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