1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickitemparticle_p.h"
41#include <QtQuick/qsgnode.h>
42#include <QTimer>
43#include <QQmlComponent>
44#include <QDebug>
45
46QT_BEGIN_NAMESPACE
47
48/*!
49 \qmltype ItemParticle
50 \instantiates QQuickItemParticle
51 \inqmlmodule QtQuick.Particles
52 \inherits ParticlePainter
53 \brief For specifying a delegate to paint particles.
54 \ingroup qtquick-particles
55
56*/
57
58
59/*!
60 \qmlmethod QtQuick.Particles::ItemParticle::freeze(Item item)
61
62 Suspends the flow of time for the logical particle which \a item represents,
63 allowing you to control its movement.
64*/
65
66/*!
67 \qmlmethod QtQuick.Particles::ItemParticle::unfreeze(Item item)
68
69 Restarts the flow of time for the logical particle which \a item represents,
70 allowing it to be moved by the particle system again.
71*/
72
73/*!
74 \qmlmethod QtQuick.Particles::ItemParticle::take(Item item, bool prioritize)
75
76 Asks the ItemParticle to take over control of \a item positioning temporarily.
77 It will follow the movement of a logical particle when one is available.
78
79 By default items form a queue when waiting for a logical particle, but if
80 \a prioritize is \c true, then it will go immediately to the head of the
81 queue.
82
83 ItemParticle does not take ownership of the item, and will relinquish
84 control when the logical particle expires. Commonly at this point you will
85 want to put it back in the queue, you can do this with the below line in
86 the delegate definition:
87
88 \code
89 ItemParticle.onDetached: itemParticleInstance.take(delegateRootItem);
90 \endcode
91
92 or delete it, such as with the below line in the delegate definition:
93
94 \code
95 ItemParticle.onDetached: delegateRootItem.destroy();
96 \endcode
97*/
98
99/*!
100 \qmlmethod QtQuick.Particles::ItemParticle::give(Item item)
101
102 Orders the ItemParticle to give you control of the \a item. It will cease
103 controlling it and the item will lose its association to the logical
104 particle.
105*/
106
107/*!
108 \qmlproperty bool QtQuick.Particles::ItemParticle::fade
109
110 If true, the item will automatically be faded in and out
111 at the ends of its lifetime. If false, you will have to
112 implement any entry effect yourself.
113
114 Default is true.
115*/
116/*!
117 \qmlproperty Component QtQuick.Particles::ItemParticle::delegate
118
119 An instance of the delegate will be created for every logical particle, and
120 moved along with it. As an alternative to using delegate, you can create
121 Item instances yourself and hand them to the ItemParticle to move using the
122 \l take method.
123
124 Any delegate instances created by ItemParticle will be destroyed when
125 the logical particle expires.
126*/
127
128QQuickItemParticle::QQuickItemParticle(QQuickItem *parent) :
129 QQuickParticlePainter(parent), m_fade(true), m_lastT(0), m_activeCount(0), m_delegate(nullptr)
130{
131 setFlag(flag: QQuickItem::ItemHasContents);
132 clock = new Clock(this);
133 clock->start();
134}
135
136QQuickItemParticle::~QQuickItemParticle()
137{
138 delete clock;
139 qDeleteAll(c: m_managed);
140}
141
142void QQuickItemParticle::freeze(QQuickItem* item)
143{
144 m_stasis << item;
145}
146
147
148void QQuickItemParticle::unfreeze(QQuickItem* item)
149{
150 m_stasis.remove(value: item);
151}
152
153void QQuickItemParticle::take(QQuickItem *item, bool prioritize)
154{
155 if (prioritize)
156 m_pendingItems.push_front(t: item);
157 else
158 m_pendingItems.push_back(t: item);
159}
160
161void QQuickItemParticle::give(QQuickItem *item)
162{
163 for (auto groupId : groupIds()) {
164 for (QQuickParticleData* data : qAsConst(t&: m_system->groupData[groupId]->data)) {
165 if (data->delegate == item){
166 m_deletables << item;
167 data->delegate = nullptr;
168 m_system->groupData[groupId]->kill(d: data);
169 return;
170 }
171 }
172 }
173}
174
175void QQuickItemParticle::initialize(int gIdx, int pIdx)
176{
177 Q_UNUSED(gIdx);
178 Q_UNUSED(pIdx);
179}
180
181void QQuickItemParticle::commit(int, int)
182{
183}
184
185void QQuickItemParticle::processDeletables()
186{
187 foreach (QQuickItem* item, m_deletables){
188 if (m_fade)
189 item->setOpacity(0.);
190 item->setVisible(false);
191 QQuickItemParticleAttached* mpa;
192 if ((mpa = qobject_cast<QQuickItemParticleAttached*>(object: qmlAttachedPropertiesObject<QQuickItemParticle>(obj: item)))) {
193 if (mpa->m_parentItem != nullptr)
194 item->setParentItem(mpa->m_parentItem);
195 mpa->detach();
196 }
197 int idx = -1;
198 if ((idx = m_managed.indexOf(t: item)) != -1) {
199 m_managed.takeAt(i: idx);
200 delete item;
201 }
202 m_activeCount--;
203 }
204 m_deletables.clear();
205}
206
207void QQuickItemParticle::tick(int time)
208{
209 Q_UNUSED(time);//only needed because QTickAnimationProxy expects one
210 processDeletables();
211 for (auto groupId : groupIds()) {
212 for (QQuickParticleData* d : qAsConst(t&: m_system->groupData[groupId]->data)) {
213 if (!d->delegate && d->t != -1 && d->stillAlive(system: m_system)) {
214 QQuickItem* parentItem = nullptr;
215 if (!m_pendingItems.isEmpty()){
216 QQuickItem *item = m_pendingItems.front();
217 m_pendingItems.pop_front();
218 parentItem = item->parentItem();
219 d->delegate = item;
220 }else if (m_delegate){
221 d->delegate = qobject_cast<QQuickItem*>(object: m_delegate->create(context: qmlContext(this)));
222 if (d->delegate)
223 m_managed << d->delegate;
224 }
225 if (d && d->delegate){//###Data can be zero if creating an item leads to a reset - this screws things up.
226 d->delegate->setX(d->curX(particleSystem: m_system) - d->delegate->width() / 2); //TODO: adjust for system?
227 d->delegate->setY(d->curY(particleSystem: m_system) - d->delegate->height() / 2);
228 QQuickItemParticleAttached* mpa = qobject_cast<QQuickItemParticleAttached*>(object: qmlAttachedPropertiesObject<QQuickItemParticle>(obj: d->delegate));
229 if (mpa){
230 mpa->m_parentItem = parentItem;
231 mpa->m_mp = this;
232 mpa->attach();
233 }
234 d->delegate->setParentItem(this);
235 if (m_fade)
236 d->delegate->setOpacity(0.);
237 d->delegate->setVisible(false);//Will be set to true when we prepare the next frame
238 m_activeCount++;
239 }
240 }
241 }
242 }
243}
244
245void QQuickItemParticle::reset()
246{
247 QQuickParticlePainter::reset();
248
249 // delete all managed items which had their logical particles cleared
250 // but leave it alone if the logical particle is maintained
251 QSet<QQuickItem*> lost = QSet<QQuickItem*>(m_managed.cbegin(), m_managed.cend());
252 for (auto groupId : groupIds()) {
253 for (QQuickParticleData* d : qAsConst(t&: m_system->groupData[groupId]->data)) {
254 lost.remove(value: d->delegate);
255 }
256 }
257 m_deletables.unite(other: lost);
258 //TODO: This doesn't yet handle calling detach on taken particles in the system reset case
259 processDeletables();
260}
261
262
263QSGNode* QQuickItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d)
264{
265 //Dummy update just to get painting tick
266 if (m_pleaseReset)
267 m_pleaseReset = false;
268
269 prepareNextFrame();
270
271 update();//Get called again
272 if (n)
273 n->markDirty(bits: QSGNode::DirtyMaterial);
274 return QQuickItem::updatePaintNode(n,d);
275}
276
277void QQuickItemParticle::prepareNextFrame()
278{
279 if (!m_system)
280 return;
281 qint64 timeStamp = m_system->systemSync(p: this);
282 qreal curT = timeStamp/1000.0;
283 qreal dt = curT - m_lastT;
284 m_lastT = curT;
285 if (!m_activeCount)
286 return;
287
288 //TODO: Size, better fade?
289 for (auto groupId : groupIds()) {
290 for (QQuickParticleData* data : qAsConst(t&: m_system->groupData[groupId]->data)) {
291 QQuickItem* item = data->delegate;
292 if (!item)
293 continue;
294 float t = ((timeStamp / 1000.0f) - data->t) / data->lifeSpan;
295 if (m_stasis.contains(value: item)) {
296 data->t += dt;//Stasis effect
297 continue;
298 }
299 if (t >= 1.0f){//Usually happens from load
300 m_deletables << item;
301 data->delegate = nullptr;
302 }else{//Fade
303 data->delegate->setVisible(true);
304 if (m_fade){
305 float o = 1.f;
306 if (t <0.2f)
307 o = t * 5;
308 if (t > 0.8f)
309 o = (1-t)*5;
310 item->setOpacity(o);
311 }
312 }
313 item->setX(data->curX(particleSystem: m_system) - item->width() / 2 - m_systemOffset.x());
314 item->setY(data->curY(particleSystem: m_system) - item->height() / 2 - m_systemOffset.y());
315 }
316 }
317}
318
319QQuickItemParticleAttached *QQuickItemParticle::qmlAttachedProperties(QObject *object)
320{
321 return new QQuickItemParticleAttached(object);
322}
323
324QT_END_NAMESPACE
325
326#include "moc_qquickitemparticle_p.cpp"
327

source code of qtdeclarative/src/particles/qquickitemparticle.cpp