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#include "qquickparticlepainter_p.h"
5#include <QQuickWindow>
6#include <QDebug>
7QT_BEGIN_NAMESPACE
8/*!
9 \qmltype ParticlePainter
10 \instantiates QQuickParticlePainter
11 \inqmlmodule QtQuick.Particles
12 \inherits Item
13 \brief For specifying how to paint particles.
14 \ingroup qtquick-particles
15
16 The default implementation paints nothing. See the subclasses if you want to
17 paint something visible.
18
19*/
20/*!
21 \qmlproperty ParticleSystem QtQuick.Particles::ParticlePainter::system
22 This is the system whose particles can be painted by the element.
23 If the ParticlePainter is a direct child of a ParticleSystem, it will automatically be associated with it.
24*/
25/*!
26 \qmlproperty list<string> QtQuick.Particles::ParticlePainter::groups
27 Which logical particle groups will be painted.
28
29 If empty, it will paint the default particle group ("").
30*/
31QQuickParticlePainter::QQuickParticlePainter(QQuickItem *parent)
32 : QQuickItem(parent)
33 , m_system(nullptr)
34 , m_count(0)
35 , m_pleaseReset(true)
36 , m_window(nullptr)
37 , m_windowChanged(false)
38 , m_groupIdsNeedRecalculation(false)
39{
40}
41
42void QQuickParticlePainter::itemChange(ItemChange change, const ItemChangeData &data)
43{
44 if (change == QQuickItem::ItemSceneChange) {
45 if (m_window)
46 disconnect(sender: m_window, SIGNAL(sceneGraphInvalidated()), receiver: this, SLOT(sceneGraphInvalidated()));
47 m_window = data.window;
48 m_windowChanged = true;
49 if (m_window)
50 connect(sender: m_window, SIGNAL(sceneGraphInvalidated()), receiver: this, SLOT(sceneGraphInvalidated()), Qt::DirectConnection);
51 }
52 QQuickItem::itemChange(change, data);
53}
54
55void QQuickParticlePainter::componentComplete()
56{
57 if (!m_system && qobject_cast<QQuickParticleSystem*>(object: parentItem()))
58 setSystem(qobject_cast<QQuickParticleSystem*>(object: parentItem()));
59 QQuickItem::componentComplete();
60}
61
62void QQuickParticlePainter::recalculateGroupIds() const
63{
64 if (!m_system) {
65 m_groupIds.clear();
66 return;
67 }
68
69 m_groupIdsNeedRecalculation = false;
70 m_groupIds.clear();
71
72 const auto groupList = groups();
73 for (const QString &str : groupList) {
74 QQuickParticleGroupData::ID groupId = m_system->groupIds.value(key: str, defaultValue: QQuickParticleGroupData::InvalidID);
75 if (groupId == QQuickParticleGroupData::InvalidID) {
76 // invalid data, not finished setting up, or whatever. Fallback: do not cache.
77 m_groupIdsNeedRecalculation = true;
78 } else {
79 m_groupIds.append(t: groupId);
80 }
81 }
82}
83
84void QQuickParticlePainter::setSystem(QQuickParticleSystem *arg)
85{
86 if (m_system != arg) {
87 m_system = arg;
88 m_groupIdsNeedRecalculation = true;
89 if (m_system){
90 m_system->registerParticlePainter(p: this);
91 reset();
92 }
93 emit systemChanged(arg);
94 }
95}
96
97void QQuickParticlePainter::setGroups(const QStringList &arg)
98{
99 if (m_groups != arg) {
100 m_groups = arg;
101 m_groupIdsNeedRecalculation = true;
102 //Note: The system watches this as it has to recalc things when groups change. It will request a reset if necessary
103 Q_EMIT groupsChanged(arg);
104 }
105}
106
107void QQuickParticlePainter::load(QQuickParticleData* d)
108{
109 initialize(gIdx: d->groupId, pIdx: d->index);
110 if (m_pleaseReset)
111 return;
112 m_pendingCommits << qMakePair(value1&: d->groupId, value2&: d->index);
113}
114
115void QQuickParticlePainter::reload(QQuickParticleData* d)
116{
117 if (m_pleaseReset)
118 return;
119 m_pendingCommits << qMakePair(value1&: d->groupId, value2&: d->index);
120}
121
122void QQuickParticlePainter::reset()
123{
124 m_pendingCommits.clear();
125 m_pleaseReset = true;
126}
127
128void QQuickParticlePainter::setCount(int c)//### TODO: some resizeing so that particles can reallocate on size change instead of recreate
129{
130 Q_ASSERT(c >= 0); //XXX
131 if (c == m_count)
132 return;
133 m_count = c;
134 emit countChanged();
135 reset();
136}
137
138void QQuickParticlePainter::calcSystemOffset(bool resetPending)
139{
140 if (!m_system || !parentItem())
141 return;
142 QPointF lastOffset = m_systemOffset;
143 m_systemOffset = -1 * this->mapFromItem(item: m_system, point: QPointF(0.0, 0.0));
144 if (lastOffset != m_systemOffset && !resetPending){
145 //Reload all particles//TODO: Necessary?
146 foreach (const QString &g, m_groups){
147 int gId = m_system->groupIds[g];
148 foreach (QQuickParticleData* d, m_system->groupData[gId]->data)
149 reload(d);
150 }
151 }
152}
153typedef QPair<int,int> intPair;
154void QQuickParticlePainter::performPendingCommits()
155{
156 calcSystemOffset();
157 foreach (intPair p, m_pendingCommits)
158 commit(gIdx: p.first, pIdx: p.second);
159 m_pendingCommits.clear();
160}
161
162QT_END_NAMESPACE
163
164#include "moc_qquickparticlepainter_p.cpp"
165

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