Doc: Changed \qmlclass to \qmltype and added \instantiates.
[profile/ivi/qtdeclarative.git] / src / particles / qquickitemparticle.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickitemparticle_p.h"
43 #include <private/qquickvisualitemmodel_p.h>
44 #include <QtQuick/qsgnode.h>
45 #include <QTimer>
46 #include <QQmlComponent>
47 #include <QDebug>
48
49 QT_BEGIN_NAMESPACE
50
51 /*!
52     \qmltype ItemParticle
53     \instantiates QQuickItemParticle
54     \inqmlmodule QtQuick.Particles 2
55     \inherits ParticlePainter
56     \brief For specifying a delegate to paint particles
57     \ingroup qtquick-particles
58
59 */
60
61
62 /*!
63     \qmlmethod void QtQuick.Particles2::ItemParticle::freeze(Item item)
64
65     Suspends the flow of time for the logical particle which item represents, allowing you to control its movement.
66 */
67
68 /*!
69     \qmlmethod void QtQuick.Particles2::ItemParticle::unfreeze(Item item)
70
71     Restarts the flow of time for the logical particle which item represents, allowing it to be moved by the particle system again.
72 */
73
74 /*!
75     \qmlmethod void QtQuick.Particles2::ItemParticle::take(Item item, bool prioritize)
76
77     Asks the ItemParticle to take over control of item. It will be emitted when there is a logical particle available.
78
79     By default items form a queue when waiting for a logical particle, but if prioritize is true then it will go immediately to the
80     head of the queue.
81 */
82 /*!
83     \qmlmethod void QtQuick.Particles2::ItemParticle::give(Item item)
84
85     Orders the ItemParticle to give you control of the item. It will cease controlling it and the item will lose its association to the logical particle.
86 */
87
88 /*!
89     \qmlproperty bool QtQuick.Particles2::ItemParticle::fade
90
91     If true, the item will automatically be faded in and out
92     at the ends of its lifetime. If false, you will have to
93     implement any entry effect yourself.
94
95     Default is true.
96 */
97 /*!
98     \qmlproperty Component QtQuick.Particles2::ItemParticle::delegate
99
100     An instance of the delegate will be created for every logical
101     particle, and moved along with it.
102 */
103
104 QQuickItemParticle::QQuickItemParticle(QQuickItem *parent) :
105     QQuickParticlePainter(parent), m_fade(true), m_delegate(0)
106 {
107     setFlag(QQuickItem::ItemHasContents);
108     clock = new Clock(this);
109     clock->start();
110 }
111
112 QQuickItemParticle::~QQuickItemParticle()
113 {
114     delete clock;
115 }
116
117 void QQuickItemParticle::freeze(QQuickItem* item)
118 {
119     m_stasis << item;
120 }
121
122
123 void QQuickItemParticle::unfreeze(QQuickItem* item)
124 {
125     m_stasis.remove(item);
126 }
127
128 void QQuickItemParticle::take(QQuickItem *item, bool prioritize)
129 {
130     if (prioritize)
131         m_pendingItems.push_front(item);
132     else
133         m_pendingItems.push_back(item);
134 }
135
136 void QQuickItemParticle::give(QQuickItem *item)
137 {
138     //TODO: This
139     Q_UNUSED(item);
140 }
141
142 void QQuickItemParticle::initialize(int gIdx, int pIdx)
143 {
144     m_loadables << m_system->groupData[gIdx]->data[pIdx];//defer to other thread
145 }
146
147 void QQuickItemParticle::commit(int, int)
148 {
149 }
150
151 void QQuickItemParticle::tick(int time)
152 {
153     Q_UNUSED(time);//only needed because QTickAnimationProxy expects one
154     foreach (QQuickItem* item, m_deletables){
155         if (m_fade)
156             item->setOpacity(0.);
157         item->setVisible(false);
158         QQuickItemParticleAttached* mpa;
159         if ((mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(item))))
160             mpa->detach();//reparent as well?
161         //TODO: Delete iff we created it
162         m_activeCount--;
163     }
164     m_deletables.clear();
165
166     foreach (QQuickParticleData* d, m_loadables){
167         if (m_stasis.contains(d->delegate))
168             qWarning() << "Current model particles prefers overwrite:false";
169         //remove old item from the particle that is dying to make room for this one
170         if (d->delegate)
171             m_deletables << d->delegate;
172         d->delegate = 0;
173         if (!m_pendingItems.isEmpty()){
174             d->delegate = m_pendingItems.front();
175             m_pendingItems.pop_front();
176         }else if (m_delegate){
177             d->delegate = qobject_cast<QQuickItem*>(m_delegate->create(qmlContext(this)));
178         }
179         if (d->delegate && d){//###Data can be zero if creating an item leads to a reset - this screws things up.
180             d->delegate->setX(d->curX() - d->delegate->width()/2);//TODO: adjust for system?
181             d->delegate->setY(d->curY() - d->delegate->height()/2);
182             QQuickItemParticleAttached* mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(d->delegate));
183             if (mpa){
184                 mpa->m_mp = this;
185                 mpa->attach();
186             }
187             d->delegate->setParentItem(this);
188             if (m_fade)
189                 d->delegate->setOpacity(0.);
190             d->delegate->setVisible(false);//Will be set to true when we prepare the next frame
191             m_activeCount++;
192         }
193     }
194     m_loadables.clear();
195 }
196
197 void QQuickItemParticle::reset()
198 {
199     QQuickParticlePainter::reset();
200     m_loadables.clear();
201     //TODO: Cleanup items?
202     //deletables?
203 }
204
205
206 QSGNode* QQuickItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d)
207 {
208     //Dummy update just to get painting tick
209     if (m_pleaseReset){
210         m_pleaseReset = false;
211         //Refill loadables, delayed here so as to only happen once per frame max
212         //### Constant resetting might lead to m_loadables never being populated when tick() occurs
213         foreach (const QString group, m_groups){
214             int gIdx = m_system->groupIds[group];
215             foreach (QQuickParticleData* d, m_system->groupData[gIdx]->data)
216                 if (!d->delegate && d->t != -1  && d->stillAlive())
217                     m_loadables << d;
218         }
219     }
220     prepareNextFrame();
221
222     update();//Get called again
223     if (n)
224         n->markDirty(QSGNode::DirtyMaterial);
225     return QQuickItem::updatePaintNode(n,d);
226 }
227
228 void QQuickItemParticle::prepareNextFrame()
229 {
230     if (!m_system)
231         return;
232     qint64 timeStamp = m_system->systemSync(this);
233     qreal curT = timeStamp/1000.0;
234     qreal dt = curT - m_lastT;
235     m_lastT = curT;
236     if (!m_activeCount)
237         return;
238
239     //TODO: Size, better fade?
240     foreach (const QString &str, m_groups){
241         int gIdx = m_system->groupIds[str];
242         int count = m_system->groupData[gIdx]->size();
243
244         for (int i=0; i<count; i++){
245             QQuickParticleData* data = m_system->groupData[gIdx]->data[i];
246             QQuickItem* item = data->delegate;
247             if (!item)
248                 continue;
249             qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan;
250             if (m_stasis.contains(item)) {
251                 data->t += dt;//Stasis effect
252                 continue;
253             }
254             if (t >= 1.0){//Usually happens from load
255                 m_deletables << item;
256                 data->delegate = 0;
257             }else{//Fade
258                 data->delegate->setVisible(true);
259                 if (m_fade){
260                     qreal o = 1.;
261                     if (t<0.2)
262                         o = t*5;
263                     if (t>0.8)
264                         o = (1-t)*5;
265                     item->setOpacity(o);
266                 }
267             }
268             item->setX(data->curX() - item->width()/2 - m_systemOffset.x());
269             item->setY(data->curY() - item->height()/2 - m_systemOffset.y());
270         }
271     }
272 }
273
274 QQuickItemParticleAttached *QQuickItemParticle::qmlAttachedProperties(QObject *object)
275 {
276     return new QQuickItemParticleAttached(object);
277 }
278
279 QT_END_NAMESPACE