1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickbasicprogressbar_p.h"
5
6#include <QtCore/qeasingcurve.h>
7#include <QtQuick/private/qquickitem_p.h>
8#include <QtQuick/private/qsgadaptationlayer_p.h>
9#include <QtQuickControls2Impl/private/qquickanimatednode_p.h>
10
11QT_BEGIN_NAMESPACE
12
13static const int Blocks = 4;
14static const int BlockWidth = 16;
15static const int BlockRestingSpacing = 4;
16static const int BlockMovingSpacing = 48;
17static const int BlockSpan = Blocks * (BlockWidth + BlockRestingSpacing) - BlockRestingSpacing;
18static const int QbpbTotalDuration = 4000;
19static const int SecondPhaseStart = QbpbTotalDuration * 0.4;
20static const int ThirdPhaseStart = QbpbTotalDuration * 0.6;
21
22static inline qreal blockStartX(int blockIndex)
23{
24 return ((blockIndex + 1) * -BlockWidth) - (blockIndex * BlockMovingSpacing);
25}
26
27static inline qreal blockRestX(int blockIndex, qreal availableWidth)
28{
29 const qreal spanRightEdgePos = availableWidth / 2 + BlockSpan / 2.0;
30 return spanRightEdgePos - (blockIndex + 1) * BlockWidth - (blockIndex * BlockRestingSpacing);
31}
32
33static inline qreal blockEndX(int blockIndex, qreal availableWidth)
34{
35 return availableWidth - blockStartX(blockIndex: Blocks - 1 - blockIndex) - BlockWidth;
36}
37
38class QQuickBasicProgressBarNode : public QQuickAnimatedNode
39{
40public:
41 QQuickBasicProgressBarNode(QQuickBasicProgressBar *item);
42
43 void updateCurrentTime(int time) override;
44 void sync(QQuickItem *item) override;
45
46private:
47 bool m_indeterminate = false;
48 qreal m_pixelsPerSecond = 0;
49};
50
51QQuickBasicProgressBarNode::QQuickBasicProgressBarNode(QQuickBasicProgressBar *item)
52 : QQuickAnimatedNode(item),
53 m_pixelsPerSecond(item->width())
54{
55 setLoopCount(Infinite);
56 setDuration(QbpbTotalDuration);
57}
58
59void QQuickBasicProgressBarNode::updateCurrentTime(int time)
60{
61 QSGTransformNode *transformNode = static_cast<QSGTransformNode*>(firstChild());
62 for (int i = 0; i < Blocks; ++i) {
63 Q_ASSERT(transformNode->type() == QSGNode::TransformNodeType);
64
65 QMatrix4x4 m;
66 const qreal restX = blockRestX(blockIndex: i, availableWidth: m_pixelsPerSecond);
67 const qreal timeInSeconds = time / 1000.0;
68
69 if (time < SecondPhaseStart) {
70 // Move into the resting position for the first phase.
71 QEasingCurve easingCurve(QEasingCurve::InQuad);
72 const qreal easedCompletion = easingCurve.valueForProgress(progress: time / qreal(SecondPhaseStart));
73 const qreal distance = m_pixelsPerSecond * (easedCompletion * (SecondPhaseStart / 1000.0));
74 const qreal position = blockStartX(blockIndex: i) + distance;
75 const qreal destination = restX;
76 m.translate(x: qMin(a: position, b: destination), y: 0);
77 } else if (time < ThirdPhaseStart) {
78 // Stay in the same position for the second phase.
79 m.translate(x: restX, y: 0);
80 } else {
81 // Move out of view for the third phase.
82 const int thirdPhaseSubKickoff = (BlockMovingSpacing / m_pixelsPerSecond) * 1000;
83 const int subphase = (time - ThirdPhaseStart) / thirdPhaseSubKickoff;
84 // If we're not at this subphase yet, don't try to animate movement,
85 // because it will be incorrect.
86 if (subphase < i)
87 return;
88
89 const qreal timeSinceSecondPhase = timeInSeconds - (ThirdPhaseStart / 1000.0);
90 // We only want to start keeping track of time once our subphase has started,
91 // otherwise we move too much because we account for time that has already elapsed.
92 // For example, if we were 60 milliseconds into the third subphase:
93 //
94 // 0 ..... 1 ..... 2 ...
95 // 100 100 60
96 //
97 // i == 0, timeSinceOurKickoff == 260
98 // i == 1, timeSinceOurKickoff == 160
99 // i == 2, timeSinceOurKickoff == 60
100 const qreal timeSinceOurKickoff = timeSinceSecondPhase - (thirdPhaseSubKickoff / 1000.0 * i);
101 const qreal position = restX + (m_pixelsPerSecond * (timeSinceOurKickoff));
102 const qreal destination = blockEndX(blockIndex: i, availableWidth: m_pixelsPerSecond);
103 m.translate(x: qMin(a: position, b: destination), y: 0);
104 }
105
106 transformNode->setMatrix(m);
107
108 transformNode = static_cast<QSGTransformNode*>(transformNode->nextSibling());
109 }
110}
111
112void QQuickBasicProgressBarNode::sync(QQuickItem *item)
113{
114 QQuickBasicProgressBar *bar = static_cast<QQuickBasicProgressBar *>(item);
115 if (m_indeterminate != bar->isIndeterminate()) {
116 m_indeterminate = bar->isIndeterminate();
117 if (m_indeterminate)
118 start();
119 else
120 stop();
121 }
122 m_pixelsPerSecond = item->width();
123
124 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
125
126 QMatrix4x4 m;
127 m.translate(x: 0, y: (item->height() - item->implicitHeight()) / 2);
128 setMatrix(m);
129
130 if (m_indeterminate) {
131 if (childCount() != Blocks) {
132 // This was previously a regular progress bar; remove the old nodes.
133 removeAllChildNodes();
134 }
135
136 QSGTransformNode *transformNode = static_cast<QSGTransformNode*>(firstChild());
137 for (int i = 0; i < Blocks; ++i) {
138 if (!transformNode) {
139 transformNode = new QSGTransformNode;
140 appendChildNode(node: transformNode);
141 }
142
143 QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode*>(transformNode->firstChild());
144 if (!rectNode) {
145 rectNode = d->sceneGraphContext()->createInternalRectangleNode();
146 rectNode->setColor(bar->color());
147 transformNode->appendChildNode(node: rectNode);
148 }
149
150 QMatrix4x4 m;
151 m.translate(x: blockStartX(blockIndex: i), y: 0);
152 transformNode->setMatrix(m);
153
154 rectNode->setRect(QRectF(QPointF(0, 0), QSizeF(BlockWidth, item->implicitHeight())));
155 rectNode->update();
156
157 transformNode = static_cast<QSGTransformNode *>(transformNode->nextSibling());
158 }
159 } else {
160 if (childCount() > 1) {
161 // This was previously an indeterminate progress bar; remove the old nodes.
162 removeAllChildNodes();
163 }
164
165 QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(firstChild());
166 if (!rectNode) {
167 rectNode = d->sceneGraphContext()->createInternalRectangleNode();
168 rectNode->setColor(bar->color());
169 appendChildNode(node: rectNode);
170 }
171
172 rectNode->setRect(QRectF(QPointF(0, 0), QSizeF(bar->progress() * item->width(), item->implicitHeight())));
173 rectNode->update();
174 }
175}
176
177QQuickBasicProgressBar::QQuickBasicProgressBar(QQuickItem *parent) :
178 QQuickItem(parent)
179{
180 setFlag(flag: ItemHasContents);
181}
182
183qreal QQuickBasicProgressBar::progress() const
184{
185 return m_progress;
186}
187
188void QQuickBasicProgressBar::setProgress(qreal progress)
189{
190 if (progress == m_progress)
191 return;
192
193 m_progress = progress;
194 update();
195}
196
197bool QQuickBasicProgressBar::isIndeterminate() const
198{
199 return m_indeterminate;
200}
201
202void QQuickBasicProgressBar::setIndeterminate(bool indeterminate)
203{
204 if (indeterminate == m_indeterminate)
205 return;
206
207 m_indeterminate = indeterminate;
208 setClip(m_indeterminate);
209 update();
210}
211
212QColor QQuickBasicProgressBar::color() const
213{
214 return m_color;
215}
216
217void QQuickBasicProgressBar::setColor(const QColor &color)
218{
219 if (color == m_color)
220 return;
221
222 m_color = color;
223 update();
224}
225
226void QQuickBasicProgressBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
227{
228 QQuickItem::itemChange(change, data);
229 if (change == ItemVisibleHasChanged)
230 update();
231}
232
233QSGNode *QQuickBasicProgressBar::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
234{
235 QQuickBasicProgressBarNode *node = static_cast<QQuickBasicProgressBarNode *>(oldNode);
236 if (isVisible() && width() > 0 && height() > 0) {
237 if (!node)
238 node = new QQuickBasicProgressBarNode(this);
239 node->sync(item: this);
240 } else {
241 delete node;
242 node = nullptr;
243 }
244 return node;
245}
246
247QT_END_NAMESPACE
248
249#include "moc_qquickbasicprogressbar_p.cpp"
250

source code of qtdeclarative/src/quickcontrols/basic/impl/qquickbasicprogressbar.cpp