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 "qquickdefaultbusyindicator_p.h"
38
39#include <QtQuick/private/qquickitem_p.h>
40#include <QtQuick/private/qsgadaptationlayer_p.h>
41#include <QtQuickControls2/private/qquickanimatednode_p.h>
42
43QT_BEGIN_NAMESPACE
44
45static const int CircleCount = 10;
46static const int TotalDuration = 100 * CircleCount * 2;
47static const QRgb TransparentColor = 0x00000000;
48
49static QPointF moveCircle(const QPointF &pos, qreal rotation, qreal distance)
50{
51 return pos - QTransform().rotate(a: rotation).map(p: QPointF(0, distance));
52}
53
54class QQuickDefaultBusyIndicatorNode : public QQuickAnimatedNode
55{
56public:
57 QQuickDefaultBusyIndicatorNode(QQuickDefaultBusyIndicator *item);
58
59 void updateCurrentTime(int time) override;
60 void sync(QQuickItem *item) override;
61
62private:
63 QColor m_pen;
64 QColor m_fill;
65};
66
67QQuickDefaultBusyIndicatorNode::QQuickDefaultBusyIndicatorNode(QQuickDefaultBusyIndicator *item)
68 : QQuickAnimatedNode(item)
69{
70 setLoopCount(Infinite);
71 setDuration(TotalDuration);
72 setCurrentTime(item->elapsed());
73
74 for (int i = 0; i < CircleCount; ++i) {
75 QSGTransformNode *transformNode = new QSGTransformNode;
76 appendChildNode(node: transformNode);
77
78 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
79 QSGInternalRectangleNode *rectNode = d->sceneGraphContext()->createInternalRectangleNode();
80 rectNode->setAntialiasing(true);
81 transformNode->appendChildNode(node: rectNode);
82 }
83}
84
85void QQuickDefaultBusyIndicatorNode::updateCurrentTime(int time)
86{
87 const qreal percentageComplete = time / qreal(TotalDuration);
88 const qreal firstPhaseProgress = percentageComplete <= 0.5 ? percentageComplete * 2 : 0;
89 const qreal secondPhaseProgress = percentageComplete > 0.5 ? (percentageComplete - 0.5) * 2 : 0;
90
91 QSGTransformNode *transformNode = static_cast<QSGTransformNode*>(firstChild());
92 Q_ASSERT(transformNode->type() == QSGNode::TransformNodeType);
93 for (int i = 0; i < CircleCount; ++i) {
94 QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode*>(transformNode->firstChild());
95 Q_ASSERT(rectNode->type() == QSGNode::GeometryNodeType);
96
97 const bool fill = (firstPhaseProgress > qreal(i) / CircleCount) || (secondPhaseProgress > 0 && secondPhaseProgress < qreal(i) / CircleCount);
98 rectNode->setColor(fill ? m_fill : QColor::fromRgba(rgba: TransparentColor));
99 rectNode->setPenColor(m_pen);
100 rectNode->setPenWidth(1);
101 rectNode->update();
102
103 transformNode = static_cast<QSGTransformNode*>(transformNode->nextSibling());
104 }
105}
106
107void QQuickDefaultBusyIndicatorNode::sync(QQuickItem *item)
108{
109 const qreal w = item->width();
110 const qreal h = item->height();
111 const qreal sz = qMin(a: w, b: h);
112 const qreal dx = (w - sz) / 2;
113 const qreal dy = (h - sz) / 2;
114 const int circleRadius = sz / 12;
115
116 m_pen = static_cast<QQuickDefaultBusyIndicator *>(item)->pen();
117 m_fill = static_cast<QQuickDefaultBusyIndicator *>(item)->fill();
118
119 QSGTransformNode *transformNode = static_cast<QSGTransformNode *>(firstChild());
120 for (int i = 0; i < CircleCount; ++i) {
121 Q_ASSERT(transformNode->type() == QSGNode::TransformNodeType);
122
123 QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(transformNode->firstChild());
124 Q_ASSERT(rectNode->type() == QSGNode::GeometryNodeType);
125
126 QPointF pos = QPointF(sz / 2 - circleRadius, sz / 2 - circleRadius);
127 pos = moveCircle(pos, rotation: 360.0 / CircleCount * i, distance: sz / 2 - circleRadius);
128
129 QMatrix4x4 m;
130 m.translate(x: dx + pos.x(), y: dy + pos.y());
131 transformNode->setMatrix(m);
132
133 rectNode->setRect(QRectF(QPointF(), QSizeF(circleRadius * 2, circleRadius * 2)));
134 rectNode->setRadius(circleRadius);
135
136 transformNode = static_cast<QSGTransformNode *>(transformNode->nextSibling());
137 }
138}
139
140QQuickDefaultBusyIndicator::QQuickDefaultBusyIndicator(QQuickItem *parent) :
141 QQuickItem(parent)
142{
143 setFlag(flag: ItemHasContents);
144}
145
146QColor QQuickDefaultBusyIndicator::pen() const
147{
148 return m_pen;
149}
150
151void QQuickDefaultBusyIndicator::setPen(const QColor &pen)
152{
153 if (pen == m_pen)
154 return;
155
156 m_pen = pen;
157 update();
158}
159
160QColor QQuickDefaultBusyIndicator::fill() const
161{
162 return m_fill;
163}
164
165void QQuickDefaultBusyIndicator::setFill(const QColor &fill)
166{
167 if (fill == m_fill)
168 return;
169
170 m_fill = fill;
171 update();
172}
173
174bool QQuickDefaultBusyIndicator::isRunning() const
175{
176 return isVisible();
177}
178
179void QQuickDefaultBusyIndicator::setRunning(bool running)
180{
181 if (running)
182 setVisible(true);
183}
184
185int QQuickDefaultBusyIndicator::elapsed() const
186{
187 return m_elapsed;
188}
189
190void QQuickDefaultBusyIndicator::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
191{
192 QQuickItem::itemChange(change, data);
193 switch (change) {
194 case ItemOpacityHasChanged:
195 if (qFuzzyIsNull(d: data.realValue))
196 setVisible(false);
197 break;
198 case ItemVisibleHasChanged:
199 update();
200 break;
201 default:
202 break;
203 }
204}
205
206QSGNode *QQuickDefaultBusyIndicator::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
207{
208 QQuickDefaultBusyIndicatorNode *node = static_cast<QQuickDefaultBusyIndicatorNode *>(oldNode);
209 if (isRunning() && width() > 0 && height() > 0) {
210 if (!node) {
211 node = new QQuickDefaultBusyIndicatorNode(this);
212 node->start();
213 }
214 node->sync(item: this);
215 } else {
216 m_elapsed = node ? node->currentTime() : 0;
217 delete node;
218 node = nullptr;
219 }
220 return node;
221}
222
223QT_END_NAMESPACE
224

source code of qtquickcontrols2/src/imports/controls/qquickdefaultbusyindicator.cpp