1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qquickuniversalprogressbar_p.h"
38
39#include <QtCore/qmath.h>
40#include <QtCore/qeasingcurve.h>
41#include <QtQuick/private/qquickitem_p.h>
42#include <QtQuick/private/qsgadaptationlayer_p.h>
43#include <QtQuick/qsgrectanglenode.h>
44#include <QtQuickControls2/private/qquickanimatednode_p.h>
45
46QT_BEGIN_NAMESPACE
47
48static const int PhaseCount = 4;
49static const int EllipseCount = 5;
50static const int Interval = 167;
51static const int TotalDuration = 3917;
52static const int VisibleDuration = 3000;
53static const qreal EllipseDiameter = 4;
54static const qreal EllipseOffset = 4;
55static const qreal ContainerAnimationStartPosition = -34; // absolute
56static const qreal ContainerAnimationEndPosition = 0.435222; // relative
57static const qreal EllipseAnimationWellPosition = 0.333333333333333; // relative
58static const qreal EllipseAnimationEndPosition = 0.666666666666667; // relative
59
60class QQuickUniversalProgressBarNode : public QQuickAnimatedNode
61{
62public:
63 QQuickUniversalProgressBarNode(QQuickUniversalProgressBar *item);
64
65 void updateCurrentTime(int time) override;
66 void sync(QQuickItem *item) override;
67
68private:
69 struct Phase {
70 Phase() = default;
71 Phase(int d, qreal f, qreal t) : duration(d), from(f), to(t) { }
72 int duration = 0;
73 qreal from = 0;
74 qreal to = 0;
75 };
76
77 bool m_indeterminate = false;
78 Phase m_borderPhases[PhaseCount];
79 Phase m_ellipsePhases[PhaseCount];
80};
81
82QQuickUniversalProgressBarNode::QQuickUniversalProgressBarNode(QQuickUniversalProgressBar *item)
83 : QQuickAnimatedNode(item)
84{
85 setLoopCount(Infinite);
86 setDuration(TotalDuration);
87
88 m_borderPhases[0] = Phase( 500, -50, 0);
89 m_borderPhases[1] = Phase(1500, 0, 0);
90 m_borderPhases[2] = Phase(1000, 0, 100);
91 m_borderPhases[3] = Phase( 917, 100, 100);
92
93 m_ellipsePhases[0] = Phase(1000, 0, EllipseAnimationWellPosition);
94 m_ellipsePhases[1] = Phase(1000, EllipseAnimationWellPosition, EllipseAnimationWellPosition);
95 m_ellipsePhases[2] = Phase(1000, EllipseAnimationWellPosition, EllipseAnimationEndPosition);
96 m_ellipsePhases[3] = Phase(1000, EllipseAnimationWellPosition, EllipseAnimationEndPosition);
97}
98
99void QQuickUniversalProgressBarNode::updateCurrentTime(int time)
100{
101 QSGRectangleNode *geometryNode = static_cast<QSGRectangleNode *>(firstChild());
102 Q_ASSERT(!geometryNode || geometryNode->type() == QSGNode::GeometryNodeType);
103 if (!geometryNode)
104 return;
105
106 QSGTransformNode *gridNode = static_cast<QSGTransformNode *>(geometryNode->firstChild());
107 Q_ASSERT(!gridNode || gridNode->type() == QSGNode::TransformNodeType);
108 if (!gridNode)
109 return;
110
111 qreal width = geometryNode->rect().width();
112 {
113 qreal from = ContainerAnimationStartPosition;
114 qreal to = from + ContainerAnimationEndPosition * width;
115 qreal progress = static_cast<qreal>(time) / TotalDuration;
116 qreal dx = from + (to - from) * progress;
117
118 QMatrix4x4 matrix;
119 matrix.translate(x: dx, y: 0);
120 gridNode->setMatrix(matrix);
121 }
122
123 int nodeIndex = 0;
124 QSGTransformNode *borderNode = static_cast<QSGTransformNode *>(gridNode->firstChild());
125 while (borderNode) {
126 Q_ASSERT(borderNode->type() == QSGNode::TransformNodeType);
127
128 QSGTransformNode *ellipseNode = static_cast<QSGTransformNode *>(borderNode->firstChild());
129 Q_ASSERT(ellipseNode->type() == QSGNode::TransformNodeType);
130
131 QSGOpacityNode *opacityNode = static_cast<QSGOpacityNode *>(ellipseNode->firstChild());
132 Q_ASSERT(opacityNode->type() == QSGNode::OpacityNodeType);
133
134 int begin = nodeIndex * Interval;
135 int end = VisibleDuration + nodeIndex * Interval;
136
137 bool visible = time >= begin && time <= end;
138 opacityNode->setOpacity(visible ? 1.0 : 0.0);
139
140 if (visible) {
141 {
142 int phaseIndex, remain = time, elapsed = 0;
143 for (phaseIndex = 0; phaseIndex < PhaseCount - 1; ++phaseIndex) {
144 if (remain <= m_borderPhases[phaseIndex].duration + begin)
145 break;
146 remain -= m_borderPhases[phaseIndex].duration;
147 elapsed += m_borderPhases[phaseIndex].duration;
148 }
149
150 const Phase &phase = m_borderPhases[phaseIndex];
151
152 qreal pos = time - elapsed - begin;
153 qreal progress = pos / phase.duration;
154 qreal dx = phase.from + (phase.to - phase.from) * progress;
155
156 QMatrix4x4 matrix;
157 matrix.translate(x: dx, y: 0);
158 borderNode->setMatrix(matrix);
159 }
160
161 {
162 QEasingCurve curve(QEasingCurve::BezierSpline);
163 curve.addCubicBezierSegment(c1: QPointF(0.4, 0.0), c2: QPointF(0.6, 1.0), endPoint: QPointF(1.0, 1.0));
164
165 int phaseIndex, remain = time, elapsed = 0;
166 for (phaseIndex = 0; phaseIndex < PhaseCount - 1; ++phaseIndex) {
167 if (remain <= m_ellipsePhases[phaseIndex].duration + begin)
168 break;
169 remain -= m_ellipsePhases[phaseIndex].duration;
170 elapsed += m_ellipsePhases[phaseIndex].duration;
171 }
172
173 const Phase &phase = m_ellipsePhases[phaseIndex];
174
175 qreal from = phase.from * width;
176 qreal to = phase.to * width;
177 qreal pos = time - elapsed - begin;
178 qreal progress = curve.valueForProgress(progress: pos / phase.duration);
179 qreal dx = from + (to - from) * progress;
180
181 QMatrix4x4 matrix;
182 matrix.translate(x: dx, y: 0);
183 ellipseNode->setMatrix(matrix);
184 }
185 }
186
187 borderNode = static_cast<QSGTransformNode *>(borderNode->nextSibling());
188 ++nodeIndex;
189 }
190}
191
192void QQuickUniversalProgressBarNode::sync(QQuickItem *item)
193{
194 QQuickUniversalProgressBar *bar = static_cast<QQuickUniversalProgressBar *>(item);
195 if (m_indeterminate != bar->isIndeterminate()) {
196 m_indeterminate = bar->isIndeterminate();
197 if (m_indeterminate)
198 start();
199 else
200 stop();
201 }
202
203 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
204
205 QRectF bounds = item->boundingRect();
206 bounds.setHeight(item->implicitHeight());
207 bounds.moveTop(pos: (item->height() - bounds.height()) / 2.0);
208 if (!m_indeterminate)
209 bounds.setWidth(bar->progress() * bounds.width());
210
211 QSGRectangleNode *geometryNode = static_cast<QSGRectangleNode *>(firstChild());
212 if (!geometryNode) {
213 geometryNode = item->window()->createRectangleNode();
214 appendChildNode(node: geometryNode);
215 }
216 geometryNode->setRect(bounds);
217 geometryNode->setColor(m_indeterminate ? Qt::transparent : bar->color());
218
219 if (!m_indeterminate) {
220 while (QSGNode *node = geometryNode->firstChild())
221 delete node;
222 return;
223 }
224
225 QSGTransformNode *gridNode = static_cast<QSGTransformNode *>(geometryNode->firstChild());
226 if (!gridNode) {
227 gridNode = new QSGTransformNode;
228 geometryNode->appendChildNode(node: gridNode);
229 }
230 Q_ASSERT(gridNode->type() == QSGNode::TransformNodeType);
231
232 QSGNode *borderNode = gridNode->firstChild();
233 for (int i = 0; i < EllipseCount; ++i) {
234 if (!borderNode) {
235 borderNode = new QSGTransformNode;
236 gridNode->appendChildNode(node: borderNode);
237
238 QSGTransformNode *ellipseNode = new QSGTransformNode;
239 borderNode->appendChildNode(node: ellipseNode);
240
241 QSGOpacityNode *opacityNode = new QSGOpacityNode;
242 ellipseNode->appendChildNode(node: opacityNode);
243
244 QSGInternalRectangleNode *rectNode = d->sceneGraphContext()->createInternalRectangleNode();
245 rectNode->setAntialiasing(true);
246 rectNode->setRadius(EllipseDiameter / 2);
247 opacityNode->appendChildNode(node: rectNode);
248 }
249 Q_ASSERT(borderNode->type() == QSGNode::TransformNodeType);
250
251 QSGNode *ellipseNode = borderNode->firstChild();
252 Q_ASSERT(ellipseNode->type() == QSGNode::TransformNodeType);
253
254 QSGNode *opacityNode = ellipseNode->firstChild();
255 Q_ASSERT(opacityNode->type() == QSGNode::OpacityNodeType);
256
257 QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(opacityNode->firstChild());
258 Q_ASSERT(rectNode->type() == QSGNode::GeometryNodeType);
259
260 rectNode->setRect(QRectF((EllipseCount - i - 1) * (EllipseDiameter + EllipseOffset), (item->height() - EllipseDiameter) / 2, EllipseDiameter, EllipseDiameter));
261 rectNode->setColor(bar->color());
262 rectNode->update();
263
264 borderNode = borderNode->nextSibling();
265 }
266}
267
268QQuickUniversalProgressBar::QQuickUniversalProgressBar(QQuickItem *parent)
269 : QQuickItem(parent)
270{
271 setFlag(flag: ItemHasContents);
272}
273
274QColor QQuickUniversalProgressBar::color() const
275{
276 return m_color;
277}
278
279void QQuickUniversalProgressBar::setColor(const QColor &color)
280{
281 if (m_color == color)
282 return;
283
284 m_color = color;
285 update();
286}
287
288qreal QQuickUniversalProgressBar::progress() const
289{
290 return m_progress;
291}
292
293void QQuickUniversalProgressBar::setProgress(qreal progress)
294{
295 if (progress == m_progress)
296 return;
297
298 m_progress = progress;
299 update();
300}
301
302bool QQuickUniversalProgressBar::isIndeterminate() const
303{
304 return m_indeterminate;
305}
306
307void QQuickUniversalProgressBar::setIndeterminate(bool indeterminate)
308{
309 if (indeterminate == m_indeterminate)
310 return;
311
312 m_indeterminate = indeterminate;
313 setClip(m_indeterminate);
314 update();
315}
316
317void QQuickUniversalProgressBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
318{
319 QQuickItem::itemChange(change, data);
320 if (change == ItemVisibleHasChanged)
321 update();
322}
323
324QSGNode *QQuickUniversalProgressBar::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
325{
326 QQuickUniversalProgressBarNode *node = static_cast<QQuickUniversalProgressBarNode *>(oldNode);
327 if (isVisible() && width() > 0 && height() > 0) {
328 if (!node)
329 node = new QQuickUniversalProgressBarNode(this);
330 node->sync(item: this);
331 } else {
332 delete node;
333 node = nullptr;
334 }
335 return node;
336}
337
338QT_END_NAMESPACE
339

source code of qtquickcontrols2/src/imports/controls/universal/qquickuniversalprogressbar.cpp