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