diff options
6 files changed, 223 insertions, 11 deletions
diff --git a/examples/quick/particles/itemparticle/delegates.qml b/examples/quick/particles/itemparticle/delegates.qml index 762a9c8e1c..56b80469c9 100644 --- a/examples/quick/particles/itemparticle/delegates.qml +++ b/examples/quick/particles/itemparticle/delegates.qml @@ -72,6 +72,10 @@ Rectangle { ParticleSystem { anchors.fill: parent id: syssy + MouseArea { + anchors.fill: parent + onClicked: syssy.running = !syssy.running + } Emitter { anchors.centerIn: parent emitRate: 1 diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp index 4f81c0662b..b9a889cddb 100644 --- a/src/particles/qquickitemparticle.cpp +++ b/src/particles/qquickitemparticle.cpp @@ -65,11 +65,25 @@ QT_BEGIN_NAMESPACE /*! \qmlmethod QtQuick.Particles::ItemParticle::take(Item item, bool prioritize) - Asks the ItemParticle to take over control of item. It will be emitted when there is a logical particle available. + Asks the ItemParticle to take over control of item positioning temporarily. + It will follow the movement of a logical particle when one is available. By default items form a queue when waiting for a logical particle, but if prioritize is true then it will go immediately to the head of the queue. + + ItemParticle does not take ownership of the item, and will relinquish + control when the logical particle expires. Commonly at this point you will + want to put it back in the queue, you can do this with the below line in + the delegate definition: + \code + ItemParticle.onDetached: itemParticleInstance.take(delegateRootItem); + \endcode + or delete it, such as with the below line in the delegate definition: + \code + ItemParticle.onDetached: delegateRootItem.destroy(); + \endcode */ + /*! \qmlmethod QtQuick.Particles::ItemParticle::give(Item item) @@ -88,8 +102,13 @@ QT_BEGIN_NAMESPACE /*! \qmlproperty Component QtQuick.Particles::ItemParticle::delegate - An instance of the delegate will be created for every logical - particle, and moved along with it. + An instance of the delegate will be created for every logical particle, and + moved along with it. As an alternative to using delegate, you can create + Item instances yourself and hand them to the ItemParticle to move using the + \l take method. + + Any delegate instances created by ItemParticle will be destroyed when + the logical particle expires. */ QQuickItemParticle::QQuickItemParticle(QQuickItem *parent) : @@ -139,9 +158,8 @@ void QQuickItemParticle::commit(int, int) { } -void QQuickItemParticle::tick(int time) +void QQuickItemParticle::processDeletables() { - Q_UNUSED(time);//only needed because QTickAnimationProxy expects one foreach (QQuickItem* item, m_deletables){ if (m_fade) item->setOpacity(0.); @@ -149,26 +167,39 @@ void QQuickItemParticle::tick(int time) QQuickItemParticleAttached* mpa; if ((mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(item)))) mpa->detach();//reparent as well? - delete item; + int idx = -1; + if ((idx = m_managed.indexOf(item)) != -1) { + m_managed.takeAt(idx); + delete item; + } m_activeCount--; } m_deletables.clear(); +} + +void QQuickItemParticle::tick(int time) +{ + Q_UNUSED(time);//only needed because QTickAnimationProxy expects one + processDeletables(); foreach (QQuickParticleData* d, m_loadables){ Q_ASSERT(d); if (m_stasis.contains(d->delegate)) qWarning() << "Current model particles prefers overwrite:false"; //remove old item from the particle that is dying to make room for this one - if (d->delegate) + if (d->delegate) { m_deletables << d->delegate; - d->delegate = 0; + d->delegate = 0; + } if (!m_pendingItems.isEmpty()){ d->delegate = m_pendingItems.front(); m_pendingItems.pop_front(); }else if (m_delegate){ d->delegate = qobject_cast<QQuickItem*>(m_delegate->create(qmlContext(this))); + if (d->delegate) + m_managed << d->delegate; } - if (d->delegate && d){//###Data can be zero if creating an item leads to a reset - this screws things up. + if (d && d->delegate){//###Data can be zero if creating an item leads to a reset - this screws things up. d->delegate->setX(d->curX() - d->delegate->width()/2);//TODO: adjust for system? d->delegate->setY(d->curY() - d->delegate->height()/2); QQuickItemParticleAttached* mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(d->delegate)); @@ -190,8 +221,18 @@ void QQuickItemParticle::reset() { QQuickParticlePainter::reset(); m_loadables.clear(); - //TODO: Cleanup items? - //deletables? + + // delete all managed items which had their logical particles cleared + // but leave it alone if the logical particle is maintained + QSet<QQuickItem*> lost = QSet<QQuickItem*>::fromList(m_managed); + foreach (const QString group, m_groups){ + int gIdx = m_system->groupIds[group]; + foreach (QQuickParticleData* d, m_system->groupData[gIdx]->data) + lost.remove(d->delegate); + } + m_deletables.append(lost.toList()); + //TODO: This doesn't yet handle calling detach on taken particles in the system reset case + processDeletables(); } diff --git a/src/particles/qquickitemparticle_p.h b/src/particles/qquickitemparticle_p.h index 24c33319c9..04564e739a 100644 --- a/src/particles/qquickitemparticle_p.h +++ b/src/particles/qquickitemparticle_p.h @@ -88,8 +88,10 @@ protected: virtual void initialize(int gIdx, int pIdx); void prepareNextFrame(); private: + void processDeletables(); void tick(int time = 0); QList<QQuickItem* > m_deletables; + QList<QQuickItem* > m_managed; QList< QQuickParticleData* > m_loadables; bool m_fade; diff --git a/tests/auto/particles/qquickitemparticle/data/managed.qml b/tests/auto/particles/qquickitemparticle/data/managed.qml new file mode 100644 index 0000000000..f8f231dc55 --- /dev/null +++ b/tests/auto/particles/qquickitemparticle/data/managed.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Particles 2.0 + +Rectangle { + color: "black" + width: 320 + height: 320 + + ParticleSystem { + id: sys + objectName: "system" + anchors.fill: parent + property int acc: 0 + + ItemParticle { + delegate: Image { + Component.onCompleted: sys.acc = sys.acc + 1; + Component.onDestruction: sys.acc = sys.acc - 1; + source: "../../shared/star.png" + } + } + + Emitter{ + //0,0 position + size: 32 + emitRate: 1000 + lifeSpan: 100 + } + } +} diff --git a/tests/auto/particles/qquickitemparticle/data/unmanaged.qml b/tests/auto/particles/qquickitemparticle/data/unmanaged.qml new file mode 100644 index 0000000000..c9b66b9d4f --- /dev/null +++ b/tests/auto/particles/qquickitemparticle/data/unmanaged.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Particles 2.0 + +Rectangle { + color: "black" + width: 320 + height: 320 + + Repeater { + model: 100 + delegate: Image { + id: img + Component.onCompleted: { + sys.acc = sys.acc + 1; + ip.take(img); + } + Component.onDestruction: sys.acc = sys.acc - 1; + + //Test uses the recycling case because it's most realistic + //Attempts by ItemParticle to delete the delegate should lead to a segfault + ItemParticle.onDetached: ip.take(img); + + source: "../../shared/star.png" + } + } + + ParticleSystem { + id: sys + objectName: "system" + anchors.fill: parent + property int acc: 0 + + ItemParticle { + id: ip + } + + Emitter{ + //0,0 position + size: 32 + emitRate: 1000 + lifeSpan: 100 + } + } +} diff --git a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp index c019f2e3bb..c825591972 100644 --- a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp +++ b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp @@ -48,6 +48,8 @@ public: private slots: void initTestCase(); void test_basic(); + void test_deletion(); + void test_noDeletion(); }; void tst_qquickitemparticle::initTestCase() @@ -87,6 +89,29 @@ void tst_qquickitemparticle::test_basic() delete view; } +void tst_qquickitemparticle::test_deletion() +{ + QQuickView* view = createView(testFileUrl("managed.qml"), 500); + QQuickParticleSystem* system = view->rootObject()->findChild<QQuickParticleSystem*>("system"); + ensureAnimTime(500, system->m_animation); + + QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 100, 10)); + //qDebug() << system->property("acc").toInt(); Seems to be around +15 due to the one frame delay in cleanup compared to creation + QVERIFY(extremelyFuzzyCompare(system->property("acc").toInt(), 100, 20)); + delete view; +} + +void tst_qquickitemparticle::test_noDeletion() +{ + QQuickView* view = createView(testFileUrl("unmanaged.qml"), 500); + QQuickParticleSystem* system = view->rootObject()->findChild<QQuickParticleSystem*>("system"); + ensureAnimTime(500, system->m_animation); + + QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 100, 10)); + QVERIFY(extremelyFuzzyCompare(system->property("acc").toInt(), 100, 10)); + delete view; +} + QTEST_MAIN(tst_qquickitemparticle); #include "tst_qquickitemparticle.moc" |