1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qgraphsinputhandler_p.h"
5
6#include <QtQuick/private/qquickdraghandler_p.h>
7#include <QtQuick/private/qquickpinchhandler_p.h>
8#include <QtQuick/private/qquicktaphandler_p.h>
9#include <QtQuick/private/qquickwheelhandler_p.h>
10
11#include "qquickgraphsitem_p.h"
12
13QGraphsInputHandler::QGraphsInputHandler(QQuickItem *parent)
14 : QQuickItem(parent)
15 , m_zoomEnabled(true)
16 , m_zoomAtTarget(true)
17 , m_rotationEnabled(true)
18 , m_selectionEnabled(true)
19 , m_pendingPoint(QPoint())
20 , m_pinchDiff(.0f)
21 , m_graphsItem(nullptr)
22{
23 m_pinchHandler = new QQuickPinchHandler(this);
24 m_tapHandler = new QQuickTapHandler(this);
25
26 // This is to support QQuickGraphsItem's mouseMove signal.
27 setAcceptHoverEvents(true);
28 m_dragHandler = new QQuickDragHandler(this);
29 m_wheelHandler = new QQuickWheelHandler(this);
30 m_dragHandler->setAcceptedButtons(Qt::MouseButton::RightButton);
31 m_wheelHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse
32 | QInputDevice::DeviceType::TouchPad);
33 QObject::connect(sender: m_tapHandler, signal: &QQuickTapHandler::tapped, context: this, slot: &QGraphsInputHandler::onTapped);
34 QObject::connect(sender: m_dragHandler,
35 signal: &QQuickDragHandler::translationChanged,
36 context: this,
37 slot: &QGraphsInputHandler::onTranslationChanged);
38 QObject::connect(sender: m_dragHandler,
39 signal: &QQuickDragHandler::grabChanged,
40 context: this,
41 slot: &QGraphsInputHandler::onGrabChanged);
42 QObject::connect(sender: m_wheelHandler,
43 signal: &QQuickWheelHandler::wheel,
44 context: this,
45 slot: &QGraphsInputHandler::onWheel);
46 QObject::connect(sender: m_pinchHandler,
47 signal: &QQuickPinchHandler::scaleChanged,
48 context: this,
49 slot: &QGraphsInputHandler::onPinchScaleChanged);
50 QObject::connect(sender: m_pinchHandler,
51 signal: &QQuickPinchHandler::grabChanged,
52 context: this,
53 slot: &QGraphsInputHandler::onGrabChanged);
54}
55
56QGraphsInputHandler::~QGraphsInputHandler() {}
57
58void QGraphsInputHandler::setZoomEnabled(bool enable)
59{
60 if (m_zoomEnabled != enable) {
61 m_zoomEnabled = enable;
62 if (m_graphsItem)
63 emit m_graphsItem->zoomEnabledChanged(enable);
64 }
65}
66
67bool QGraphsInputHandler::isZoomEnabled()
68{
69 return m_zoomEnabled;
70}
71
72void QGraphsInputHandler::setZoomAtTargetEnabled(bool enable)
73{
74 if (m_zoomAtTarget != enable) {
75 m_zoomAtTarget = enable;
76 if (m_graphsItem)
77 emit m_graphsItem->zoomAtTargetEnabledChanged(enable);
78 }
79}
80
81bool QGraphsInputHandler::isZoomAtTargetEnabled()
82{
83 return m_zoomAtTarget;
84}
85
86void QGraphsInputHandler::setRotationEnabled(bool enable)
87{
88 if (m_rotationEnabled != enable) {
89 m_rotationEnabled = enable;
90 if (m_graphsItem)
91 emit m_graphsItem->rotationEnabledChanged(enable);
92 }
93}
94
95bool QGraphsInputHandler::isRotationEnabled()
96{
97 return m_rotationEnabled;
98}
99
100void QGraphsInputHandler::setSelectionEnabled(bool enable)
101{
102 if (m_selectionEnabled != enable) {
103 m_selectionEnabled = enable;
104 if (m_graphsItem)
105 emit m_graphsItem->selectionEnabledChanged(enable);
106 }
107}
108
109bool QGraphsInputHandler::isSelectionEnabled()
110{
111 return m_selectionEnabled;
112}
113
114void QGraphsInputHandler::setDefaultInputHandler()
115{
116 setVisible(true);
117}
118
119void QGraphsInputHandler::unsetDefaultInputHandler()
120{
121 setVisible(false);
122}
123
124void QGraphsInputHandler::unsetDefaultTapHandler()
125{
126 QObject::disconnect(sender: m_tapHandler,
127 signal: &QQuickTapHandler::tapped,
128 receiver: this,
129 slot: &QGraphsInputHandler::onTapped);
130}
131
132void QGraphsInputHandler::unsetDefaultDragHandler()
133{
134 QObject::disconnect(sender: m_dragHandler,
135 signal: &QQuickDragHandler::translationChanged,
136 receiver: this,
137 slot: &QGraphsInputHandler::onTranslationChanged);
138}
139
140void QGraphsInputHandler::unsetDefaultWheelHandler()
141{
142 QObject::disconnect(sender: m_wheelHandler,
143 signal: &QQuickWheelHandler::wheel,
144 receiver: this,
145 slot: &QGraphsInputHandler::onWheel);
146}
147
148void QGraphsInputHandler::unsetDefaultPinchHandler()
149{
150 QObject::disconnect(sender: m_pinchHandler,
151 signal: &QQuickPinchHandler::scaleChanged,
152 receiver: this,
153 slot: &QGraphsInputHandler::onPinchScaleChanged);
154}
155
156void QGraphsInputHandler::setDragButton(Qt::MouseButtons button)
157{
158 m_dragHandler->setAcceptedButtons(button | Qt::MouseButton::RightButton);
159}
160
161void QGraphsInputHandler::setGraphsItem(QQuickGraphsItem *item)
162{
163 m_graphsItem = item;
164 QObject::connect(sender: m_tapHandler, signal: &QQuickTapHandler::tapped, context: item, slot: &QQuickGraphsItem::tapped);
165 QObject::connect(sender: m_tapHandler,
166 signal: &QQuickTapHandler::doubleTapped,
167 context: item,
168 slot: &QQuickGraphsItem::doubleTapped);
169 QObject::connect(sender: m_tapHandler,
170 signal: &QQuickTapHandler::longPressed,
171 context: item,
172 slot: &QQuickGraphsItem::longPressed);
173 QObject::connect(sender: m_dragHandler,
174 signal: &QQuickDragHandler::translationChanged,
175 context: item,
176 slot: &QQuickGraphsItem::dragged);
177 QObject::connect(sender: m_wheelHandler, signal: &QQuickWheelHandler::wheel, context: item, slot: &QQuickGraphsItem::wheel);
178 QObject::connect(sender: m_pinchHandler,
179 signal: &QQuickPinchHandler::scaleChanged,
180 context: item,
181 slot: &QQuickGraphsItem::pinch);
182 QObject::connect(sender: this, signal: &QGraphsInputHandler::mouseMove, context: item, slot: &QQuickGraphsItem::mouseMove);
183}
184
185void QGraphsInputHandler::onTapped()
186{
187 if (!m_selectionEnabled)
188 return;
189
190 if (m_graphsItem->isSlicingActive()) {
191 m_graphsItem->setSliceActivatedChanged(true);
192 m_graphsItem->update();
193 return;
194 }
195 m_graphsItem->doPicking(point: m_tapHandler->point().position());
196}
197
198void QGraphsInputHandler::onTranslationChanged(QVector2D delta)
199{
200 if (!m_rotationEnabled)
201 return;
202
203 if (m_dragHandler->centroid().pressedButtons().testFlag(flag: Qt::LeftButton))
204 return;
205
206 float rotationSpeed = 1.0f;
207#if !defined(Q_OS_IOS)
208 rotationSpeed = 10.0f;
209#endif
210 QQuickGraphsItem *item = m_graphsItem;
211 // Calculate mouse movement since last frame
212 float xRotation = item->cameraXRotation();
213 float yRotation = item->cameraYRotation();
214 // Apply to rotations
215 xRotation += (delta.x() / rotationSpeed);
216 yRotation += (delta.y() / rotationSpeed);
217 item->setCameraXRotation(xRotation);
218 item->setCameraYRotation(yRotation);
219}
220
221void QGraphsInputHandler::onGrabChanged(QPointingDevice::GrabTransition transition,
222 QEventPoint point)
223{
224 static QPointF pickPoint;
225
226 if (transition == QPointingDevice::GrabPassive) {
227 pickPoint = point.position().toPoint();
228 } else if (transition == QPointingDevice::GrabExclusive) {
229 if (m_dragHandler->centroid().pressedButtons().testFlag(flag: Qt::LeftButton))
230 m_graphsItem->doPicking(point: pickPoint);
231 } else if (transition == QPointingDevice::UngrabExclusive
232 || transition == QPointingDevice::UngrabPassive) {
233 setPosition(QPointF(.0f, .0f));
234 setScale(1.f);
235 setRotation(.0f);
236 emit m_graphsItem->selectedElementChanged(type: QtGraphs3D::ElementType::None);
237 pickPoint = QPointF();
238 }
239}
240
241void QGraphsInputHandler::onWheel(QQuickWheelEvent *event)
242{
243 if (!m_zoomEnabled)
244 return;
245
246 int halfSizeZoomLevel = 50;
247 int oneToOneZoomLevel = 100;
248
249 int driftTowardCenterLevel = 175;
250 float wheelZoomDrift = 0.1f;
251
252 int nearZoomRangeDivider = 12;
253 int midZoomRangeDivider = 60;
254 int farZoomRangeDivider = 120;
255
256 if (m_graphsItem->isSlicingActive())
257 return;
258
259 // Adjust zoom level based on what zoom range we're in.
260 QQuickGraphsItem *item = m_graphsItem;
261 int zoomLevel = int(item->cameraZoomLevel());
262 const int minZoomLevel = int(item->minCameraZoomLevel());
263 const int maxZoomLevel = int(item->maxCameraZoomLevel());
264 if (zoomLevel > oneToOneZoomLevel)
265 zoomLevel += event->angleDelta().y() / nearZoomRangeDivider;
266 else if (zoomLevel > halfSizeZoomLevel)
267 zoomLevel += event->angleDelta().y() / midZoomRangeDivider;
268 else
269 zoomLevel += event->angleDelta().y() / farZoomRangeDivider;
270 zoomLevel = qBound(min: minZoomLevel, val: zoomLevel, max: maxZoomLevel);
271
272 if (m_zoomAtTarget) {
273 QVector3D targetPosition = item->graphPositionAt(point: QPoint(event->x(), event->y()));
274 float previousZoom = item->cameraZoomLevel();
275 item->setCameraZoomLevel(zoomLevel);
276
277 float diffAdj = 0.0f;
278
279 // If zooming in/out outside the graph, or zooming out after certain point,
280 // move towards the center.
281 if ((qAbs(t: targetPosition.x()) > 2.0f || qAbs(t: targetPosition.y()) > 2.0f
282 || qAbs(t: targetPosition.z()) > 2.0f)
283 || (previousZoom > zoomLevel && zoomLevel <= driftTowardCenterLevel)) {
284 targetPosition = QVector3D();
285 // Add some extra correction so that we actually reach the center
286 // eventually
287 diffAdj = wheelZoomDrift;
288 if (previousZoom > zoomLevel)
289 diffAdj *= 2.0f; // Correct towards center little more when zooming out
290 }
291
292 float zoomFraction = 1.0f - (previousZoom / zoomLevel);
293
294 // Adjust camera towards the zoom point, attempting to keep the cursor at
295 // same graph point
296 QVector3D oldTarget = item->cameraTargetPosition();
297 QVector3D origDiff = targetPosition - oldTarget;
298 QVector3D diff = origDiff * zoomFraction + (origDiff.normalized() * diffAdj);
299 if (diff.length() > origDiff.length())
300 diff = origDiff;
301 item->setCameraTargetPosition(oldTarget + diff);
302 } else {
303 item->setCameraZoomLevel(zoomLevel);
304 }
305
306 item->update();
307}
308
309void QGraphsInputHandler::onPinchScaleChanged(qreal delta)
310{
311 if (!m_zoomEnabled)
312 return;
313
314 m_pinchDiff += (delta - 1.0f);
315 int driftTowardCenterLevel = 175;
316 float wheelZoomDrift = 0.1f;
317
318 QQuickGraphsItem *item = m_graphsItem;
319 int zoomLevel = int(item->cameraZoomLevel());
320 const int minZoomLevel = int(item->minCameraZoomLevel());
321 const int maxZoomLevel = int(item->maxCameraZoomLevel());
322 float zoomRate = qSqrt(v: qSqrt(v: zoomLevel));
323 if (m_pinchDiff > .0f)
324 zoomLevel += zoomRate;
325 else
326 zoomLevel -= zoomRate;
327 zoomLevel = qBound(min: minZoomLevel, val: zoomLevel, max: maxZoomLevel);
328 if (m_zoomAtTarget) {
329 QVector3D targetPosition = item->graphPositionAt(
330 point: m_pinchHandler->centroid().position().toPoint());
331 item->setCameraZoomLevel(zoomLevel);
332
333 float diffAdj = 0.0f;
334
335 // If zooming in/out outside the graph, or zooming out after certain point,
336 // move towards the center.
337 if ((qAbs(t: targetPosition.x()) > 2.0f || qAbs(t: targetPosition.y()) > 2.0f
338 || qAbs(t: targetPosition.z()) > 2.0f)
339 || (m_pinchDiff > .0f && zoomLevel <= driftTowardCenterLevel)) {
340 targetPosition = QVector3D();
341 // Add some extra correction so that we actually reach the center
342 // eventually
343 diffAdj = wheelZoomDrift;
344 if (m_pinchDiff > .0f)
345 diffAdj *= 2.0f; // Correct towards center little more when zooming out
346 }
347
348 // Adjust camera towards the zoom point, attempting to keep the cursor at
349 // same graph point
350 QVector3D oldTarget = item->cameraTargetPosition();
351 QVector3D origDiff = targetPosition - oldTarget;
352 QVector3D diff = origDiff * m_pinchDiff + (origDiff.normalized() * diffAdj);
353 if (diff.length() > origDiff.length())
354 diff = origDiff;
355 item->setCameraTargetPosition(oldTarget + diff);
356 } else {
357 item->setCameraZoomLevel(zoomLevel);
358 }
359 m_pinchDiff = .0f;
360}
361
362void QGraphsInputHandler::hoverMoveEvent(QHoverEvent *event)
363{
364 Q_EMIT mouseMove(mousePos: event->oldPos());
365}
366

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtgraphs/src/graphs3d/input/qgraphsinputhandler.cpp