1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Data Visualization module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "qtouch3dinputhandler_p.h"
31#include <QtCore/QTimer>
32#include <QtCore/qmath.h>
33
34QT_BEGIN_NAMESPACE_DATAVISUALIZATION
35
36static const float maxTapAndHoldJitter = 20.0f;
37static const int maxPinchJitter = 10;
38#if defined (Q_OS_ANDROID) || defined(Q_OS_IOS)
39static const int maxSelectionJitter = 10;
40#else
41static const int maxSelectionJitter = 5;
42#endif
43static const int tapAndHoldTime = 250;
44static const float rotationSpeed = 200.0f;
45static const float touchZoomDrift = 0.02f;
46
47/*!
48 * \class QTouch3DInputHandler
49 * \inmodule QtDataVisualization
50 * \brief Basic touch display based input handler.
51 * \since QtDataVisualization 1.0
52 *
53 * QTouch3DInputHandler is the basic input handler for touch screen devices.
54 *
55 * Default touch input handler has the following functionalty:
56 * \table
57 * \header
58 * \li Gesture
59 * \li Action
60 * \row
61 * \li Touch-And-Move
62 * \li Rotate graph within limits set for Q3DCamera
63 * \row
64 * \li Tap
65 * \li Select the item tapped or remove selection if none.
66 * May open the secondary view depending on the
67 * \l {QAbstract3DGraph::selectionMode}{selection mode}.
68 * \row
69 * \li Tap-And-Hold
70 * \li Same as tap.
71 * \row
72 * \li Pinch
73 * \li Zoom in/out within the allowable zoom range set for Q3DCamera.
74 * \row
75 * \li Tap on the primary view when the secondary view is visible
76 * \li Closes the secondary view.
77 * \note Secondary view is available only for Q3DBars and Q3DSurface graphs.
78 * \endtable
79 *
80 * Rotation, zoom, and selection can each be individually disabled using
81 * corresponding Q3DInputHandler properties.
82 */
83
84/*!
85 * \qmltype TouchInputHandler3D
86 * \inqmlmodule QtDataVisualization
87 * \since QtDataVisualization 1.2
88 * \ingroup datavisualization_qml
89 * \instantiates QTouch3DInputHandler
90 * \inherits InputHandler3D
91 * \brief Basic touch display based input handler.
92 *
93 * TouchInputHandler3D is the basic input handler for touch screen devices.
94 *
95 * See QTouch3DInputHandler documentation for more details.
96 */
97
98/*!
99 * Constructs the basic touch display input handler. An optional \a parent parameter can be given
100 * and is then passed to QObject constructor.
101 */
102QTouch3DInputHandler::QTouch3DInputHandler(QObject *parent)
103 : Q3DInputHandler(parent),
104 d_ptr(new QTouch3DInputHandlerPrivate(this))
105{
106}
107
108/*!
109 * Destroys the input handler.
110 */
111QTouch3DInputHandler::~QTouch3DInputHandler()
112{
113}
114
115/*!
116 * Override this to change handling of touch events.
117 * Touch event is given in the \a event.
118 */
119void QTouch3DInputHandler::touchEvent(QTouchEvent *event)
120{
121 QList<QTouchEvent::TouchPoint> points;
122 points = event->touchPoints();
123
124 if (!scene()->isSlicingActive() && points.count() == 2) {
125 d_ptr->m_holdTimer->stop();
126 QPointF distance = points.at(i: 0).pos() - points.at(i: 1).pos();
127 QPoint midPoint = ((points.at(i: 0).pos() + points.at(i: 1).pos()) / 2.0).toPoint();
128 d_ptr->handlePinchZoom(distance: distance.manhattanLength(), pos: midPoint);
129 } else if (points.count() == 1) {
130 QPointF pointerPos = points.at(i: 0).pos();
131 if (event->type() == QEvent::TouchBegin) {
132 // Flush input state
133 d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone;
134 if (scene()->isSlicingActive()) {
135 if (isSelectionEnabled()) {
136 if (scene()->isPointInPrimarySubView(point: pointerPos.toPoint()))
137 setInputView(InputViewOnPrimary);
138 else if (scene()->isPointInSecondarySubView(point: pointerPos.toPoint()))
139 setInputView(InputViewOnSecondary);
140 else
141 setInputView(InputViewNone);
142 }
143 } else {
144 // Handle possible tap-and-hold selection
145 if (isSelectionEnabled()) {
146 d_ptr->m_startHoldPos = pointerPos;
147 d_ptr->m_touchHoldPos = d_ptr->m_startHoldPos;
148 d_ptr->m_holdTimer->start();
149 setInputView(InputViewOnPrimary);
150 }
151 // Start rotating
152 if (isRotationEnabled()) {
153 d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating;
154 setInputPosition(pointerPos.toPoint());
155 setInputView(InputViewOnPrimary);
156 }
157 }
158 } else if (event->type() == QEvent::TouchEnd) {
159 setInputView(InputViewNone);
160 d_ptr->m_holdTimer->stop();
161 // Handle possible selection
162 if (!scene()->isSlicingActive()
163 && QAbstract3DInputHandlerPrivate::InputStatePinching
164 != d_ptr->m_inputState) {
165 d_ptr->handleSelection(position: pointerPos);
166 }
167 } else if (event->type() == QEvent::TouchUpdate) {
168 if (!scene()->isSlicingActive()) {
169 d_ptr->m_touchHoldPos = pointerPos;
170 // Handle rotation
171 d_ptr->handleRotation(position: pointerPos);
172 }
173 }
174 } else {
175 d_ptr->m_holdTimer->stop();
176 }
177}
178
179QTouch3DInputHandlerPrivate::QTouch3DInputHandlerPrivate(QTouch3DInputHandler *q)
180 : Q3DInputHandlerPrivate(q),
181 q_ptr(q),
182 m_holdTimer(0),
183 m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone)
184{
185 m_holdTimer = new QTimer();
186 m_holdTimer->setSingleShot(true);
187 m_holdTimer->setInterval(tapAndHoldTime);
188 connect(sender: m_holdTimer, signal: &QTimer::timeout, receiver: this, slot: &QTouch3DInputHandlerPrivate::handleTapAndHold);
189}
190
191QTouch3DInputHandlerPrivate::~QTouch3DInputHandlerPrivate()
192{
193 m_holdTimer->stop();
194 delete m_holdTimer;
195}
196
197void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance, const QPoint &pos)
198{
199 if (q_ptr->isZoomEnabled()) {
200 int newDistance = distance;
201 int prevDist = q_ptr->prevDistance();
202 if (prevDist > 0 && qAbs(t: prevDist - newDistance) < maxPinchJitter)
203 return;
204 m_inputState = QAbstract3DInputHandlerPrivate::InputStatePinching;
205 Q3DCamera *camera = q_ptr->scene()->activeCamera();
206 int zoomLevel = int(camera->zoomLevel());
207 const int minZoomLevel = int(camera->minZoomLevel());
208 const int maxZoomLevel = int(camera->maxZoomLevel());
209 float zoomRate = qSqrt(v: qSqrt(v: zoomLevel));
210 if (newDistance > prevDist)
211 zoomLevel += zoomRate;
212 else
213 zoomLevel -= zoomRate;
214 zoomLevel = qBound(min: minZoomLevel, val: zoomLevel, max: maxZoomLevel);
215
216 if (q_ptr->isZoomAtTargetEnabled()) {
217 q_ptr->scene()->setGraphPositionQuery(pos);
218 m_zoomAtTargetPending = true;
219 // If zoom at target is enabled, we don't want to zoom yet, as that causes
220 // jitter. Instead, we zoom next frame, when we apply the camera position.
221 m_requestedZoomLevel = zoomLevel;
222 m_driftMultiplier = touchZoomDrift;
223 } else {
224 camera->setZoomLevel(zoomLevel);
225 }
226
227 q_ptr->setPrevDistance(newDistance);
228 }
229}
230
231void QTouch3DInputHandlerPrivate::handleTapAndHold()
232{
233 if (q_ptr->isSelectionEnabled()) {
234 QPointF distance = m_startHoldPos - m_touchHoldPos;
235 if (distance.manhattanLength() < maxTapAndHoldJitter) {
236 q_ptr->setInputPosition(m_touchHoldPos.toPoint());
237 q_ptr->scene()->setSelectionQueryPosition(m_touchHoldPos.toPoint());
238 m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
239 }
240 }
241}
242
243void QTouch3DInputHandlerPrivate::handleSelection(const QPointF &position)
244{
245 if (q_ptr->isSelectionEnabled()) {
246 QPointF distance = m_startHoldPos - position;
247 if (distance.manhattanLength() < maxSelectionJitter) {
248 m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
249 q_ptr->scene()->setSelectionQueryPosition(position.toPoint());
250 } else {
251 m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone;
252 q_ptr->setInputView(QAbstract3DInputHandler::InputViewNone);
253 }
254 q_ptr->setPreviousInputPos(position.toPoint());
255 }
256}
257
258void QTouch3DInputHandlerPrivate::handleRotation(const QPointF &position)
259{
260 if (q_ptr->isRotationEnabled()
261 && QAbstract3DInputHandlerPrivate::InputStateRotating == m_inputState) {
262 Q3DScene *scene = q_ptr->scene();
263 Q3DCamera *camera = scene->activeCamera();
264 float xRotation = camera->xRotation();
265 float yRotation = camera->yRotation();
266 QPointF inputPos = q_ptr->inputPosition();
267 float mouseMoveX = float(inputPos.x() - position.x())
268 / (scene->viewport().width() / rotationSpeed);
269 float mouseMoveY = float(inputPos.y() - position.y())
270 / (scene->viewport().height() / rotationSpeed);
271 xRotation -= mouseMoveX;
272 yRotation -= mouseMoveY;
273 camera->setXRotation(xRotation);
274 camera->setYRotation(yRotation);
275
276 q_ptr->setPreviousInputPos(inputPos.toPoint());
277 q_ptr->setInputPosition(position.toPoint());
278 }
279}
280
281QT_END_NAMESPACE_DATAVISUALIZATION
282

source code of qtdatavis3d/src/datavisualization/input/qtouch3dinputhandler.cpp