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 "qquickmaterialprogressbar_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 <QtQuick/qsgimagenode.h>
45#include <QtQuick/qquickwindow.h>
46#include <QtQuickControls2/private/qquickanimatednode_p.h>
47
48QT_BEGIN_NAMESPACE
49
50static const int PauseDuration = 520;
51static const int SlideDuration = 1240;
52static const int TotalDuration = SlideDuration + PauseDuration;
53
54class QQuickMaterialProgressBarNode : public QQuickAnimatedNode
55{
56public:
57 QQuickMaterialProgressBarNode(QQuickMaterialProgressBar *item);
58
59 void updateCurrentTime(int time) override;
60 void sync(QQuickItem *item) override;
61
62private:
63 void moveNode(QSGTransformNode *node, const QRectF &geometry, qreal progress);
64
65 bool m_indeterminate = false;
66 QEasingCurve m_easing = QEasingCurve::OutCubic;
67};
68
69QQuickMaterialProgressBarNode::QQuickMaterialProgressBarNode(QQuickMaterialProgressBar *item)
70 : QQuickAnimatedNode(item)
71{
72 setLoopCount(Infinite);
73 setDuration(TotalDuration);
74}
75
76void QQuickMaterialProgressBarNode::updateCurrentTime(int time)
77{
78 QSGRectangleNode *geometryNode = static_cast<QSGRectangleNode *>(firstChild());
79 Q_ASSERT(geometryNode->type() == QSGNode::GeometryNodeType);
80 const QRectF geometry = geometryNode->rect();
81
82 QSGTransformNode *firstNode = static_cast<QSGTransformNode *>(geometryNode->firstChild());
83 if (firstNode) {
84 Q_ASSERT(firstNode->type() == QSGNode::TransformNodeType);
85
86 const qreal progress = qMin<qreal>(1.0, static_cast<qreal>(time) / SlideDuration);
87 moveNode(static_cast<QSGTransformNode *>(firstNode), geometry, progress);
88 }
89
90 QSGTransformNode *secondNode = static_cast<QSGTransformNode *>(geometryNode->lastChild());
91 if (secondNode) {
92 Q_ASSERT(secondNode->type() == QSGNode::TransformNodeType);
93
94 const qreal progress = qMax<qreal>(0.0, static_cast<qreal>(time - PauseDuration) / SlideDuration);
95 moveNode(static_cast<QSGTransformNode *>(secondNode), geometry, progress);
96 }
97}
98
99void QQuickMaterialProgressBarNode::sync(QQuickItem *item)
100{
101 QQuickMaterialProgressBar *bar = static_cast<QQuickMaterialProgressBar *>(item);
102 if (m_indeterminate != bar->isIndeterminate()) {
103 m_indeterminate = bar->isIndeterminate();
104 if (m_indeterminate)
105 start();
106 else
107 stop();
108 }
109
110 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
111
112 QRectF bounds = item->boundingRect();
113 bounds.setHeight(item->implicitHeight());
114 bounds.moveTop((item->height() - bounds.height()) / 2.0);
115
116 QSGRectangleNode *geometryNode = static_cast<QSGRectangleNode *>(firstChild());
117 if (!geometryNode) {
118 geometryNode = item->window()->createRectangleNode();
119 geometryNode->setColor(Qt::transparent);
120 appendChildNode(geometryNode);
121 }
122 geometryNode->setRect(bounds);
123
124 const int count = m_indeterminate ? 2 : 1;
125 const qreal w = m_indeterminate ? 0 : bar->progress() * item->width();
126 const QRectF rect(0, bounds.y(), w, bounds.height());
127
128 QSGNode *transformNode = geometryNode->firstChild();
129 for (int i = 0; i < count; ++i) {
130 if (!transformNode) {
131 transformNode = new QSGTransformNode;
132 geometryNode->appendChildNode(transformNode);
133
134 QSGInternalRectangleNode *rectNode = d->sceneGraphContext()->createInternalRectangleNode();
135 rectNode->setAntialiasing(true);
136 transformNode->appendChildNode(rectNode);
137 }
138 Q_ASSERT(transformNode->type() == QSGNode::TransformNodeType);
139 static_cast<QSGTransformNode *>(transformNode)->setMatrix(QMatrix4x4());
140
141 QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(transformNode->firstChild());
142 Q_ASSERT(rectNode->type() == QSGNode::GeometryNodeType);
143
144 rectNode->setRect(rect);
145 rectNode->setColor(bar->color());
146 rectNode->update();
147
148 transformNode = transformNode->nextSibling();
149 }
150
151 while (transformNode) {
152 QSGNode *nextSibling = transformNode->nextSibling();
153 delete transformNode;
154 transformNode = nextSibling;
155 }
156}
157
158void QQuickMaterialProgressBarNode::moveNode(QSGTransformNode *transformNode, const QRectF &geometry, qreal progress)
159{
160 const qreal value = m_easing.valueForProgress(progress);
161 const qreal x = value * geometry.width();
162
163 QMatrix4x4 matrix;
164 matrix.translate(x, 0);
165 transformNode->setMatrix(matrix);
166
167 QSGInternalRectangleNode *rectNode = static_cast<QSGInternalRectangleNode *>(transformNode->firstChild());
168 Q_ASSERT(rectNode->type() == QSGNode::GeometryNodeType);
169
170 QRectF r = geometry;
171 r.setWidth(value * (geometry.width() - x));
172 rectNode->setRect(r);
173 rectNode->update();
174}
175
176QQuickMaterialProgressBar::QQuickMaterialProgressBar(QQuickItem *parent)
177 : QQuickItem(parent)
178{
179 setFlag(ItemHasContents);
180}
181
182QColor QQuickMaterialProgressBar::color() const
183{
184 return m_color;
185}
186
187void QQuickMaterialProgressBar::setColor(const QColor &color)
188{
189 if (color == m_color)
190 return;
191
192 m_color = color;
193 update();
194}
195
196qreal QQuickMaterialProgressBar::progress() const
197{
198 return m_progress;
199}
200
201void QQuickMaterialProgressBar::setProgress(qreal progress)
202{
203 if (progress == m_progress)
204 return;
205
206 m_progress = progress;
207 update();
208}
209
210bool QQuickMaterialProgressBar::isIndeterminate() const
211{
212 return m_indeterminate;
213}
214
215void QQuickMaterialProgressBar::setIndeterminate(bool indeterminate)
216{
217 if (indeterminate == m_indeterminate)
218 return;
219
220 m_indeterminate = indeterminate;
221 update();
222}
223
224void QQuickMaterialProgressBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
225{
226 QQuickItem::itemChange(change, data);
227 if (change == ItemVisibleHasChanged)
228 update();
229}
230
231QSGNode *QQuickMaterialProgressBar::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
232{
233 QQuickMaterialProgressBarNode *node = static_cast<QQuickMaterialProgressBarNode *>(oldNode);
234 if (isVisible() && width() > 0 && height() > 0) {
235 if (!node)
236 node = new QQuickMaterialProgressBarNode(this);
237 node->sync(this);
238 } else {
239 delete node;
240 node = nullptr;
241 }
242 return node;
243}
244
245QT_END_NAMESPACE
246