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

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