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

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