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 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | Scroller::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 | |
21 | Scroller::~Scroller() |
22 | { |
23 | } |
24 | |
25 | void 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 | |
41 | void 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 | |
71 | void 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 | |
80 | void 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 | |
109 | void 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 | |
127 | void Scroller::startTicker(int interval) |
128 | { |
129 | m_state = Scroll; |
130 | m_ticker.start(interval); |
131 | } |
132 | |
133 | void Scroller::stopTicker() |
134 | { |
135 | m_state = Idle; |
136 | m_ticker.stop(); |
137 | } |
138 | |
139 | void 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 | |
158 | void 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 | |
173 | ScrollTicker::ScrollTicker(Scroller *scroller, QObject *parent) |
174 | : QObject(parent), |
175 | m_scroller(scroller) |
176 | { |
177 | |
178 | } |
179 | |
180 | void ScrollTicker::start(int interval) |
181 | { |
182 | if (!m_timer.isActive()) |
183 | m_timer.start(msec: interval, obj: this); |
184 | } |
185 | |
186 | void ScrollTicker::stop() |
187 | { |
188 | m_timer.stop(); |
189 | } |
190 | |
191 | void ScrollTicker::timerEvent(QTimerEvent *event) |
192 | { |
193 | Q_UNUSED(event); |
194 | m_scroller->scrollTick(); |
195 | } |
196 | |
197 | QT_END_NAMESPACE |
198 | |
199 | #include "moc_scroller_p.cpp" |
200 | |