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 "qquickuniversalbusyindicator_p.h"
5
6#include <QtCore/qmath.h>
7#include <QtCore/qeasingcurve.h>
8#include <QtQuick/private/qquickitem_p.h>
9#include <QtQuick/private/qsgadaptationlayer_p.h>
10#include <QtQuickControls2Impl/private/qquickanimatednode_p.h>
11
12QT_BEGIN_NAMESPACE
13
14static const int PhaseCount = 6;
15static const int Interval = 167;
16static const int TotalDuration = 4052;
17
18class QQuickUniversalBusyIndicatorNode : public QQuickAnimatedNode
19{
20public:
21 QQuickUniversalBusyIndicatorNode(QQuickUniversalBusyIndicator *item);
22
23 void updateCurrentTime(int time) override;
24 void sync(QQuickItem *item) override;
25
26private:
27 struct Phase {
28 Phase() = default;
29 Phase(int d, qreal f, qreal t, QEasingCurve::Type c) : duration(d), from(f), to(t), curve(c) { }
30 int duration = 0;
31 qreal from = 0;
32 qreal to = 0;
33 QEasingCurve curve = QEasingCurve::Linear;
34 };
35
36 Phase m_phases[PhaseCount];
37};
38
39QQuickUniversalBusyIndicatorNode::QQuickUniversalBusyIndicatorNode(QQuickUniversalBusyIndicator *item)
40 : QQuickAnimatedNode(item)
41{
42 setLoopCount(Infinite);
43 setDuration(TotalDuration);
44 setCurrentTime(item->elapsed());
45
46 m_phases[0] = Phase(433, -110, 10, QEasingCurve::BezierSpline);
47 m_phases[1] = Phase(767, 10, 93, QEasingCurve::Linear );
48 m_phases[2] = Phase(417, 93, 205, QEasingCurve::BezierSpline);
49 m_phases[3] = Phase(400, 205, 357, QEasingCurve::BezierSpline);
50 m_phases[4] = Phase(766, 357, 439, QEasingCurve::Linear );
51 m_phases[5] = Phase(434, 439, 585, QEasingCurve::BezierSpline);
52
53 m_phases[0].curve.addCubicBezierSegment(c1: QPointF(0.02, 0.33), c2: QPointF(0.38, 0.77), endPoint: QPointF(1.00, 1.00));
54 m_phases[2].curve.addCubicBezierSegment(c1: QPointF(0.57, 0.17), c2: QPointF(0.95, 0.75), endPoint: QPointF(1.00, 1.00));
55 m_phases[3].curve.addCubicBezierSegment(c1: QPointF(0.00, 0.19), c2: QPointF(0.07, 0.72), endPoint: QPointF(1.00, 1.00));
56 m_phases[5].curve.addCubicBezierSegment(c1: QPointF(0.00, 0.00), c2: QPointF(0.95, 0.37), endPoint: QPointF(1.00, 1.00));
57}
58
59void QQuickUniversalBusyIndicatorNode::updateCurrentTime(int time)
60{
61 int nodeIndex = 0;
62 int count = childCount();
63 QSGTransformNode *transformNode = static_cast<QSGTransformNode *>(firstChild());
64 while (transformNode) {
65 Q_ASSERT(transformNode->type() == QSGNode::TransformNodeType);
66
67 QSGOpacityNode *opacityNode = static_cast<QSGOpacityNode *>(transformNode->firstChild());
68 Q_ASSERT(opacityNode->type() == QSGNode::OpacityNodeType);
69
70 int begin = nodeIndex * Interval;
71 int end = TotalDuration - (PhaseCount - nodeIndex - 1) * Interval;
72
73 bool visible = time >= begin && time <= end;
74 opacityNode->setOpacity(visible ? 1.0 : 0.0);
75
76 if (visible) {
77 int phaseIndex, remain = time, elapsed = 0;
78 for (phaseIndex = 0; phaseIndex < PhaseCount - 1; ++phaseIndex) {
79 if (remain <= m_phases[phaseIndex].duration + begin)
80 break;
81 remain -= m_phases[phaseIndex].duration;
82 elapsed += m_phases[phaseIndex].duration;
83 }
84
85 const Phase &phase = m_phases[phaseIndex];
86
87 qreal from = phase.from - nodeIndex * count;
88 qreal to = phase.to - nodeIndex * count;
89 qreal pos = time - elapsed - begin;
90
91 qreal value = phase.curve.valueForProgress(progress: pos / phase.duration);
92 qreal rotation = from + (to - from) * value;
93
94 QMatrix4x4 matrix;
95 matrix.rotate(angle: rotation, x: 0, y: 0, z: 1);
96 transformNode->setMatrix(matrix);
97 }
98
99 transformNode = static_cast<QSGTransformNode *>(transformNode->nextSibling());
100 ++nodeIndex;
101 }
102}
103
104void QQuickUniversalBusyIndicatorNode::sync(QQuickItem *item)
105{
106 QQuickUniversalBusyIndicator *indicator = static_cast<QQuickUniversalBusyIndicator *>(item);
107 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
108
109 QMatrix4x4 matrix;
110 matrix.translate(x: item->width() / 2, y: item->height() / 2);
111 setMatrix(matrix);
112
113 qreal size = qMin(a: item->width(), b: item->height());
114 qreal diameter = size / 10.0;
115 qreal radius = diameter / 2;
116 qreal offset = (size - diameter * 2) / M_PI;
117 const QRectF rect(offset, offset, diameter, diameter);
118
119 int count = indicator->count();
120 QSGNode *transformNode = firstChild();
121 for (int i = 0; i < count; ++i) {
122 if (!transformNode) {
123 transformNode = new QSGTransformNode;
124 appendChildNode(node: transformNode);
125
126 QSGOpacityNode *opacityNode = new QSGOpacityNode;
127 transformNode->appendChildNode(node: opacityNode);
128
129 QSGInternalRectangleNode *rectNode = d->sceneGraphContext()->createInternalRectangleNode();
130 rectNode->setAntialiasing(true);
131 opacityNode->appendChildNode(node: rectNode);
132 }
133
134 QSGNode *opacityNode = transformNode->firstChild();
135 Q_ASSERT(opacityNode->type() == QSGNode::OpacityNodeType);
136
137 QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(opacityNode->firstChild());
138 Q_ASSERT(rectNode->type() == QSGNode::GeometryNodeType);
139
140 rectNode->setRect(rect);
141 rectNode->setColor(indicator->color());
142 rectNode->setRadius(radius);
143 rectNode->update();
144
145 transformNode = transformNode->nextSibling();
146 }
147
148 while (transformNode) {
149 QSGNode *nextSibling = transformNode->nextSibling();
150 delete transformNode;
151 transformNode = nextSibling;
152 }
153}
154
155QQuickUniversalBusyIndicator::QQuickUniversalBusyIndicator(QQuickItem *parent)
156 : QQuickItem(parent)
157{
158 setFlag(flag: ItemHasContents);
159}
160
161int QQuickUniversalBusyIndicator::count() const
162{
163 return m_count;
164}
165
166void QQuickUniversalBusyIndicator::setCount(int count)
167{
168 if (m_count == count)
169 return;
170
171 m_count = count;
172 update();
173}
174
175QColor QQuickUniversalBusyIndicator::color() const
176{
177 return m_color;
178}
179
180void QQuickUniversalBusyIndicator::setColor(const QColor &color)
181{
182 if (m_color == color)
183 return;
184
185 m_color = color;
186 update();
187}
188
189int QQuickUniversalBusyIndicator::elapsed() const
190{
191 return m_elapsed;
192}
193
194void QQuickUniversalBusyIndicator::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
195{
196 QQuickItem::itemChange(change, data);
197 if (change == ItemVisibleHasChanged)
198 update();
199}
200
201QSGNode *QQuickUniversalBusyIndicator::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
202{
203 QQuickUniversalBusyIndicatorNode *node = static_cast<QQuickUniversalBusyIndicatorNode *>(oldNode);
204 if (isVisible() && width() > 0 && height() > 0) {
205 if (!node) {
206 node = new QQuickUniversalBusyIndicatorNode(this);
207 node->start();
208 }
209 node->sync(item: this);
210 } else {
211 m_elapsed = node ? node->currentTime() : 0;
212 delete node;
213 node = nullptr;
214 }
215 return node;
216}
217
218QT_END_NAMESPACE
219
220#include "moc_qquickuniversalbusyindicator_p.cpp"
221

source code of qtdeclarative/src/quickcontrols/universal/impl/qquickuniversalbusyindicator.cpp