1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <private/scroller_p.h>
5#include <QtCharts/QLegend>
6#include <QtWidgets/QGraphicsSceneMouseEvent>
7#include <QtCore/QDebug>
8
9QT_BEGIN_NAMESPACE
10
11Scroller::Scroller()
12 : m_ticker(this),
13 m_timeTresholdMin(50),
14 m_timeTresholdMax(300),
15 m_state(Idle),
16 m_treshold(10)
17{
18
19}
20
21Scroller::~Scroller()
22{
23}
24
25void Scroller::move(const QPointF &delta)
26{
27 switch (m_state) {
28 case Pressed:
29 m_timeStamp.restart();
30 break;
31 case Scroll:
32 stopTicker();
33 m_timeStamp.restart();
34 break;
35 default:
36 break;
37 }
38 setOffset(offset() - delta);
39}
40
41void Scroller::scrollTo(const QPointF &delta)
42{
43 // Starts scrolling, if at least m_timeTresholdMin msecs has gone since timestamp
44 // current time is no more than m_timeTresholdMax from timestamp
45
46 if ((m_timeStamp.elapsed() > m_timeTresholdMin) && (m_timeStamp.elapsed() < m_timeTresholdMax)) {
47 // Release was quick enough. Start scrolling.
48 qreal interval = 25;
49 qreal time = m_timeStamp.elapsed();
50 if (qFuzzyCompare(p1: time, p2: 0)) {
51 m_speed = delta / 5;
52 } else {
53 m_speed = delta * interval / time;
54 }
55
56 qreal fraction = qMax(a: qAbs(t: m_speed.x()), b: qAbs(t: m_speed.y()));
57
58 if (!qFuzzyCompare(p1: fraction, p2: 0)) {
59 m_fraction.setX(qAbs(t: m_speed.x() / fraction));
60 m_fraction.setY(qAbs(t: m_speed.y() / fraction));
61 } else {
62 m_fraction.setX(1);
63 m_fraction.setY(1);
64 }
65 startTicker(interval);
66 } else {
67 stopTicker(); // Stop ticker, if one is running.
68 }
69}
70
71void Scroller::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
72{
73 stopTicker();
74 m_pressPos = event->screenPos();
75 m_lastPos = m_pressPos;
76 m_state = Pressed;
77 event->accept();
78}
79
80void Scroller::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
81{
82 QPointF delta = event->screenPos() - m_lastPos;
83
84 switch (m_state) {
85 case Pressed: {
86 // calculate treshold. If enough, change to move state and send out move deltas.
87 if (qAbs(t: delta.x()) > m_treshold || qAbs(t: delta.y()) > m_treshold) {
88 m_lastPos = event->screenPos();
89 move(delta);
90 m_state = Move;
91 }
92 event->accept();
93 break;
94 }
95 case Move: {
96 m_lastPos = event->screenPos();
97 move(delta);
98 event->accept();
99 break;
100 }
101 case Idle:
102 default: {
103 event->ignore();
104 break;
105 }
106 }
107}
108
109void Scroller::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
110{
111 switch (m_state) {
112 case Move:
113 {
114 scrollTo(delta: m_lastPos - m_pressPos);
115 event->accept();
116 break;
117 }
118 default:
119 {
120 m_state = Idle;
121 event->ignore();
122 break;
123 }
124 }
125}
126
127void Scroller::startTicker(int interval)
128{
129 m_state = Scroll;
130 m_ticker.start(interval);
131}
132
133void Scroller::stopTicker()
134{
135 m_state = Idle;
136 m_ticker.stop();
137}
138
139void Scroller::scrollTick()
140{
141 switch (m_state) {
142 case Scroll:
143 lowerSpeed(speed&: m_speed);
144 setOffset(offset() - m_speed);
145 if (m_speed == QPointF(0, 0)) {
146 m_state = Idle;
147 m_ticker.stop();
148 }
149 break;
150 default:
151 qWarning() << __FUNCTION__ << "Scroller unexpected state" << m_state;
152 m_ticker.stop();
153 m_state = Idle;
154 break;
155 }
156}
157
158void Scroller::lowerSpeed(QPointF &speed, qreal maxSpeed)
159{
160 qreal x = qBound(min: -maxSpeed, val: speed.x(), max: maxSpeed);
161 qreal y = qBound(min: -maxSpeed, val: speed.y(), max: maxSpeed);
162
163 x = (x == 0) ? x :
164 (x > 0) ? qMax(a: qreal(0), b: x - m_fraction.x()) : qMin(a: qreal(0), b: x + m_fraction.x());
165 y = (y == 0) ? y :
166 (y > 0) ? qMax(a: qreal(0), b: y - m_fraction.y()) : qMin(a: qreal(0), b: y + m_fraction.y());
167 speed.setX(x);
168 speed.setY(y);
169}
170
171/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
172
173ScrollTicker::ScrollTicker(Scroller *scroller, QObject *parent)
174 : QObject(parent),
175 m_scroller(scroller)
176{
177
178}
179
180void ScrollTicker::start(int interval)
181{
182 if (!m_timer.isActive())
183 m_timer.start(msec: interval, obj: this);
184}
185
186void ScrollTicker::stop()
187{
188 m_timer.stop();
189}
190
191void ScrollTicker::timerEvent(QTimerEvent *event)
192{
193 Q_UNUSED(event);
194 m_scroller->scrollTick();
195}
196
197QT_END_NAMESPACE
198
199#include "moc_scroller_p.cpp"
200

source code of qtcharts/src/charts/scroller.cpp