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

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