From cdb4ae34924c8edb607aec0db2fd8b364865e092 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 31 May 2011 14:37:39 +0200 Subject: [PATCH 01/48] Avoid recursive updates in QSGText. --- src/declarative/items/qsgtext.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/declarative/items/qsgtext.cpp b/src/declarative/items/qsgtext.cpp index e7e655d591..8bff100f1e 100644 --- a/src/declarative/items/qsgtext.cpp +++ b/src/declarative/items/qsgtext.cpp @@ -290,6 +290,8 @@ void QSGTextPrivate::updateSize() int dy = q->height(); QSize size(0, 0); + layoutThread = QThread::currentThread(); + //setup instance of QTextLayout for all cases other than richtext if (!richText) { QRect textRect = setupTextLayout(); @@ -378,8 +380,6 @@ QRect QSGTextPrivate::setupTextLayout() bool elideText = false; bool truncate = false; - layoutThread = QThread::currentThread(); - QFontMetrics fm(layout.font()); elidePos = QPointF(); @@ -1064,6 +1064,11 @@ QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) Q_UNUSED(data); Q_D(QSGText); + if (d->text.isEmpty()) { + delete oldNode; + return 0; + } + bool richTextAsImage = false; if (d->richText) { d->ensureDoc(); From b0f23457d736baf99a2f7460d1942da632cc6a15 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 1 Jun 2011 11:37:27 +0200 Subject: [PATCH 02/48] Fix Text's underline, overline and strikeout properties for plain text. --- src/declarative/items/qsgtextnode.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/declarative/items/qsgtextnode.cpp b/src/declarative/items/qsgtextnode.cpp index 33325a14ab..876c9785b4 100644 --- a/src/declarative/items/qsgtextnode.cpp +++ b/src/declarative/items/qsgtextnode.cpp @@ -167,6 +167,13 @@ QSGGlyphNode *QSGTextNode::addGlyphs(const QPointF &position, const QGlyphRun &g appendChildNode(node); + if (glyphs.overline() || glyphs.strikeOut() || glyphs.underline()) { + QPointF baseLine = node->baseLine(); + qreal width = node->boundingRect().width(); + addTextDecorations(baseLine, glyphs.rawFont(), color, width, + glyphs.overline(), glyphs.strikeOut(), glyphs.underline()); + } + return node; } @@ -193,13 +200,6 @@ void QSGTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout QRawFont font = glyphs.rawFont(); addGlyphs(position + QPointF(0, font.ascent()), glyphs, color, style, styleColor); } - - QFont font = textLayout->font(); - QRawFont rawFont = QRawFont::fromFont(font); - if (font.strikeOut() || font.underline() || font.overline()) { - addTextDecorations(position, rawFont, color, textLayout->boundingRect().width(), - font.overline(), font.strikeOut(), font.underline()); - } } @@ -360,13 +360,8 @@ void QSGTextNode::addTextBlock(const QPointF &position, QTextDocument *textDocum for (int i=0; ibaseLine(); - qreal width = glyphNode->boundingRect().width(); - addTextDecorations(baseLine, font, color, width, - glyphs.overline(), glyphs.strikeOut(), glyphs.underline()); + addGlyphs(position + blockPosition + QPointF(0, font.ascent()), + glyphs, color, style, styleColor); } } From ac7dcaec90d7603cd4dd80cdb3dcbdc689f376c2 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 1 Jun 2011 14:44:16 +0200 Subject: [PATCH 03/48] Fix QDeclarativePixmap not setting its QPixmap. When used with the scenegraph, it was only setting the texture but not the pixmap. --- src/declarative/util/qdeclarativepixmapcache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp index f1387418ef..5c51f7186c 100644 --- a/src/declarative/util/qdeclarativepixmapcache.cpp +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -855,8 +855,8 @@ bool QDeclarativePixmapReply::event(QEvent *event) if (de->texture) { data->texture = de->texture; data->context = de->context; - } else - data->pixmap = QPixmap::fromImage(de->image); + } + data->pixmap = QPixmap::fromImage(de->image); data->implicitSize = de->implicitSize; } else { data->errorString = de->errorString; From 0e2aaa5a28349d7c906005ae12dd8656ca07b53e Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 1 Jun 2011 14:49:53 +0200 Subject: [PATCH 04/48] Fix images not being rendered when embedded in a rich text. In that case the whole text is cached in a QImage, but that cache was not updated when the embedded image was finished to be loaded. Depends on ac7dcaec90d7603cd4dd80cdb3dcbdc689f376c2. Task-number: QTBUG-19428 --- src/declarative/items/qsgtext.cpp | 16 +++++++--------- src/declarative/items/qsgtext_p_p.h | 3 ++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/declarative/items/qsgtext.cpp b/src/declarative/items/qsgtext.cpp index 8bff100f1e..fe181692f3 100644 --- a/src/declarative/items/qsgtext.cpp +++ b/src/declarative/items/qsgtext.cpp @@ -106,7 +106,8 @@ QSGTextPrivate::QSGTextPrivate() imageCacheDirty(true), updateOnComponentComplete(true), richText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false), requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false), - layoutTextElided(false), naturalWidth(0), doc(0), layoutThread(0), nodeType(NodeIsNull) + layoutTextElided(false), richTextAsImage(false), naturalWidth(0), doc(0), layoutThread(0), + nodeType(NodeIsNull) { cacheAllTextAsImage = enableImageCache(); } @@ -574,7 +575,7 @@ void QSGTextPrivate::invalidateImageCache() { Q_Q(QSGText); - if(cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && style != QSGText::Normal)){//If actually using the image cache + if(richTextAsImage || cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && style != QSGText::Normal)){//If actually using the image cache if (imageCacheDirty) return; @@ -749,6 +750,7 @@ void QSGText::setText(const QString &n) d->ensureDoc(); d->doc->setText(n); d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); + d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc); } else { d->rightToLeftText = d->text.isRightToLeft(); } @@ -988,6 +990,7 @@ void QSGText::setTextFormat(TextFormat format) if (!wasRich && d->richText && isComponentComplete()) { d->ensureDoc(); d->doc->setText(d->text); + d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc); } d->updateLayout(); @@ -1069,12 +1072,6 @@ QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) return 0; } - bool richTextAsImage = false; - if (d->richText) { - d->ensureDoc(); - richTextAsImage = QSGTextNode::isComplexRichText(d->doc); - } - QRectF bounds = boundingRect(); // We need to make sure the layout is done in the current thread @@ -1082,7 +1079,7 @@ QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) d->updateLayout(); // XXX todo - some styled text can be done by the QSGTextNode - if (richTextAsImage || d->cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && d->style != Normal)) { + if (d->richTextAsImage || d->cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && d->style != Normal)) { bool wasDirty = d->imageCacheDirty; d->checkImageCache(); @@ -1213,6 +1210,7 @@ void QSGText::componentComplete() d->ensureDoc(); d->doc->setText(d->text); d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); + d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc); } else { d->rightToLeftText = d->text.isRightToLeft(); } diff --git a/src/declarative/items/qsgtext_p_p.h b/src/declarative/items/qsgtext_p_p.h index a3836a19f8..9c0d5b11d9 100644 --- a/src/declarative/items/qsgtext_p_p.h +++ b/src/declarative/items/qsgtext_p_p.h @@ -120,12 +120,13 @@ public: bool hAlignImplicit:1; bool rightToLeftText:1; bool layoutTextElided:1; + bool richTextAsImage:1; QRect layedOutTextRect; QSize paintedSize; qreal naturalWidth; virtual qreal getImplicitWidth() const; - + void ensureDoc(); QPixmap textDocumentImage(bool drawStyle); QSGTextDocumentWithImageResources *doc; From 6da49f13f80fa4a8f1b62424672f7ea081c6fa83 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 1 Jun 2011 16:18:37 +0200 Subject: [PATCH 05/48] Make complex rich text works with the threaded renderer. Painting must be done from the GUI thread. --- src/declarative/items/qsgtext.cpp | 33 +++++++++++++++++++++-------- src/declarative/items/qsgtext_p.h | 1 + src/declarative/items/qsgtext_p_p.h | 1 + 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/declarative/items/qsgtext.cpp b/src/declarative/items/qsgtext.cpp index fe181692f3..ae9bffc795 100644 --- a/src/declarative/items/qsgtext.cpp +++ b/src/declarative/items/qsgtext.cpp @@ -103,11 +103,11 @@ QSGTextPrivate::QSGTextPrivate() lineHeightMode(QSGText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX), maximumLineCountValid(false), texture(0), - imageCacheDirty(true), updateOnComponentComplete(true), + imageCacheDirty(false), updateOnComponentComplete(true), richText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false), requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false), - layoutTextElided(false), richTextAsImage(false), naturalWidth(0), doc(0), layoutThread(0), - nodeType(NodeIsNull) + layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), naturalWidth(0), + doc(0), layoutThread(0), nodeType(NodeIsNull) { cacheAllTextAsImage = enableImageCache(); } @@ -580,9 +580,10 @@ void QSGTextPrivate::invalidateImageCache() return; imageCacheDirty = true; - imageCache = QPixmap(); - } - if (q->isComponentComplete()) + + if (q->isComponentComplete()) + QCoreApplication::postEvent(q, new QEvent(QEvent::User)); + } else if (q->isComponentComplete()) q->update(); } @@ -591,6 +592,8 @@ void QSGTextPrivate::invalidateImageCache() */ void QSGTextPrivate::checkImageCache() { + Q_Q(QSGText); + if (!imageCacheDirty) return; @@ -631,6 +634,8 @@ void QSGTextPrivate::checkImageCache() } imageCacheDirty = false; + textureImageCacheDirty = true; + q->update(); } /*! @@ -1080,9 +1085,8 @@ QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) // XXX todo - some styled text can be done by the QSGTextNode if (d->richTextAsImage || d->cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && d->style != Normal)) { - bool wasDirty = d->imageCacheDirty; - - d->checkImageCache(); + bool wasDirty = d->textureImageCacheDirty; + d->textureImageCacheDirty = false; if (d->imageCache.isNull()) { delete oldNode; @@ -1142,6 +1146,17 @@ QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) } } +bool QSGText::event(QEvent *e) +{ + Q_D(QSGText); + if (e->type() == QEvent::User) { + d->checkImageCache(); + return true; + } else { + return QSGImplicitSizeItem::event(e); + } +} + qreal QSGText::paintedWidth() const { Q_D(const QSGText); diff --git a/src/declarative/items/qsgtext_p.h b/src/declarative/items/qsgtext_p.h index 090a2b0e67..a5b61bcb2e 100644 --- a/src/declarative/items/qsgtext_p.h +++ b/src/declarative/items/qsgtext_p.h @@ -199,6 +199,7 @@ protected: virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual bool event(QEvent *); private: Q_DISABLE_COPY(QSGText) diff --git a/src/declarative/items/qsgtext_p_p.h b/src/declarative/items/qsgtext_p_p.h index 9c0d5b11d9..6e98d30594 100644 --- a/src/declarative/items/qsgtext_p_p.h +++ b/src/declarative/items/qsgtext_p_p.h @@ -121,6 +121,7 @@ public: bool rightToLeftText:1; bool layoutTextElided:1; bool richTextAsImage:1; + bool textureImageCacheDirty:1; QRect layedOutTextRect; QSize paintedSize; From 6dbd4286eb19e9ac45665046a43342bcdc8b127e Mon Sep 17 00:00:00 2001 From: Charles Yin Date: Mon, 30 May 2011 16:14:42 +1000 Subject: [PATCH 06/48] rewrite context2d with direct QtScript binding for better performance. Change-Id: I740ad4cbb7446a838954a010b0e5f0b847ec4f53 --- src/declarative/items/qsgcanvasitem.cpp | 305 +----------- src/declarative/items/qsgcanvasitem_p.h | 3 +- src/declarative/items/qsgcontext2d.cpp | 608 +++++++++++++++++++++++- src/declarative/items/qsgcontext2d_p.h | 34 +- 4 files changed, 614 insertions(+), 336 deletions(-) diff --git a/src/declarative/items/qsgcanvasitem.cpp b/src/declarative/items/qsgcanvasitem.cpp index ba3c4baa11..a6403c0081 100644 --- a/src/declarative/items/qsgcanvasitem.cpp +++ b/src/declarative/items/qsgcanvasitem.cpp @@ -56,6 +56,7 @@ class QSGCanvasItemPrivate : public QSGPaintedItemPrivate { public: QSGCanvasItemPrivate(); + ~QSGCanvasItemPrivate(); QSGContext2D* context; }; @@ -69,6 +70,10 @@ QSGCanvasItemPrivate::QSGCanvasItemPrivate() { } +QSGCanvasItemPrivate::~QSGCanvasItemPrivate() +{ +} + /*! Constructs a QSGCanvasItem with the given \a parent item. */ @@ -94,24 +99,25 @@ void QSGCanvasItem::paint(QPainter *painter) } } - -QSGContext2D* QSGCanvasItem::getContext(const QString &contextId) +QScriptValue QSGCanvasItem::getContext(const QString &contextId) { Q_D(QSGCanvasItem); + QScriptEngine* e = QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(this)); if (contextId == QLatin1String("2d")) { if (!d->context) { d->context = new QSGContext2D(this); + d->context->setScriptEngine(e); connect(d->context, SIGNAL(changed()), this, SLOT(requestPaint())); } - return d->context; + return d->context->scriptValue(); } qDebug("Canvas:requesting unsupported context"); - return 0; + return e->undefinedValue(); } void QSGCanvasItem::requestPaint() { - Q_D(QSGCanvasItem); + //Q_D(QSGCanvasItem); //TODO:update(d->context->dirtyRect()); update(); } @@ -140,19 +146,19 @@ QString QSGCanvasItem::toDataURL(const QString& mimeType) const QString mime = mimeType; QString type; if (mimeType == QLatin1String("image/bmp")) - type = "BMP"; + type = QLatin1String("BMP"); else if (mimeType == QLatin1String("image/jpeg")) - type = "JPEG"; + type = QLatin1String("JPEG"); else if (mimeType == QLatin1String("image/x-portable-pixmap")) - type = "PPM"; + type = QLatin1String("PPM"); else if (mimeType == QLatin1String("image/tiff")) - type = "TIFF"; + type = QLatin1String("TIFF"); else if (mimeType == QLatin1String("image/xbm")) - type = "XBM"; + type = QLatin1String("XBM"); else if (mimeType == QLatin1String("image/xpm")) - type = "XPM"; + type = QLatin1String("XPM"); else { - type = "PNG"; + type = QLatin1String("PNG"); mime = QLatin1String("image/png"); } image.save(&buffer, type.toAscii()); @@ -162,280 +168,5 @@ QString QSGCanvasItem::toDataURL(const QString& mimeType) const } return QLatin1String("data:,"); } -//CanvasItemTextureProvider::CanvasItemTextureProvider(QObject *parent) -// : QSGTextureProvider(parent) -// , m_ctx2d(0) -// , m_fbo(0) -// , m_multisampledFbo(0) -// , m_dirtyTexture(true) -// , m_multisamplingSupportChecked(false) -// , m_multisampling(false) -//{ -//} - -//CanvasItemTextureProvider::~CanvasItemTextureProvider() -//{ -// delete m_fbo; -// delete m_multisampledFbo; -//} - -//void CanvasItemTextureProvider::updateTexture() -//{ -// if (m_dirtyTexture) { -// if (!m_ctx2d->isDirty()) -// return; -// if (m_size.isEmpty()) { -// m_texture = QSGTextureRef(); -// delete m_fbo; -// delete m_multisampledFbo; -// m_multisampledFbo = m_fbo = 0; -// return; -// } - -//#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE -// //create texture -// if (!m_fbo || m_fbo->size() != m_size ) -// { -// const QGLContext *ctx = QSGContext::current->glContext(); -// if (!m_multisamplingSupportChecked) { -// QList extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' '); -// m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample") -// && extensions.contains("GL_EXT_framebuffer_blit"); -// m_multisamplingSupportChecked = true; -// } - -// if (ctx->format().sampleBuffers() && m_multisampling) { -// delete m_fbo; -// delete m_multisampledFbo; -// QGLFramebufferObjectFormat format; - -// format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); -// format.setSamples(ctx->format().samples()); -// m_multisampledFbo = new QGLFramebufferObject(m_size, format); -// { -// QGLFramebufferObjectFormat format; -// format.setAttachment(QGLFramebufferObject::NoAttachment); -// m_fbo = new QGLFramebufferObject(m_size, format); -// } - -// QSGPlainTexture *tex = new QSGPlainTexture; -// tex->setTextureId(m_fbo->texture()); -// tex->setOwnsTexture(false); -// tex->setHasAlphaChannel(true); -// setOpaque(!tex->hasAlphaChannel()); -// m_texture = QSGTextureRef(tex); -// } else { -// delete m_fbo; -// QGLFramebufferObjectFormat format; -// format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); -// m_fbo = new QGLFramebufferObject(m_size, format); -// QSGPlainTexture *tex = new QSGPlainTexture; -// tex->setTextureId(m_fbo->texture()); -// tex->setOwnsTexture(false); -// tex->setHasAlphaChannel(true); -// setOpaque(!tex->hasAlphaChannel()); -// m_texture = QSGTextureRef(tex); -// } -// } -//#endif - -//#ifdef QSGCANVASITEM_DEBUG -// qDebug() << "painting interval:" << m_elapsedTimer.nsecsElapsed(); -// m_elapsedTimer.restart(); -//#endif -// //paint 2d -// if (m_ctx2d) { -// QPainter p; -//#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE -// if (m_multisampledFbo) -// p.begin(m_multisampledFbo); -// else if (m_fbo) -// p.begin(m_fbo); -// else -// return; -// // move the origin of coordinates to the down left corner and -// // scale coordinates and turn y-axis up -// QSize size = m_ctx2d->size(); -// p.translate( 0, size.height()); -// p.scale(1, -1); - -// m_ctx2d->paint(&p); - -// p.end(); - -// if (m_multisampledFbo) { -// QRect r(0, 0, m_fbo->width(), m_fbo->height()); -// QGLFramebufferObject::blitFramebuffer(m_fbo, r, m_multisampledFbo, r); -// } - -// if (m_ctx2d->requireCachedImage()) -// m_ctx2d->setCachedImage(m_fbo->toImage()); - -//#else -// m_painter.begin(m_ctx2d->paintDevice()); -// m_ctx2d->paint(&m_painter); -// m_painter.end(); - -// if (m_texture.isNull()) { -// m_texture = QSGContext::current->createTexture(m_ctx2d->toImage()); -// } else { -// QSGPlainTexture* t =static_cast(m_texture.texture()); -// t->setImage(m_ctx2d->toImage()); -// } -// m_ctx2d->setCachedImage(m_ctx2d->toImage()); - -//#endif - -//#ifdef QSGCANVASITEM_DEBUG -// qDebug() << "painting time:" << m_elapsedTimer.nsecsElapsed(); -// m_elapsedTimer.restart(); -//#endif -// emit painted(); -// } -// } -//} - -//QSGTextureRef CanvasItemTextureProvider::texture() -//{ -// return m_texture; -//} -//void CanvasItemTextureProvider::setContext2D(QSGContext2D *ctx2d) -//{ -// if (ctx2d && m_ctx2d != ctx2d) { -// m_ctx2d = ctx2d; -// connect(this, SIGNAL(painted()), m_ctx2d, SIGNAL(painted())); -// } -//} -//void CanvasItemTextureProvider::setRect(const QRectF &rect) -//{ -// if (rect == m_rect) -// return; -// m_rect = rect; -// markDirtyTexture(); -//} - -//void CanvasItemTextureProvider::setSize(const QSize &size) -//{ -// if (size == m_size) -// return; -// m_size = size; -// markDirtyTexture(); -//} - -//void CanvasItemTextureProvider::markDirtyTexture() -//{ -// m_dirtyTexture = true; -// emit textureChanged(); -//} -//QSGCanvasItem::QSGCanvasItem(QSGItem *parent) -// : TextureItem(parent) -// , m_textureProvider(0) -// , m_context2dChanged(false) -// , m_context2d( new QSGContext2D(this)) -// , m_fillMode(QSGCanvasItem::Stretch) -// , m_color(Qt::white) -//{ -// m_textureProvider = new CanvasItemTextureProvider(this); -// m_textureProvider->setContext2D(m_context2d); -// setTextureProvider(m_textureProvider, true); -// setFlag(QSGItem::ItemHasContents, true); -//} - -//QSGCanvasItem::~QSGCanvasItem() -//{ -//} - -//void QSGCanvasItem::componentComplete() -//{ -// m_context2d->setSize(width(), height()); -// qDebug() << "m_context2d.size:" << m_context2d->size(); -// connect(m_context2d, SIGNAL(changed()), this, SLOT(requestPaint())); -// QScriptEngine* scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(this)); -// if (scriptEngine != m_context2d->scriptEngine()) -// m_context2d->setScriptEngine(scriptEngine); -// QSGItem::componentComplete(); -//} - - -//void QSGCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -//{ -// if (width() == 0 && height() -// && newGeometry.width() > 0 && newGeometry.height() > 0) { -// m_context2d->setSize(width(), height()); -// } -// TextureItem::geometryChanged(newGeometry, oldGeometry); -//} - -//void QSGCanvasItem::setFillMode(FillMode mode) -//{ -// if (m_fillMode == mode) -// return; - -// m_fillMode = mode; -// update(); -// emit fillModeChanged(); -//} - -//QColor QSGCanvasItem::color() -//{ -// return m_color; -//} - -//void QSGCanvasItem::setColor(const QColor &color) -//{ -// if (m_color !=color) { -// m_color = color; -// colorChanged(); -// } -//} - -//QSGCanvasItem::FillMode QSGCanvasItem::fillMode() const -//{ -// return m_fillMode; -//} - - - -//Node *QSGCanvasItem::updatePaintNode(Node *oldNode, UpdatePaintNodeData *data) -//{ -// if (width() <= 0 || height() <= 0) { -// delete oldNode; -// return 0; -// } - -// TextureNodeInterface *node = static_cast(oldNode); - -// if (node && m_context2d->isDirty()) { -// QRectF bounds = boundingRect(); - -// if (m_textureProvider) { -// m_textureProvider->setRect(QRectF(bounds.x(), bounds.y(), width(), height())); - -// m_textureProvider->setSize(QSize(width(), height())); -// //m_textureProvider->setOpaque(true); -// m_textureProvider->setHorizontalWrapMode(QSGTextureProvider::ClampToEdge); -// m_textureProvider->setVerticalWrapMode(QSGTextureProvider::ClampToEdge); -// node->setTargetRect(bounds); -// node->setSourceRect(QRectF(0, 0, 1, 1)); -// // node->setTargetRect(image.rect()); -//// node->setSourceRect(QRectF(0, 0, 1, 1)); -//// d->textureProvider->setHorizontalWrapMode(QSGTextureProvider::ClampToEdge); -//// d->textureProvider->setVerticalWrapMode(QSGTextureProvider::ClampToEdge); -//// d->textureProvider->setFiltering(d->smooth ? QSGTextureProvider::Linear : QSGTextureProvider::Nearest); -// } - -// if (m_context2dChanged) { -// //force textnode update the content -// node->setTexture(0); -// node->setTexture(m_textureProvider); -// m_context2dChanged = false; -// } -// } else { -// if (m_context2d->requireCachedImage()) -// m_context2d->setCachedImage(QImage(width(), height(), QImage::Format_ARGB32_Premultiplied)); -// } - -// return TextureItem::updatePaintNode(oldNode, data); -//} QT_END_NAMESPACE diff --git a/src/declarative/items/qsgcanvasitem_p.h b/src/declarative/items/qsgcanvasitem_p.h index a358c353ea..b501901569 100644 --- a/src/declarative/items/qsgcanvasitem_p.h +++ b/src/declarative/items/qsgcanvasitem_p.h @@ -55,6 +55,7 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE QT_MODULE(Declarative) +class QScriptValue; class QSGContext2D; class QSGCanvasItemPrivate; class QSGCanvasItem : public QSGPaintedItem @@ -68,7 +69,7 @@ signals: void canvasUpdated(); public Q_SLOTS: QString toDataURL(const QString& type = QLatin1String("image/png")) const; - QSGContext2D* getContext(const QString & = QLatin1String("2d")); + QScriptValue getContext(const QString & = QLatin1String("2d")); void requestPaint(); // Save current canvas to disk diff --git a/src/declarative/items/qsgcontext2d.cpp b/src/declarative/items/qsgcontext2d.cpp index 6f7121ac30..2e60de189f 100644 --- a/src/declarative/items/qsgcontext2d.cpp +++ b/src/declarative/items/qsgcontext2d.cpp @@ -366,6 +366,536 @@ static QString compositeOperatorToString(QPainter::CompositionMode op) return QString(); } + + #include + +//static QtScript functions +static QScriptValue ctx2d_sync(QScriptContext *c, QScriptEngine* e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + ctx2d->sync(); + return e->nullValue(); +} + + +// back-reference to the canvas, getter +static QScriptValue ctx2d_canvas(QScriptContext *c, QScriptEngine* e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + return e->newQObject(ctx2d->canvas()); +} + +// state +static QScriptValue ctx2d_restore(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + ctx2d->restore(); + return e->nullValue(); +} + +static QScriptValue ctx2d_save(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + ctx2d->save(); + return e->nullValue(); +} + +// transformations +static QScriptValue ctx2d_rotate(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) { + ctx2d->rotate(c->argument(0).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_scale(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 2) { + ctx2d->scale(c->argument(0).toNumber(), c->argument(1).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_setTransform(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 6) { + ctx2d->setTransform(c->argument(0).toNumber() + , c->argument(1).toNumber() + , c->argument(2).toNumber() + , c->argument(3).toNumber() + , c->argument(4).toNumber() + , c->argument(5).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_transform(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 6) { + ctx2d->transform(c->argument(0).toNumber() + , c->argument(1).toNumber() + , c->argument(2).toNumber() + , c->argument(3).toNumber() + , c->argument(4).toNumber() + , c->argument(5).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_translate(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 2) { + ctx2d->translate(c->argument(0).toNumber() + , c->argument(1).toNumber()); + } + return e->nullValue(); +} + +// compositing +// float getter/setter default 1.0 +static QScriptValue ctx2d_globalAlpha(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setGlobalAlpha(c->argument(0).toNumber()); + } + return e->toScriptValue(ctx2d->globalAlpha()); +} + +// string getter/setter default "source-over" +static QScriptValue ctx2d_globalCompositeOperation(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (ctx2d) { + if (c->argumentCount() == 1) {//setter + ctx2d->setGlobalCompositeOperation(c->argument(0).toString()); + } + return e->toScriptValue(ctx2d->globalCompositeOperation()); + } + return e->nullValue(); +} + +// colors and styles +// getter/setter +static QScriptValue ctx2d_fillStyle(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setFillStyle(c->argument(0).toVariant()); + } + return e->toScriptValue(ctx2d->fillStyle()); +} + +// colors and styles +// getter/setter +static QScriptValue ctx2d_fillColor(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setFillColor(c->argument(0).toVariant().value()); + } + return e->toScriptValue(ctx2d->fillColor()); +} + + +//getter/setter +static QScriptValue ctx2d_strokeStyle(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setStrokeStyle(c->argument(0).toVariant()); + } + return e->toScriptValue(ctx2d->strokeStyle()); +} + +// colors and styles +// getter/setter +static QScriptValue ctx2d_strokeColor(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setStrokeColor(c->argument(0).toVariant().value()); + } + return e->toScriptValue(ctx2d->strokeColor()); +} + +static QScriptValue ctx2d_createLinearGradient(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 4) { + QObject* gradient = ctx2d->createLinearGradient( c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber()); + return e->toScriptValue(gradient); + } + return e->nullValue(); +} +static QScriptValue ctx2d_createRadialGradient(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 6) { + QObject* gradient = ctx2d->createRadialGradient( c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber() + ,c->argument(4).toNumber() + ,c->argument(5).toNumber()); + return e->toScriptValue(gradient); + } + return e->nullValue(); +} +static QScriptValue ctx2d_createPattern(QScriptContext *c, QScriptEngine *e) +{ + //TODO + return e->nullValue(); +} + +// line styles +// string getter/setter +static QScriptValue ctx2d_lineCap(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setLineCap(c->argument(0).toString()); + } + return e->toScriptValue(ctx2d->lineCap()); +} + +// string getter/setter +static QScriptValue ctx2d_lineJoin(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setLineJoin(c->argument(0).toString()); + } + return e->toScriptValue(ctx2d->lineJoin()); +} +// float getter/setter +static QScriptValue ctx2d_lineWidth(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setLineWidth(c->argument(0).toNumber()); + } + return e->toScriptValue(ctx2d->lineWidth()); +} +// float getter/setter +static QScriptValue ctx2d_miterLimit(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setMiterLimit(c->argument(0).toNumber()); + } + return e->toScriptValue(ctx2d->miterLimit()); +} + +// shadows +// float getter/setter +static QScriptValue ctx2d_shadowBlur(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setShadowBlur(c->argument(0).toNumber()); + } + return e->toScriptValue(ctx2d->shadowBlur()); +} +static QScriptValue ctx2d_shadowColor(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setShadowColor(c->argument(0).toString()); + } + return e->toScriptValue(ctx2d->shadowColor()); +} +static QScriptValue ctx2d_shadowOffsetX(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setShadowOffsetX(c->argument(0).toNumber()); + } + return e->toScriptValue(ctx2d->shadowOffsetX()); +} + +static QScriptValue ctx2d_shadowOffsetY(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 1) {//setter + ctx2d->setShadowOffsetY(c->argument(0).toNumber()); + } + return e->toScriptValue(ctx2d->shadowOffsetY()); +} + +//rects +static QScriptValue ctx2d_clearRect(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 4) { + ctx2d->clearRect(c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_fillRect(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 4) { + ctx2d->fillRect(c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_strokeRect(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 4) { + ctx2d->strokeRect(c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber()); + } + return e->nullValue(); +} + +// Complex shapes (paths) API +static QScriptValue ctx2d_arc(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 6) { + ctx2d->arc(c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber() + ,c->argument(4).toNumber() + ,c->argument(5).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_arcTo(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 5) { + ctx2d->arcTo(c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber() + ,c->argument(4).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_beginPath(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + ctx2d->beginPath(); + return e->nullValue(); +} +static QScriptValue ctx2d_bezierCurveTo(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 5) { + ctx2d->bezierCurveTo(c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber() + ,c->argument(4).toNumber() + ,c->argument(5).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_clip(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + ctx2d->clip(); + return e->nullValue(); +} +static QScriptValue ctx2d_closePath(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + ctx2d->closePath(); + return e->nullValue(); +} +static QScriptValue ctx2d_fill(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + ctx2d->fill(); + return e->nullValue(); +} +static QScriptValue ctx2d_lineTo(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 2) { + ctx2d->lineTo(c->argument(0).toNumber() + ,c->argument(1).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_moveTo(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 2) { + ctx2d->moveTo(c->argument(0).toNumber() + ,c->argument(1).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_quadraticCurveTo(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 4) { + ctx2d->quadraticCurveTo(c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_rect(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 4) { + ctx2d->rect(c->argument(0).toNumber() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_stroke(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + ctx2d->stroke(); + return e->nullValue(); +} +static QScriptValue ctx2d_isPointInPath(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + bool pointInPath = false; + if (c->argumentCount() == 2) { + pointInPath = ctx2d->isPointInPath(c->argument(0).toNumber() + ,c->argument(1).toNumber()); + } + return e->toScriptValue(pointInPath); +} + +// text +static QScriptValue ctx2d_font(QScriptContext *c, QScriptEngine *e) +{ +} +static QScriptValue ctx2d_textAlign(QScriptContext *c, QScriptEngine *e) +{ +} +static QScriptValue ctx2d_textBaseline(QScriptContext *c, QScriptEngine *e) +{ +} +static QScriptValue ctx2d_fillText(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 3) { + ctx2d->fillText(c->argument(0).toString() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber()); + } + return e->nullValue(); +} +static QScriptValue ctx2d_strokeText(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 3) { + ctx2d->strokeText(c->argument(0).toString() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber()); + } + return e->nullValue(); +} + +// drawing images +static QScriptValue ctx2d_drawImage(QScriptContext *c, QScriptEngine *e) +{ + QSGContext2D* ctx2d = qscriptvalue_cast(c->thisObject().data()); + if (c->argumentCount() == 3) { + ctx2d->drawImage(c->argument(0).toString() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber()); + } else if (c->argumentCount() == 5) { + ctx2d->drawImage(c->argument(0).toString() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber() + ,c->argument(4).toNumber()); + } else if (c->argumentCount() == 9) { + ctx2d->drawImage(c->argument(0).toString() + ,c->argument(1).toNumber() + ,c->argument(2).toNumber() + ,c->argument(3).toNumber() + ,c->argument(4).toNumber() + ,c->argument(5).toNumber() + ,c->argument(6).toNumber() + ,c->argument(7).toNumber() + ,c->argument(8).toNumber()); + } + return e->nullValue(); +} + +// pixel manipulation +static QScriptValue ctx2d_createImageData(QScriptContext *c, QScriptEngine *e) +{ + //#TODO +} +static QScriptValue ctx2d_getImageData(QScriptContext *c, QScriptEngine *e) +{ + //#TODO +} +static QScriptValue ctx2d_putImageData(QScriptContext *c, QScriptEngine *e) +{ + //#TODO +} + +//Image Data Interface +static QScriptValue ctx2d_imageData_data(QScriptContext *c, QScriptEngine *e) +{ + //#TODO +} +static QScriptValue ctx2d_imageData_height(QScriptContext *c, QScriptEngine *e) +{ + //#TODO +} +static QScriptValue ctx2d_imageData_width(QScriptContext *c, QScriptEngine *e) +{ + //#TODO +} + +//CanvasPixelArray interface +static QScriptValue ctx2d_pixelArray_length(QScriptContext *c, QScriptEngine *e) +{ + //#TODO +} +//getter/setter by index how to? +static QScriptValue ctx2d_pixelArray(QScriptContext *c, QScriptEngine *e) +{ + //#TODO +} + +//CanvasGradient interface +static QScriptValue ctx2d_gradient_addColorStop(QScriptContext *c, QScriptEngine *e) +{ + //#TODO + +} + +//TextMetrics +static QScriptValue ctx2d_textMetrics_width(QScriptContext *c, QScriptEngine *e) +{ + //#TODO + +} + + bool QSGContext2DPrivate::hasShadow() const { return state.shadowColor.isValid() @@ -1623,6 +2153,11 @@ QSGContext2D::QSGContext2D(QSGContext2D *orig, QSGContext2DWorkerAgent* agentDat d->canvas = qobject_cast(orig); } +QSGCanvasItem* QSGContext2D::canvas() const +{ + Q_D(const QSGContext2D); + return d->canvas; +} QSGContext2D::~QSGContext2D() { Q_D(QSGContext2D); @@ -1649,24 +2184,60 @@ void QSGContext2D::setScriptEngine(QScriptEngine *eng) Q_D(QSGContext2D); if (d->scriptEngine != eng) { d->scriptEngine = eng; -// QScriptValue agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent")); -// if (!agent.isValid()) { -// d->scriptEngine->evaluate(QLatin1String( -// "(function CanvasImageData(w, h, d) {" -// " this.widht = w;" -// " this.height = h;" -// " this.data = d;" -// " })")); -// d->scriptEngine->evaluate(agentScript()); -// agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent")); -// if (!agent.isValid()) { -// qWarning() << "QSGContext2D:error when evaluating context2d script value!"; -// d->scriptValue = QScriptValue(); -// return; -// } -// } -// QScriptValue o = d->scriptEngine->newQObject(this); -// d->scriptValue = agent.construct(QScriptValueList() << o); + d->scriptValue = eng->newObject(); + d->scriptValue.setData(eng->toScriptValue(this)); + d->scriptValue.setProperty(QLatin1String("sync"), eng->newFunction(ctx2d_sync)); + d->scriptValue.setProperty(QLatin1String("canvas"), eng->newFunction(ctx2d_canvas),QScriptValue::PropertyGetter); + d->scriptValue.setProperty(QLatin1String("restore"), eng->newFunction(ctx2d_restore)); + d->scriptValue.setProperty(QLatin1String("save"), eng->newFunction(ctx2d_save)); + d->scriptValue.setProperty(QLatin1String("rotate"), eng->newFunction(ctx2d_rotate, 1)); + d->scriptValue.setProperty(QLatin1String("scale"), eng->newFunction(ctx2d_scale, 2)); + d->scriptValue.setProperty(QLatin1String("setTransform"), eng->newFunction(ctx2d_setTransform, 6)); + d->scriptValue.setProperty(QLatin1String("transform"), eng->newFunction(ctx2d_transform, 6)); + d->scriptValue.setProperty(QLatin1String("translate"), eng->newFunction(ctx2d_translate, 2)); + d->scriptValue.setProperty(QLatin1String("globalAlpha"), eng->newFunction(ctx2d_globalAlpha), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("globalCompositeOperation"), eng->newFunction(ctx2d_globalCompositeOperation), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("fillStyle"), eng->newFunction(ctx2d_fillStyle), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("strokeStyle"), eng->newFunction(ctx2d_strokeStyle), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("fillColor"), eng->newFunction(ctx2d_fillColor), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("strokeColor"), eng->newFunction(ctx2d_strokeColor), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("createLinearGradient"), eng->newFunction(ctx2d_createLinearGradient, 4)); + d->scriptValue.setProperty(QLatin1String("createRadialGradient"), eng->newFunction(ctx2d_createRadialGradient, 6)); + d->scriptValue.setProperty(QLatin1String("createPattern"), eng->newFunction(ctx2d_createPattern, 2)); + d->scriptValue.setProperty(QLatin1String("lineCap"), eng->newFunction(ctx2d_lineCap), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("lineJoin"), eng->newFunction(ctx2d_lineJoin), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("lineWidth"), eng->newFunction(ctx2d_lineWidth), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("miterLimit"), eng->newFunction(ctx2d_miterLimit), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("shadowBlur"), eng->newFunction(ctx2d_shadowBlur), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("shadowColor"), eng->newFunction(ctx2d_shadowColor), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("shadowOffsetX"), eng->newFunction(ctx2d_shadowOffsetX), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("shadowOffsetY"), eng->newFunction(ctx2d_shadowOffsetY), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("clearRect"), eng->newFunction(ctx2d_clearRect, 4)); + d->scriptValue.setProperty(QLatin1String("fillRect"), eng->newFunction(ctx2d_fillRect, 4)); + d->scriptValue.setProperty(QLatin1String("strokeRect"), eng->newFunction(ctx2d_strokeRect, 4)); + d->scriptValue.setProperty(QLatin1String("arc"), eng->newFunction(ctx2d_arc, 6)); + d->scriptValue.setProperty(QLatin1String("arcTo"), eng->newFunction(ctx2d_arcTo, 5)); + d->scriptValue.setProperty(QLatin1String("beginPath"), eng->newFunction(ctx2d_beginPath)); + d->scriptValue.setProperty(QLatin1String("bezierCurveTo"), eng->newFunction(ctx2d_bezierCurveTo, 6)); + d->scriptValue.setProperty(QLatin1String("clip"), eng->newFunction(ctx2d_clip)); + d->scriptValue.setProperty(QLatin1String("closePath"), eng->newFunction(ctx2d_closePath)); + d->scriptValue.setProperty(QLatin1String("fill"), eng->newFunction(ctx2d_fill)); + d->scriptValue.setProperty(QLatin1String("lineTo"), eng->newFunction(ctx2d_lineTo, 2)); + d->scriptValue.setProperty(QLatin1String("moveTo"), eng->newFunction(ctx2d_moveTo, 2)); + d->scriptValue.setProperty(QLatin1String("quadraticCurveTo"), eng->newFunction(ctx2d_quadraticCurveTo, 4)); + d->scriptValue.setProperty(QLatin1String("rect"), eng->newFunction(ctx2d_rect, 4)); + d->scriptValue.setProperty(QLatin1String("stroke"), eng->newFunction(ctx2d_stroke)); + d->scriptValue.setProperty(QLatin1String("isPointInPath"), eng->newFunction(ctx2d_isPointInPath, 2)); + d->scriptValue.setProperty(QLatin1String("font"), eng->newFunction(ctx2d_font), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("textAlign"), eng->newFunction(ctx2d_textAlign), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("textBaseline"), eng->newFunction(ctx2d_textBaseline), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + d->scriptValue.setProperty(QLatin1String("fillText"), eng->newFunction(ctx2d_fillText, 4)); + //d->scriptValue.setProperty(QLatin1String("measureText"), eng->newFunction(ctx2d_measureText, 1)); + d->scriptValue.setProperty(QLatin1String("strokeText"), eng->newFunction(ctx2d_strokeText, 4)); + d->scriptValue.setProperty(QLatin1String("drawImage"), eng->newFunction(ctx2d_drawImage, 9)); + d->scriptValue.setProperty(QLatin1String("createImageData"), eng->newFunction(ctx2d_createImageData, 2)); + d->scriptValue.setProperty(QLatin1String("getImageData"), eng->newFunction(ctx2d_getImageData, 4)); + d->scriptValue.setProperty(QLatin1String("putImageData"), eng->newFunction(ctx2d_putImageData, 7)); } } @@ -2380,6 +2951,7 @@ void QSGContext2D::paint(QPainter* p) qreal y = d->reals[real_idx++]; qreal w = d->reals[real_idx++]; qreal h = d->reals[real_idx++]; +// qDebug() << "fillRect(" << x << y << w << h << ")"; if (d->hasShadow()) d->fillRectShadow(p, QRectF(x, y, w, h)); else diff --git a/src/declarative/items/qsgcontext2d_p.h b/src/declarative/items/qsgcontext2d_p.h index 1a900100de..335f71b7e0 100644 --- a/src/declarative/items/qsgcontext2d_p.h +++ b/src/declarative/items/qsgcontext2d_p.h @@ -85,38 +85,9 @@ public: Q_DECLARE_METATYPE(QSGCanvasGradient*) -//class QSGCanvasImage: public QObject -//{ -// Q_OBJECT -// Q_PROPERTY(QString src READ src WRITE setSrc NOTIFY sourceChanged) -//public: -// QSGCanvasImage() {} -// QSGCanvasImage(const QString &url) : m_image(url), m_src(url) { -// } -// QSGCanvasImage(const QImage &img) {m_image = img;} - -//public slots: -// QImage &value() { return m_image; } -// const QImage &value() const{ return m_image; } -// QString src() { return m_src; } -// void setSrc(const QString &src) { m_src = src; m_image.load(src); emit sourceChanged();} -//signals: -// void sourceChanged(); - -//private: -// QSGImage* img; -// QString src; -//}; - -//Q_DECLARE_METATYPE(QSGCanvasImage*) - - -/* - - */ - class QSGContext2DWorkerAgent; class QSGContext2DPrivate; +class QSGCanvasItem; class QSGContext2D : public QObject { Q_OBJECT @@ -215,6 +186,9 @@ public: QSGContext2D(QObject *parent = 0); QSGContext2D(QSGContext2D *ctx2d, QSGContext2DWorkerAgent* agentData); ~QSGContext2D(); + + QSGCanvasItem* canvas() const; + void setSize(int width, int height); void setSize(const QSize &size); QSize size() const; From 21d0c6ef9a7df3e5fa69ff344e9dee2d2159c43c Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Tue, 7 Jun 2011 15:12:53 +1000 Subject: [PATCH 07/48] Immense Particles Refactor Part A Qt.labs.particles 2.0 moved to QtQuick.Particles 2.0. All C++ classes changed names, some renaming of QML types. Also adds CustomParticle. --- src/declarative/declarative.pro | 1 + src/declarative/items/items.pri | 6 + src/declarative/items/qsgitemsmodule.cpp | 4 + .../items/qsgsprite.cpp} | 4 +- .../items/qsgsprite_p.h} | 8 +- .../items/qsgspriteengine.cpp} | 44 +- .../items/qsgspriteengine_p.h} | 40 +- .../items/qsgspriteimage.cpp} | 46 +- .../items/qsgspriteimage_p.h} | 20 +- .../defaultshaders}/ctfragment.shader | 0 .../particles/defaultshaders}/ctvertex.shader | 0 .../defaultshaders}/defaultFadeInOut.png | Bin .../defaultshaders}/deformablefragment.shader | 0 .../defaultshaders}/deformablevertex.shader | 0 .../defaultshaders}/identitytable.png | Bin .../defaultshaders}/simplefragment.shader | 0 .../defaultshaders}/simplevertex.shader | 0 .../defaultshaders}/spritefragment.shader | 0 .../spriteimagefragment.shader | 0 .../defaultshaders}/spriteimagevertex.shader | 0 .../defaultshaders}/spritevertex.shader | 0 .../defaultshaders}/superfragment.shader | 0 .../defaultshaders}/supervertex.shader | 0 .../defaultshaders}/trailsfragment.shader | 0 .../defaultshaders}/trailsvertex.shader | 0 .../defaultshaders}/ultrafragment.shader | 0 .../defaultshaders}/ultravertex.shader | 0 src/declarative/particles/particles.pri | 60 ++ src/declarative/particles/particles.qrc | 22 + .../particles/qsgangleddirection.cpp} | 8 +- .../particles/qsgangleddirection_p.h} | 12 +- .../particles/qsgcustomparticle.cpp | 581 ++++++++++++++++++ .../particles/qsgcustomparticle_p.h | 114 ++++ .../particles/qsgellipseextruder.cpp} | 10 +- .../particles/qsgellipseextruder_p.h} | 6 +- .../particles/qsgemitter.cpp} | 18 +- .../particles/qsgemitter_p.h} | 9 +- .../particles/qsgfollowemitter.cpp} | 22 +- .../particles/qsgfollowemitter_p.h} | 22 +- .../particles/qsgfriction.cpp} | 8 +- .../particles/qsgfriction_p.h} | 8 +- .../particles/qsggravity.cpp} | 10 +- .../particles/qsggravity_p.h} | 8 +- .../particles/qsgimageparticle.cpp} | 96 +-- .../particles/qsgimageparticle_p.h} | 56 +- .../particles/qsgitemparticle.cpp} | 48 +- .../particles/qsgitemparticle_p.h} | 30 +- .../particles/qsgkill.cpp} | 10 +- .../particles/qsgkill_p.h} | 8 +- .../particles/qsglineextruder.cpp} | 8 +- .../particles/qsglineextruder_p.h} | 6 +- .../particles/qsgmaskextruder.cpp} | 12 +- .../particles/qsgmaskextruder_p.h} | 6 +- .../particles/qsgmodelparticle.cpp} | 50 +- .../particles/qsgmodelparticle_p.h} | 30 +- .../particles/qsgparticleaffector.cpp} | 20 +- .../particles/qsgparticleaffector_p.h} | 32 +- .../particles/qsgparticleemitter.cpp} | 24 +- .../particles/qsgparticleemitter_p.h} | 58 +- .../particles/qsgparticleextruder.cpp} | 8 +- .../particles/qsgparticleextruder_p.h} | 4 +- .../particles/qsgparticlepainter.cpp} | 26 +- .../particles/qsgparticlepainter_p.h} | 26 +- .../particles/qsgparticlesmodule.cpp | 111 ++++ .../particles/qsgparticlesmodule_p.h} | 28 +- .../particles/qsgparticlesystem.cpp} | 92 +-- .../particles/qsgparticlesystem_p.h} | 46 +- .../particles/qsgpointattractor.cpp} | 8 +- .../particles/qsgpointattractor_p.h} | 8 +- .../particles/qsgpointdirection.cpp} | 8 +- .../particles/qsgpointdirection_p.h} | 6 +- .../particles/qsgspritegoal.cpp} | 26 +- .../particles/qsgspritegoal_p.h} | 14 +- .../particles/qsgstochasticdirection.cpp} | 6 +- .../particles/qsgstochasticdirection_p.h} | 4 +- .../particles/qsgtargeteddirection.cpp} | 12 +- .../particles/qsgtargeteddirection_p.h} | 6 +- .../particles/qsgturbulence.cpp} | 20 +- .../particles/qsgturbulence_p.h} | 10 +- .../particles/qsgwander.cpp} | 16 +- .../particles/qsgwander_p.h} | 12 +- src/declarative/qml/qdeclarativeengine.cpp | 2 + src/imports/particles/burstemitter.h | 67 -- src/imports/particles/coloredparticle.cpp | 540 ---------------- src/imports/particles/coloredparticle.h | 254 -------- src/imports/particles/deformableparticle.cpp | 438 ------------- src/imports/particles/deformableparticle.h | 235 ------- src/imports/particles/driftaffector.cpp | 67 -- src/imports/particles/driftaffector.h | 104 ---- src/imports/particles/eternalaffector.cpp | 60 -- .../gravitationalsingularityaffector.cpp | 179 ------ .../gravitationalsingularityaffector.h | 121 ---- src/imports/particles/main.cpp | 160 ----- src/imports/particles/meanderaffector.cpp | 65 -- src/imports/particles/meanderaffector.h | 103 ---- .../{eternalaffector.h => particles.cpp} | 51 +- src/imports/particles/particles.pro | 109 +--- src/imports/particles/pictureaffector.cpp | 118 ---- src/imports/particles/pictureaffector.h | 99 --- src/imports/particles/pluginmain.h | 65 -- src/imports/particles/resetaffector.cpp | 78 --- src/imports/particles/resetaffector.h | 75 --- src/imports/particles/speedlimitaffector.cpp | 77 --- src/imports/particles/speedlimitaffector.h | 89 --- src/imports/particles/spriteparticle.cpp | 449 -------------- src/imports/particles/spriteparticle.h | 99 --- src/imports/particles/spriteparticles.qrc | 22 - src/imports/particles/superparticle.cpp | 511 --------------- src/imports/particles/superparticle.h | 389 ------------ src/imports/particles/swarmaffector.cpp | 114 ---- src/imports/particles/swarmaffector.h | 116 ---- src/imports/particles/toggleaffector.cpp | 59 -- src/imports/particles/toggleaffector.h | 102 --- src/imports/particles/zoneaffector.cpp | 68 -- src/imports/particles/zoneaffector.h | 159 ----- 115 files changed, 1514 insertions(+), 5812 deletions(-) rename src/{imports/particles/spritestate.cpp => declarative/items/qsgsprite.cpp} (96%) rename src/{imports/particles/spritestate.h => declarative/items/qsgsprite_p.h} (97%) rename src/{imports/particles/spriteengine.cpp => declarative/items/qsgspriteengine.cpp} (92%) rename src/{imports/particles/spriteengine.h => declarative/items/qsgspriteengine_p.h} (74%) rename src/{imports/particles/spriteimage.cpp => declarative/items/qsgspriteimage.cpp} (87%) rename src/{imports/particles/spriteimage.h => declarative/items/qsgspriteimage_p.h} (86%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/ctfragment.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/ctvertex.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/defaultFadeInOut.png (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/deformablefragment.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/deformablevertex.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/identitytable.png (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/simplefragment.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/simplevertex.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/spritefragment.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/spriteimagefragment.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/spriteimagevertex.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/spritevertex.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/superfragment.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/supervertex.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/trailsfragment.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/trailsvertex.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/ultrafragment.shader (100%) rename src/{imports/particles/resources => declarative/particles/defaultshaders}/ultravertex.shader (100%) create mode 100644 src/declarative/particles/particles.pri create mode 100644 src/declarative/particles/particles.qrc rename src/{imports/particles/angledvector.cpp => declarative/particles/qsgangleddirection.cpp} (91%) rename src/{imports/particles/angledvector.h => declarative/particles/qsgangleddirection_p.h} (93%) create mode 100644 src/declarative/particles/qsgcustomparticle.cpp create mode 100644 src/declarative/particles/qsgcustomparticle_p.h rename src/{imports/particles/ellipseextruder.cpp => declarative/particles/qsgellipseextruder.cpp} (88%) rename src/{imports/particles/ellipseextruder.h => declarative/particles/qsgellipseextruder_p.h} (93%) rename src/{imports/particles/trailsemitter.cpp => declarative/particles/qsgemitter.cpp} (93%) rename src/{imports/particles/trailsemitter.h => declarative/particles/qsgemitter_p.h} (93%) rename src/{imports/particles/followemitter.cpp => declarative/particles/qsgfollowemitter.cpp} (91%) rename src/{imports/particles/followemitter.h => declarative/particles/qsgfollowemitter_p.h} (88%) rename src/{imports/particles/frictionaffector.cpp => declarative/particles/qsgfriction.cpp} (89%) rename src/{imports/particles/frictionaffector.h => declarative/particles/qsgfriction_p.h} (90%) rename src/{imports/particles/gravityaffector.cpp => declarative/particles/qsggravity.cpp} (88%) rename src/{imports/particles/gravityaffector.h => declarative/particles/qsggravity_p.h} (92%) rename src/{imports/particles/ultraparticle.cpp => declarative/particles/qsgimageparticle.cpp} (91%) rename src/{imports/particles/ultraparticle.h => declarative/particles/qsgimageparticle_p.h} (87%) rename src/{imports/particles/itemparticle.cpp => declarative/particles/qsgitemparticle.cpp} (77%) rename src/{imports/particles/itemparticle.h => declarative/particles/qsgitemparticle_p.h} (81%) rename src/{imports/particles/killaffector.cpp => declarative/particles/qsgkill.cpp} (88%) rename src/{imports/particles/killaffector.h => declarative/particles/qsgkill_p.h} (89%) rename src/{imports/particles/lineextruder.cpp => declarative/particles/qsglineextruder.cpp} (91%) rename src/{imports/particles/lineextruder.h => declarative/particles/qsglineextruder_p.h} (93%) rename src/{imports/particles/maskextruder.cpp => declarative/particles/qsgmaskextruder.cpp} (90%) rename src/{imports/particles/maskextruder.h => declarative/particles/qsgmaskextruder_p.h} (94%) rename src/{imports/particles/dataparticle.cpp => declarative/particles/qsgmodelparticle.cpp} (81%) rename src/{imports/particles/dataparticle.h => declarative/particles/qsgmodelparticle_p.h} (83%) rename src/{imports/particles/particleaffector.cpp => declarative/particles/qsgparticleaffector.cpp} (87%) rename src/{imports/particles/particleaffector.h => declarative/particles/qsgparticleaffector_p.h} (83%) rename src/{imports/particles/particleemitter.cpp => declarative/particles/qsgparticleemitter.cpp} (87%) rename src/{imports/particles/particleemitter.h => declarative/particles/qsgparticleemitter_p.h} (82%) rename src/{imports/particles/particleextruder.cpp => declarative/particles/qsgparticleextruder.cpp} (91%) rename src/{imports/particles/particleextruder.h => declarative/particles/qsgparticleextruder_p.h} (96%) rename src/{imports/particles/particle.cpp => declarative/particles/qsgparticlepainter.cpp} (84%) rename src/{imports/particles/particle.h => declarative/particles/qsgparticlepainter_p.h} (84%) create mode 100644 src/declarative/particles/qsgparticlesmodule.cpp rename src/{imports/particles/burstemitter.cpp => declarative/particles/qsgparticlesmodule_p.h} (86%) rename src/{imports/particles/particlesystem.cpp => declarative/particles/qsgparticlesystem.cpp} (81%) rename src/{imports/particles/particlesystem.h => declarative/particles/qsgparticlesystem_p.h} (84%) rename src/{imports/particles/attractoraffector.cpp => declarative/particles/qsgpointattractor.cpp} (88%) rename src/{imports/particles/attractoraffector.h => declarative/particles/qsgpointattractor_p.h} (92%) rename src/{imports/particles/pointvector.cpp => declarative/particles/qsgpointdirection.cpp} (90%) rename src/{imports/particles/pointvector.h => declarative/particles/qsgpointdirection_p.h} (95%) rename src/{imports/particles/spritegoalaffector.cpp => declarative/particles/qsgspritegoal.cpp} (78%) rename src/{imports/particles/spritegoalaffector.h => declarative/particles/qsgspritegoal_p.h} (88%) rename src/{imports/particles/varyingvector.cpp => declarative/particles/qsgstochasticdirection.cpp} (90%) rename src/{imports/particles/varyingvector.h => declarative/particles/qsgstochasticdirection_p.h} (94%) rename src/{imports/particles/directedvector.cpp => declarative/particles/qsgtargeteddirection.cpp} (90%) rename src/{imports/particles/directedvector.h => declarative/particles/qsgtargeteddirection_p.h} (96%) rename src/{imports/particles/turbulenceaffector.cpp => declarative/particles/qsgturbulence.cpp} (91%) rename src/{imports/particles/turbulenceaffector.h => declarative/particles/qsgturbulence_p.h} (93%) rename src/{imports/particles/wanderaffector.cpp => declarative/particles/qsgwander.cpp} (89%) rename src/{imports/particles/wanderaffector.h => declarative/particles/qsgwander_p.h} (92%) delete mode 100644 src/imports/particles/burstemitter.h delete mode 100644 src/imports/particles/coloredparticle.cpp delete mode 100644 src/imports/particles/coloredparticle.h delete mode 100644 src/imports/particles/deformableparticle.cpp delete mode 100644 src/imports/particles/deformableparticle.h delete mode 100644 src/imports/particles/driftaffector.cpp delete mode 100644 src/imports/particles/driftaffector.h delete mode 100644 src/imports/particles/eternalaffector.cpp delete mode 100644 src/imports/particles/gravitationalsingularityaffector.cpp delete mode 100644 src/imports/particles/gravitationalsingularityaffector.h delete mode 100644 src/imports/particles/main.cpp delete mode 100644 src/imports/particles/meanderaffector.cpp delete mode 100644 src/imports/particles/meanderaffector.h rename src/imports/particles/{eternalaffector.h => particles.cpp} (59%) delete mode 100644 src/imports/particles/pictureaffector.cpp delete mode 100644 src/imports/particles/pictureaffector.h delete mode 100644 src/imports/particles/pluginmain.h delete mode 100644 src/imports/particles/resetaffector.cpp delete mode 100644 src/imports/particles/resetaffector.h delete mode 100644 src/imports/particles/speedlimitaffector.cpp delete mode 100644 src/imports/particles/speedlimitaffector.h delete mode 100644 src/imports/particles/spriteparticle.cpp delete mode 100644 src/imports/particles/spriteparticle.h delete mode 100644 src/imports/particles/spriteparticles.qrc delete mode 100644 src/imports/particles/superparticle.cpp delete mode 100644 src/imports/particles/superparticle.h delete mode 100644 src/imports/particles/swarmaffector.cpp delete mode 100644 src/imports/particles/swarmaffector.h delete mode 100644 src/imports/particles/toggleaffector.cpp delete mode 100644 src/imports/particles/toggleaffector.h delete mode 100644 src/imports/particles/zoneaffector.cpp delete mode 100644 src/imports/particles/zoneaffector.h diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro index adf1408c58..fc64fa07fb 100644 --- a/src/declarative/declarative.pro +++ b/src/declarative/declarative.pro @@ -33,6 +33,7 @@ include(qml/qml.pri) include(debugger/debugger.pri) include(scenegraph/scenegraph.pri) include(items/items.pri) +include(particles/particles.pri) symbian: { TARGET.UID3=0x2001E623 diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri index d6942973cd..f29a82e77e 100644 --- a/src/declarative/items/items.pri +++ b/src/declarative/items/items.pri @@ -61,6 +61,9 @@ HEADERS += \ $$PWD/qsgcanvasitem_p.h \ $$PWD/qsgcontext2d_p.h \ $$PWD/qsgcontext2d_p_p.h \ + $$PWD/qsgspriteengine_p.h \ + $$PWD/qsgsprite_p.h \ + $$PWD/qsgspriteimage_p.h \ SOURCES += \ $$PWD/qsgevents.cpp \ @@ -100,6 +103,9 @@ SOURCES += \ $$PWD/qsgimplicitsizeitem.cpp \ $$PWD/qsgcanvasitem.cpp \ $$PWD/qsgcontext2d.cpp \ + $$PWD/qsgspriteengine.cpp \ + $$PWD/qsgsprite.cpp \ + $$PWD/qsgspriteimage.cpp \ SOURCES += \ $$PWD/qsgshadereffectitem.cpp \ diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp index 6ea20bb38b..a29776fc68 100644 --- a/src/declarative/items/qsgitemsmodule.cpp +++ b/src/declarative/items/qsgitemsmodule.cpp @@ -75,6 +75,8 @@ //#include "private/qsgpincharea_p.h" #include "qsgcanvasitem_p.h" #include "qsgcontext2d_p.h" +#include "qsgsprite_p.h" +#include "qsgspriteimage_p.h" static QDeclarativePrivate::AutoParentResult qsgitem_autoParent(QObject *obj, QObject *parent) { @@ -179,6 +181,8 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(); qmlRegisterType(); + qmlRegisterType("QtQuick", 2, 0, "Sprite"); + qmlRegisterType("QtQuick", 2, 0, "SpriteImage"); qmlRegisterType(uri, major, minor,"ParentChange"); qmlRegisterType(uri, major, minor,"AnchorChanges"); diff --git a/src/imports/particles/spritestate.cpp b/src/declarative/items/qsgsprite.cpp similarity index 96% rename from src/imports/particles/spritestate.cpp rename to src/declarative/items/qsgsprite.cpp index 72535c0e73..694976a540 100644 --- a/src/imports/particles/spritestate.cpp +++ b/src/declarative/items/qsgsprite.cpp @@ -39,11 +39,11 @@ ** ****************************************************************************/ -#include "spritestate.h" +#include "qsgsprite_p.h" QT_BEGIN_NAMESPACE -SpriteState::SpriteState(QObject *parent) : +QSGSprite::QSGSprite(QObject *parent) : QObject(parent) , m_generatedCount(0) , m_framesPerRow(0) diff --git a/src/imports/particles/spritestate.h b/src/declarative/items/qsgsprite_p.h similarity index 97% rename from src/imports/particles/spritestate.h rename to src/declarative/items/qsgsprite_p.h index 5157a8bb31..652a4cd482 100644 --- a/src/imports/particles/spritestate.h +++ b/src/declarative/items/qsgsprite_p.h @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class SpriteState : public QObject +class QSGSprite : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) @@ -69,7 +69,7 @@ class SpriteState : public QObject Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged) public: - explicit SpriteState(QObject *parent = 0); + explicit QSGSprite(QObject *parent = 0); QUrl source() const { @@ -211,8 +211,8 @@ public slots: } private: - friend class SpriteParticle; - friend class SpriteEngine; + friend class QSGImageParticle; + friend class QSGSpriteEngine; int m_generatedCount; int m_framesPerRow; QUrl m_source; diff --git a/src/imports/particles/spriteengine.cpp b/src/declarative/items/qsgspriteengine.cpp similarity index 92% rename from src/imports/particles/spriteengine.cpp rename to src/declarative/items/qsgspriteengine.cpp index 7676d9ed46..27de0d94f6 100644 --- a/src/imports/particles/spriteengine.cpp +++ b/src/declarative/items/qsgspriteengine.cpp @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#include "spriteengine.h" -#include "spritestate.h" +#include "qsgspriteengine_p.h" +#include "qsgsprite_p.h" #include #include #include @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE -SpriteEngine::SpriteEngine(QObject *parent) : +QSGSpriteEngine::QSGSpriteEngine(QObject *parent) : QObject(parent), m_timeOffset(0) { //Default size 1 @@ -56,7 +56,7 @@ SpriteEngine::SpriteEngine(QObject *parent) : m_advanceTime.start(); } -SpriteEngine::SpriteEngine(QList states, QObject *parent) : +QSGSpriteEngine::QSGSpriteEngine(QList states, QObject *parent) : QObject(parent), m_states(states), m_timeOffset(0) { //Default size 1 @@ -64,11 +64,11 @@ SpriteEngine::SpriteEngine(QList states, QObject *parent) : m_advanceTime.start(); } -SpriteEngine::~SpriteEngine() +QSGSpriteEngine::~QSGSpriteEngine() { } -int SpriteEngine::maxFrames() +int QSGSpriteEngine::maxFrames() { return m_maxFrames; } @@ -79,7 +79,7 @@ int SpriteEngine::maxFrames() But States maintain their listed index for internal structures TODO: All these calculations should be pre-calculated and cached during initialization for a significant performance boost */ -int SpriteEngine::spriteState(int sprite) +int QSGSpriteEngine::spriteState(int sprite) { int state = m_sprites[sprite]; if(!m_states[state]->m_generatedCount) @@ -89,7 +89,7 @@ int SpriteEngine::spriteState(int sprite) return state + extra; } -int SpriteEngine::spriteStart(int sprite) +int QSGSpriteEngine::spriteStart(int sprite) { int state = m_sprites[sprite]; if(!m_states[state]->m_generatedCount) @@ -99,7 +99,7 @@ int SpriteEngine::spriteStart(int sprite) return state + extra*rowDuration; } -int SpriteEngine::spriteFrames(int sprite) +int QSGSpriteEngine::spriteFrames(int sprite) { int state = m_sprites[sprite]; if(!m_states[state]->m_generatedCount) @@ -112,7 +112,7 @@ int SpriteEngine::spriteFrames(int sprite) return m_states[state]->m_framesPerRow; } -int SpriteEngine::spriteDuration(int sprite) +int QSGSpriteEngine::spriteDuration(int sprite) { int state = m_sprites[sprite]; if(!m_states[state]->m_generatedCount) @@ -125,12 +125,12 @@ int SpriteEngine::spriteDuration(int sprite) return rowDuration; } -int SpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together +int QSGSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together { return m_imageStateCount; } -void SpriteEngine::setGoal(int state, int sprite, bool jump) +void QSGSpriteEngine::setGoal(int state, int sprite, bool jump) { if(sprite >= m_sprites.count() || state >= m_states.count()) return; @@ -147,7 +147,7 @@ void SpriteEngine::setGoal(int state, int sprite, bool jump) return; } -QImage SpriteEngine::assembledImage() +QImage QSGSpriteEngine::assembledImage() { int frameHeight = 0; int frameWidth = 0; @@ -157,7 +157,7 @@ QImage SpriteEngine::assembledImage() int maxSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); - foreach(SpriteState* state, m_states){ + foreach(QSGSprite* state, m_states){ if(state->frames() > m_maxFrames) m_maxFrames = state->frames(); @@ -215,7 +215,7 @@ QImage SpriteEngine::assembledImage() image.fill(0); QPainter p(&image); int y = 0; - foreach(SpriteState* state, m_states){ + foreach(QSGSprite* state, m_states){ QImage img(state->source().toLocalFile()); if(img.height() == frameHeight && img.width() < maxSize){//Simple case p.drawImage(0,y,img); @@ -262,14 +262,14 @@ QImage SpriteEngine::assembledImage() return image; } -void SpriteEngine::setCount(int c) +void QSGSpriteEngine::setCount(int c) { m_sprites.resize(c); m_goals.resize(c); m_startTimes.resize(c); } -void SpriteEngine::startSprite(int index) +void QSGSpriteEngine::startSprite(int index) { if(index >= m_sprites.count()) return; @@ -278,7 +278,7 @@ void SpriteEngine::startSprite(int index) restartSprite(index); } -void SpriteEngine::restartSprite(int index) +void QSGSpriteEngine::restartSprite(int index) { m_startTimes[index] = m_timeOffset + m_advanceTime.elapsed(); int time = m_states[m_sprites[index]]->duration() * m_states[m_sprites[index]]->frames() + m_startTimes[index]; @@ -287,7 +287,7 @@ void SpriteEngine::restartSprite(int index) addToUpdateList(time, index); } -uint SpriteEngine::updateSprites(uint time) +uint QSGSpriteEngine::updateSprites(uint time) { //Sprite State Update; while(!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){ @@ -341,7 +341,7 @@ uint SpriteEngine::updateSprites(uint time) return m_stateUpdates.first().first; } -int SpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist) +int QSGSpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist) { QString goalName; if(m_goals[spriteIdx] != -1) @@ -357,7 +357,7 @@ int SpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist) return curIdx; if(dist < 0) dist = m_states.count(); - SpriteState* curState = m_states[curIdx]; + QSGSprite* curState = m_states[curIdx]; for(QVariantMap::const_iterator iter = curState->m_to.constBegin(); iter!=curState->m_to.constEnd(); iter++){ if(iter.key() == goalName) @@ -416,7 +416,7 @@ int SpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist) return -1; } -void SpriteEngine::addToUpdateList(uint t, int idx) +void QSGSpriteEngine::addToUpdateList(uint t, int idx) { for(int i=0; i sprites READ sprites) + Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged) public: - explicit SpriteEngine(QObject *parent = 0); - SpriteEngine(QList sprites, QObject *parent=0); - ~SpriteEngine(); + explicit QSGSpriteEngine(QObject *parent = 0); + QSGSpriteEngine(QList sprites, QObject *parent=0); + ~QSGSpriteEngine(); - QDeclarativeListProperty sprites() + QDeclarativeListProperty sprites() { - return QDeclarativeListProperty(this, m_states); + return QDeclarativeListProperty(this, m_states); } QString globalGoal() const { @@ -95,10 +95,10 @@ public: void startSprite(int index=0); private://Nothing outside should use this? - friend class SpriteGoalAffector;//XXX: Fix interface + friend class QSGSpriteGoalAffector;//XXX: Fix interface int stateCount() {return m_states.count();} - int stateIndex(SpriteState* s){return m_states.indexOf(s);}//TODO: Does this need to be hidden? - SpriteState* state(int idx){return m_states[idx];}//Used by spritegoal affector + int stateIndex(QSGSprite* s){return m_states.indexOf(s);}//TODO: Does this need to be hidden? + QSGSprite* state(int idx){return m_states[idx];}//Used by spritegoal affector signals: void globalGoalChanged(QString arg); @@ -118,7 +118,7 @@ private: void restartSprite(int sprite); void addToUpdateList(uint t, int idx); int goalSeek(int curState, int spriteIdx, int dist=-1); - QList m_states; + QList m_states; QVector m_sprites;//int is the index in m_states of the current state QVector m_goals; QVector m_startTimes; @@ -132,26 +132,26 @@ private: }; //Common use is to have your own list property which is transparently an engine -inline void spriteAppend(QDeclarativeListProperty *p, SpriteState* s) +inline void spriteAppend(QDeclarativeListProperty *p, QSGSprite* s) { - reinterpret_cast *>(p->data)->append(s); + reinterpret_cast *>(p->data)->append(s); p->object->metaObject()->invokeMethod(p->object, "createEngine"); } -inline SpriteState* spriteAt(QDeclarativeListProperty *p, int idx) +inline QSGSprite* spriteAt(QDeclarativeListProperty *p, int idx) { - return reinterpret_cast *>(p->data)->at(idx); + return reinterpret_cast *>(p->data)->at(idx); } -inline void spriteClear(QDeclarativeListProperty *p) +inline void spriteClear(QDeclarativeListProperty *p) { - reinterpret_cast *>(p->data)->clear(); + reinterpret_cast *>(p->data)->clear(); p->object->metaObject()->invokeMethod(p->object, "createEngine"); } -inline int spriteCount(QDeclarativeListProperty *p) +inline int spriteCount(QDeclarativeListProperty *p) { - return reinterpret_cast *>(p->data)->count(); + return reinterpret_cast *>(p->data)->count(); } QT_END_NAMESPACE diff --git a/src/imports/particles/spriteimage.cpp b/src/declarative/items/qsgspriteimage.cpp similarity index 87% rename from src/imports/particles/spriteimage.cpp rename to src/declarative/items/qsgspriteimage.cpp index ea08ae4ea0..8cc0dc5b76 100644 --- a/src/imports/particles/spriteimage.cpp +++ b/src/declarative/items/qsgspriteimage.cpp @@ -39,9 +39,9 @@ ** ****************************************************************************/ -#include "spriteimage.h" -#include "spritestate.h" -#include "spriteengine.h" +#include "qsgspriteimage_p.h" +#include "qsgsprite_p.h" +#include "qsgspriteengine_p.h" #include #include #include @@ -55,16 +55,16 @@ QT_BEGIN_NAMESPACE -class SpriteMaterial : public QSGMaterial +class QSGSpriteMaterial : public QSGMaterial { public: - SpriteMaterial(); - virtual ~SpriteMaterial(); + QSGSpriteMaterial(); + virtual ~QSGSpriteMaterial(); virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } virtual QSGMaterialShader *createShader() const; virtual int compare(const QSGMaterial *other) const { - return this - static_cast(other); + return this - static_cast(other); } QSGTexture *texture; @@ -77,7 +77,7 @@ public: int height; }; -SpriteMaterial::SpriteMaterial() +QSGSpriteMaterial::QSGSpriteMaterial() : timestamp(0) , timelength(1) , framecount(1) @@ -88,7 +88,7 @@ SpriteMaterial::SpriteMaterial() setFlag(Blending, true); } -SpriteMaterial::~SpriteMaterial() +QSGSpriteMaterial::~QSGSpriteMaterial() { delete texture; } @@ -98,11 +98,11 @@ class SpriteMaterialData : public QSGMaterialShader public: SpriteMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) { - QFile vf(vertexFile ? vertexFile : ":resources/spriteimagevertex.shader"); + QFile vf(vertexFile ? vertexFile : ":defaultshaders/spriteimagevertex.shader"); vf.open(QFile::ReadOnly); m_vertex_code = vf.readAll(); - QFile ff(fragmentFile ? fragmentFile : ":resources/spriteimagefragment.shader"); + QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/spriteimagefragment.shader"); ff.open(QFile::ReadOnly); m_fragment_code = ff.readAll(); @@ -120,7 +120,7 @@ public: virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) { - SpriteMaterial *m = static_cast(newEffect); + QSGSpriteMaterial *m = static_cast(newEffect); m->texture->bind(); program()->setUniformValue(m_opacity_id, state.opacity()); @@ -173,7 +173,7 @@ public: }; float SpriteMaterialData::chunkOfBytes[1024]; -QSGMaterialShader *SpriteMaterial::createShader() const +QSGMaterialShader *QSGSpriteMaterial::createShader() const { return new SpriteMaterialData; } @@ -194,7 +194,7 @@ struct SpriteVertices { SpriteVertex v4; }; -SpriteImage::SpriteImage(QSGItem *parent) : +QSGSpriteImage::QSGSpriteImage(QSGItem *parent) : QSGItem(parent) , m_node(0) , m_material(0) @@ -207,18 +207,18 @@ SpriteImage::SpriteImage(QSGItem *parent) : this, SLOT(update())); } -QDeclarativeListProperty SpriteImage::sprites() +QDeclarativeListProperty QSGSpriteImage::sprites() { - return QDeclarativeListProperty(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); + return QDeclarativeListProperty(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); } -void SpriteImage::createEngine() +void QSGSpriteImage::createEngine() { //TODO: delay until component complete if(m_spriteEngine) delete m_spriteEngine; if(m_sprites.count()) - m_spriteEngine = new SpriteEngine(m_sprites, this); + m_spriteEngine = new QSGSpriteEngine(m_sprites, this); else m_spriteEngine = 0; reset(); @@ -236,7 +236,7 @@ static QSGGeometry::AttributeSet SpriteImage_AttributeSet = SpriteImage_Attributes }; -QSGGeometryNode* SpriteImage::buildNode() +QSGGeometryNode* QSGSpriteImage::buildNode() { if (!m_spriteEngine) { qWarning() << "SpriteImage: No sprite engine..."; @@ -248,7 +248,7 @@ QSGGeometryNode* SpriteImage::buildNode() m_material = 0; } - m_material = new SpriteMaterial(); + m_material = new QSGSpriteMaterial(); QImage image = m_spriteEngine->assembledImage(); if(image.isNull()) @@ -297,12 +297,12 @@ QSGGeometryNode* SpriteImage::buildNode() return m_node; } -void SpriteImage::reset() +void QSGSpriteImage::reset() { m_pleaseReset = true; } -QSGNode *SpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *) +QSGNode *QSGSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *) { if(m_pleaseReset){ delete m_node; @@ -324,7 +324,7 @@ QSGNode *SpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *) return m_node; } -void SpriteImage::prepareNextFrame() +void QSGSpriteImage::prepareNextFrame() { if (m_node == 0) m_node = buildNode(); diff --git a/src/imports/particles/spriteimage.h b/src/declarative/items/qsgspriteimage_p.h similarity index 86% rename from src/imports/particles/spriteimage.h rename to src/declarative/items/qsgspriteimage_p.h index cd73c97333..f03fd869f0 100644 --- a/src/imports/particles/spriteimage.h +++ b/src/declarative/items/qsgspriteimage_p.h @@ -52,22 +52,22 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QSGContext; -class SpriteState; -class SpriteEngine; +class QSGSprite; +class QSGSpriteEngine; class QSGGeometryNode; -class SpriteMaterial; -class SpriteImage : public QSGItem +class QSGSpriteMaterial; +class QSGSpriteImage : public QSGItem { Q_OBJECT Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged) //###try to share similar spriteEngines for less overhead? - Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) + Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) Q_CLASSINFO("DefaultProperty", "sprites") public: - explicit SpriteImage(QSGItem *parent = 0); + explicit QSGSpriteImage(QSGItem *parent = 0); - QDeclarativeListProperty sprites(); + QDeclarativeListProperty sprites(); bool running() const { @@ -98,9 +98,9 @@ private: void prepareNextFrame(); QSGGeometryNode* buildNode(); QSGGeometryNode *m_node; - SpriteMaterial *m_material; - QList m_sprites; - SpriteEngine* m_spriteEngine; + QSGSpriteMaterial *m_material; + QList m_sprites; + QSGSpriteEngine* m_spriteEngine; QTime m_timestamp; int m_maxFrames; bool m_pleaseReset; diff --git a/src/imports/particles/resources/ctfragment.shader b/src/declarative/particles/defaultshaders/ctfragment.shader similarity index 100% rename from src/imports/particles/resources/ctfragment.shader rename to src/declarative/particles/defaultshaders/ctfragment.shader diff --git a/src/imports/particles/resources/ctvertex.shader b/src/declarative/particles/defaultshaders/ctvertex.shader similarity index 100% rename from src/imports/particles/resources/ctvertex.shader rename to src/declarative/particles/defaultshaders/ctvertex.shader diff --git a/src/imports/particles/resources/defaultFadeInOut.png b/src/declarative/particles/defaultshaders/defaultFadeInOut.png similarity index 100% rename from src/imports/particles/resources/defaultFadeInOut.png rename to src/declarative/particles/defaultshaders/defaultFadeInOut.png diff --git a/src/imports/particles/resources/deformablefragment.shader b/src/declarative/particles/defaultshaders/deformablefragment.shader similarity index 100% rename from src/imports/particles/resources/deformablefragment.shader rename to src/declarative/particles/defaultshaders/deformablefragment.shader diff --git a/src/imports/particles/resources/deformablevertex.shader b/src/declarative/particles/defaultshaders/deformablevertex.shader similarity index 100% rename from src/imports/particles/resources/deformablevertex.shader rename to src/declarative/particles/defaultshaders/deformablevertex.shader diff --git a/src/imports/particles/resources/identitytable.png b/src/declarative/particles/defaultshaders/identitytable.png similarity index 100% rename from src/imports/particles/resources/identitytable.png rename to src/declarative/particles/defaultshaders/identitytable.png diff --git a/src/imports/particles/resources/simplefragment.shader b/src/declarative/particles/defaultshaders/simplefragment.shader similarity index 100% rename from src/imports/particles/resources/simplefragment.shader rename to src/declarative/particles/defaultshaders/simplefragment.shader diff --git a/src/imports/particles/resources/simplevertex.shader b/src/declarative/particles/defaultshaders/simplevertex.shader similarity index 100% rename from src/imports/particles/resources/simplevertex.shader rename to src/declarative/particles/defaultshaders/simplevertex.shader diff --git a/src/imports/particles/resources/spritefragment.shader b/src/declarative/particles/defaultshaders/spritefragment.shader similarity index 100% rename from src/imports/particles/resources/spritefragment.shader rename to src/declarative/particles/defaultshaders/spritefragment.shader diff --git a/src/imports/particles/resources/spriteimagefragment.shader b/src/declarative/particles/defaultshaders/spriteimagefragment.shader similarity index 100% rename from src/imports/particles/resources/spriteimagefragment.shader rename to src/declarative/particles/defaultshaders/spriteimagefragment.shader diff --git a/src/imports/particles/resources/spriteimagevertex.shader b/src/declarative/particles/defaultshaders/spriteimagevertex.shader similarity index 100% rename from src/imports/particles/resources/spriteimagevertex.shader rename to src/declarative/particles/defaultshaders/spriteimagevertex.shader diff --git a/src/imports/particles/resources/spritevertex.shader b/src/declarative/particles/defaultshaders/spritevertex.shader similarity index 100% rename from src/imports/particles/resources/spritevertex.shader rename to src/declarative/particles/defaultshaders/spritevertex.shader diff --git a/src/imports/particles/resources/superfragment.shader b/src/declarative/particles/defaultshaders/superfragment.shader similarity index 100% rename from src/imports/particles/resources/superfragment.shader rename to src/declarative/particles/defaultshaders/superfragment.shader diff --git a/src/imports/particles/resources/supervertex.shader b/src/declarative/particles/defaultshaders/supervertex.shader similarity index 100% rename from src/imports/particles/resources/supervertex.shader rename to src/declarative/particles/defaultshaders/supervertex.shader diff --git a/src/imports/particles/resources/trailsfragment.shader b/src/declarative/particles/defaultshaders/trailsfragment.shader similarity index 100% rename from src/imports/particles/resources/trailsfragment.shader rename to src/declarative/particles/defaultshaders/trailsfragment.shader diff --git a/src/imports/particles/resources/trailsvertex.shader b/src/declarative/particles/defaultshaders/trailsvertex.shader similarity index 100% rename from src/imports/particles/resources/trailsvertex.shader rename to src/declarative/particles/defaultshaders/trailsvertex.shader diff --git a/src/imports/particles/resources/ultrafragment.shader b/src/declarative/particles/defaultshaders/ultrafragment.shader similarity index 100% rename from src/imports/particles/resources/ultrafragment.shader rename to src/declarative/particles/defaultshaders/ultrafragment.shader diff --git a/src/imports/particles/resources/ultravertex.shader b/src/declarative/particles/defaultshaders/ultravertex.shader similarity index 100% rename from src/imports/particles/resources/ultravertex.shader rename to src/declarative/particles/defaultshaders/ultravertex.shader diff --git a/src/declarative/particles/particles.pri b/src/declarative/particles/particles.pri new file mode 100644 index 0000000000..04200a380e --- /dev/null +++ b/src/declarative/particles/particles.pri @@ -0,0 +1,60 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qsgangleddirection_p.h \ + $$PWD/qsgcustomparticle_p.h \ + $$PWD/qsgellipseextruder_p.h \ + $$PWD/qsgemitter_p.h \ + $$PWD/qsgfollowemitter_p.h \ + $$PWD/qsgfriction_p.h \ + $$PWD/qsggravity_p.h \ + $$PWD/qsgimageparticle_p.h \ + $$PWD/qsgitemparticle_p.h \ + $$PWD/qsgkill_p.h \ + $$PWD/qsglineextruder_p.h \ + $$PWD/qsgmaskextruder_p.h \ + $$PWD/qsgmodelparticle_p.h \ + $$PWD/qsgparticleaffector_p.h \ + $$PWD/qsgparticleemitter_p.h \ + $$PWD/qsgparticleextruder_p.h \ + $$PWD/qsgparticlepainter_p.h \ + $$PWD/qsgparticlesmodule_p.h \ + $$PWD/qsgparticlesystem_p.h \ + $$PWD/qsgpointattractor_p.h \ + $$PWD/qsgpointdirection_p.h \ + $$PWD/qsgspritegoal_p.h \ + $$PWD/qsgstochasticdirection_p.h \ + $$PWD/qsgtargeteddirection_p.h \ + $$PWD/qsgturbulence_p.h \ + $$PWD/qsgwander_p.h + +SOURCES += \ + $$PWD/qsgangleddirection.cpp \ + $$PWD/qsgcustomparticle.cpp \ + $$PWD/qsgellipseextruder.cpp \ + $$PWD/qsgemitter.cpp \ + $$PWD/qsgfollowemitter.cpp \ + $$PWD/qsgfriction.cpp \ + $$PWD/qsggravity.cpp \ + $$PWD/qsgimageparticle.cpp \ + $$PWD/qsgitemparticle.cpp \ + $$PWD/qsgkill.cpp \ + $$PWD/qsglineextruder.cpp \ + $$PWD/qsgmaskextruder.cpp \ + $$PWD/qsgmodelparticle.cpp \ + $$PWD/qsgparticleaffector.cpp \ + $$PWD/qsgparticleemitter.cpp \ + $$PWD/qsgparticleextruder.cpp \ + $$PWD/qsgparticlepainter.cpp \ + $$PWD/qsgparticlesmodule.cpp \ + $$PWD/qsgparticlesystem.cpp \ + $$PWD/qsgpointattractor.cpp \ + $$PWD/qsgpointdirection.cpp \ + $$PWD/qsgspritegoal.cpp \ + $$PWD/qsgstochasticdirection.cpp \ + $$PWD/qsgtargeteddirection.cpp \ + $$PWD/qsgturbulence.cpp \ + $$PWD/qsgwander.cpp + +RESOURCES += \ + $$PWD/particles.qrc diff --git a/src/declarative/particles/particles.qrc b/src/declarative/particles/particles.qrc new file mode 100644 index 0000000000..85931ec9ce --- /dev/null +++ b/src/declarative/particles/particles.qrc @@ -0,0 +1,22 @@ + + + defaultshaders/spritefragment.shader + defaultshaders/spritevertex.shader + defaultshaders/ctfragment.shader + defaultshaders/ctvertex.shader + defaultshaders/trailsfragment.shader + defaultshaders/trailsvertex.shader + defaultshaders/spriteimagefragment.shader + defaultshaders/spriteimagevertex.shader + defaultshaders/identitytable.png + defaultshaders/defaultFadeInOut.png + defaultshaders/deformablefragment.shader + defaultshaders/deformablevertex.shader + defaultshaders/ultravertex.shader + defaultshaders/ultrafragment.shader + defaultshaders/supervertex.shader + defaultshaders/superfragment.shader + defaultshaders/simplevertex.shader + defaultshaders/simplefragment.shader + + diff --git a/src/imports/particles/angledvector.cpp b/src/declarative/particles/qsgangleddirection.cpp similarity index 91% rename from src/imports/particles/angledvector.cpp rename to src/declarative/particles/qsgangleddirection.cpp index 85b5ed7472..5c32b53e3e 100644 --- a/src/imports/particles/angledvector.cpp +++ b/src/declarative/particles/qsgangleddirection.cpp @@ -39,12 +39,12 @@ ** ****************************************************************************/ -#include "angledvector.h" +#include "qsgangleddirection_p.h" #include QT_BEGIN_NAMESPACE const qreal CONV = 0.017453292519943295; -AngledVector::AngledVector(QObject *parent) : - VaryingVector(parent) +QSGAngledDirection::QSGAngledDirection(QObject *parent) : + QSGStochasticDirection(parent) , m_angle(0) , m_magnitude(0) , m_angleVariation(0) @@ -53,7 +53,7 @@ AngledVector::AngledVector(QObject *parent) : } -const QPointF &AngledVector::sample(const QPointF &from) +const QPointF &QSGAngledDirection::sample(const QPointF &from) { //TODO: Faster qreal theta = m_angle*CONV - m_angleVariation*CONV + rand()/float(RAND_MAX) * m_angleVariation*CONV * 2; diff --git a/src/imports/particles/angledvector.h b/src/declarative/particles/qsgangleddirection_p.h similarity index 93% rename from src/imports/particles/angledvector.h rename to src/declarative/particles/qsgangleddirection_p.h index ac78059e43..76262453a9 100644 --- a/src/imports/particles/angledvector.h +++ b/src/declarative/particles/qsgangleddirection_p.h @@ -39,16 +39,16 @@ ** ****************************************************************************/ -#ifndef ANGLEDVECTOR_H -#define ANGLEDVECTOR_H -#include "varyingvector.h" +#ifndef QSGANGLEDDIRECTION_H +#define QSGANGLEDDIRECTION_H +#include "qsgstochasticdirection_p.h" QT_BEGIN_HEADER QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class AngledVector : public VaryingVector +class QSGAngledDirection : public QSGStochasticDirection { Q_OBJECT Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) @@ -56,7 +56,7 @@ class AngledVector : public VaryingVector Q_PROPERTY(qreal angleVariation READ angleVariation WRITE setAngleVariation NOTIFY angleVariationChanged) Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged) public: - explicit AngledVector(QObject *parent = 0); + explicit QSGAngledDirection(QObject *parent = 0); const QPointF &sample(const QPointF &from); qreal angle() const { @@ -130,4 +130,4 @@ qreal m_magnitudeVariation; QT_END_NAMESPACE QT_END_HEADER -#endif // ANGLEDVECTOR_H +#endif // QSGANGLEDDIRECTION_H diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp new file mode 100644 index 0000000000..808ff1c427 --- /dev/null +++ b/src/declarative/particles/qsgcustomparticle.cpp @@ -0,0 +1,581 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgcustomparticle_p.h" +#include +#include + +QT_BEGIN_NAMESPACE +/* + "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" + "attribute highp vec4 qt_Vertex; \n" + "attribute highp vec2 qt_MultiTexCoord0; \n" + "varying highp vec2 qt_TexCoord0; \n" + "void main() { \n" + " qt_TexCoord0 = qt_MultiTexCoord0; \n" + " gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; \n" + "}"; +*/ +//Includes comments because the code isn't self explanatory +static const char qt_particles_default_vertex_code[] = + "attribute highp vec2 vPos; \n" + "attribute highp vec2 vTex; \n" + "attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize \n" + "attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration \n" + "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" + "uniform highp float timestamp; \n" + "varying highp vec2 fTex; \n" + "void main() { \n" + " fTex = vTex; \n" + " highp float size = vData.z; \n" + " highp float endSize = vData.w; \n" + " highp float t = (timestamp - vData.x) / vData.y; \n" + " highp float currentSize = mix(size, endSize, t * t); \n" + " if (t < 0. || t > 1.) \n" + " currentSize = 0.; \n" + " highp vec2 pos = vPos \n" + " - currentSize / 2. + currentSize * vTex // adjust size \n" + " + vVec.xy * t * vData.y // apply speed vector.. \n" + " + 0.5 * vVec.zw * pow(t * vData.y, 2.); \n" + " gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); \n" + "}"; + +static const char qt_particles_default_fragment_code[] =//TODO: Default frag requires source? + "uniform sampler2D source; \n" + "varying highp vec2 fTex; \n" + "uniform lowp float qt_Opacity; \n" + "void main() { \n" + " gl_FragColor = texture2D(source, fTex) * qt_Opacity; \n" + "}"; + +/* +static const char qt_particles_default_vertex_code[] = + "attribute highp vec2 vPos; \n" + "attribute highp vec2 vTex; \n" + "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" + "void main() { \n" + " highp float currentSize = 1000.0; \n" + " highp vec2 pos = vec2(100.0,100.0) \n" + " - currentSize / 2. + currentSize * vTex; // adjust size \n" + " gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); \n" + "}"; +static const char qt_particles_default_fragment_code[] =//TODO: Default frag requires source? + "void main() { \n" + " gl_FragColor = vec4(0,255,0,255); \n" + "}"; +*/ + +static const char qt_position_attribute_name[] = "qt_Vertex"; +static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0"; + +static QSGGeometry::Attribute PlainParticle_Attributes[] = { + { 0, 2, GL_FLOAT }, // Position + { 1, 2, GL_FLOAT }, // TexCoord + { 2, 4, GL_FLOAT }, // Data + { 3, 4, GL_FLOAT }, // Vectors + { 4, 1, GL_FLOAT } // r +}; + +static QSGGeometry::AttributeSet PlainParticle_AttributeSet = +{ + 5, // Attribute Count + (2 + 2 + 4 + 4 + 1) * sizeof(float), + PlainParticle_Attributes +}; + +struct PlainVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; + float r; +}; + +struct PlainVertices { + PlainVertex v1; + PlainVertex v2; + PlainVertex v3; + PlainVertex v4; +}; + +QSGCustomParticle::QSGCustomParticle(QSGItem* parent) + : QSGParticlePainter(parent) + , m_pleaseReset(true) + , m_dirtyData(true) +{ + setFlag(QSGItem::ItemHasContents); +} + +void QSGCustomParticle::componentComplete() +{ + reset(); + QSGParticlePainter::componentComplete(); +} + + +//Trying to keep the shader conventions the same as in qsgshadereffectitem +/*! + \qmlproperty string CustomParticle::fragmentShader + + This property holds the fragment shader's GLSL source code. + The default shader passes the texture coordinate along to the fragment + shader as "varying highp vec2 qt_TexCoord0". +*/ + +void QSGCustomParticle::setFragmentShader(const QByteArray &code) +{ + if (m_source.fragmentCode.constData() == code.constData()) + return; + m_source.fragmentCode = code; + if (isComponentComplete()) { + reset(); + } + emit fragmentShaderChanged(); +} + +/*! + \qmlproperty string CustomParticle::vertexShader + + This property holds the vertex shader's GLSL source code. + The default shader expects the texture coordinate to be passed from the + vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a + sampler2D named "source". +*/ + +void QSGCustomParticle::setVertexShader(const QByteArray &code) +{ + if (m_source.vertexCode.constData() == code.constData()) + return; + m_source.vertexCode = code; + if (isComponentComplete()) { + reset(); + } + emit vertexShaderChanged(); +} + +void QSGCustomParticle::setCount(int c) +{ + QSGParticlePainter::setCount(c); + m_pleaseReset = true; +} + +void QSGCustomParticle::reset() +{ + disconnectPropertySignals(); + + m_source.attributeNames.clear(); + m_source.uniformNames.clear(); + m_source.respectsOpacity = false; + m_source.respectsMatrix = false; + m_source.className = metaObject()->className(); + + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + delete source.mapper; + if (source.item && source.item->parentItem() == this) + source.item->setParentItem(0); + } + m_sources.clear(); + + QSGParticlePainter::reset(); + m_pleaseReset = true; +} + + +void QSGCustomParticle::changeSource(int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + QVariant v = property(m_sources.at(index).name.constData()); + setSource(v, index); +} + +void QSGCustomParticle::updateData() +{ + m_dirtyData = true; + update(); +} + +void QSGCustomParticle::setSource(const QVariant &var, int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + + SourceData &source = m_sources[index]; + + source.item = 0; + if (var.isNull()) { + return; + } else if (!qVariantCanConvert(var)) { + qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); + return; + } + + QObject *obj = qVariantValue(var); + + QSGTextureProvider *int3rface = QSGTextureProvider::from(obj); + if (!int3rface) { + qWarning("Could not assign property '%s', did not implement QSGTextureProvider.", source.name.constData()); + } + + source.item = qobject_cast(obj); + + // TODO: Copy better solution in QSGShaderEffectItem when they find it. + // 'source.item' needs a canvas to get a scenegraph node. + // The easiest way to make sure it gets a canvas is to + // make it a part of the same item tree as 'this'. + if (source.item && source.item->parentItem() == 0) { + source.item->setParentItem(this); + source.item->setVisible(false); + } +} + +void QSGCustomParticle::disconnectPropertySignals() +{ + disconnect(this, 0, this, SLOT(updateData())); + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + disconnect(this, 0, source.mapper, 0); + disconnect(source.mapper, 0, this, 0); + } +} + +void QSGCustomParticle::connectPropertySignals() +{ + QSet::const_iterator it; + for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) { + int pi = metaObject()->indexOfProperty(it->constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + if (!mp.hasNotifySignal()) + qWarning("QSGShaderEffectItem: property '%s' does not have notification method!", it->constData()); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, this, SLOT(updateData())); + } else { + qWarning("QSGShaderEffectItem: '%s' does not have a matching property!", it->constData()); + } + } + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + int pi = metaObject()->indexOfProperty(source.name.constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, source.mapper, SLOT(map())); + source.mapper->setMapping(this, i); + connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); + } else { + qWarning("QSGShaderEffectItem: '%s' does not have a matching source!", source.name.constData()); + } + } +} + +void QSGCustomParticle::updateProperties() +{ + QByteArray vertexCode = m_source.vertexCode; + QByteArray fragmentCode = m_source.fragmentCode; + if (vertexCode.isEmpty()) + vertexCode = qt_particles_default_vertex_code; + if (fragmentCode.isEmpty()) + fragmentCode = qt_particles_default_fragment_code; + + m_source.attributeNames.clear(); + m_source.attributeNames << "vPos" << "vTex" << "vData" << "vVec" << "r"; + + lookThroughShaderCode(vertexCode); + lookThroughShaderCode(fragmentCode); + + if (!m_source.attributeNames.contains(qt_position_attribute_name)) + qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_position_attribute_name); + if (!m_source.attributeNames.contains(qt_texcoord_attribute_name)) + qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_texcoord_attribute_name); + if (!m_source.respectsMatrix) + qWarning("QSGShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'."); + if (!m_source.respectsOpacity) + qWarning("QSGShaderEffectItem: Missing reference to \'qt_Opacity\'."); + + for (int i = 0; i < m_sources.size(); ++i) { + QVariant v = property(m_sources.at(i).name); + setSource(v, i); + } + + connectPropertySignals(); +} + +void QSGCustomParticle::lookThroughShaderCode(const QByteArray &code) +{ + // Regexp for matching attributes and uniforms. + // In human readable form: attribute|uniform [lowp|mediump|highp] + static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); + Q_ASSERT(re.isValid()); + + int pos = -1; + + QString wideCode = QString::fromLatin1(code.constData(), code.size()); + + while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { + QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute + QByteArray type = re.cap(2).toLatin1(); // type + QByteArray name = re.cap(3).toLatin1(); // variable name + + if (decl == "attribute") { + if(!m_source.attributeNames.contains(name))//TODO: Can they add custom attributes? + qWarning() << "Custom Particle: Unknown attribute " << name; + } else { + Q_ASSERT(decl == "uniform");//TODO: Shouldn't assert + + if (name == "qt_ModelViewProjectionMatrix") { + m_source.respectsMatrix = true; + } else if (name == "qt_Opacity") { + m_source.respectsOpacity = true; + } else if (name == "timestamp") { + //TODO: Copy the whole thing just because I have one more uniform? + } else { + m_source.uniformNames.insert(name); + if (type == "sampler2D") { + SourceData d; + d.mapper = new QSignalMapper; + d.name = name; + d.item = 0; + m_sources.append(d); + } + } + } + } +} + +QSGNode *QSGCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + if(m_pleaseReset){ + if(m_node) + delete m_node; + //delete m_material;//Shader effect item doesn't regen material? + + m_node = 0; + m_pleaseReset = false; + m_dirtyData = false; + } + + if(m_system && m_system->isRunning()) + prepareNextFrame(); + if (m_node){ + if(oldNode) + Q_ASSERT(oldNode == m_node); + update(); + m_node->markDirty(QSGNode::DirtyMaterial); //done in buildData? + } + + return m_node; +} + +void QSGCustomParticle::prepareNextFrame(){ + if(!m_node) + m_node = buildCustomNode(); + if(!m_node) + return; + + m_lastTime = m_system->systemSync(this) / 1000.; + if(m_dirtyData || true)//Currently this is how we update timestamp... potentially over expensive. + buildData(); +} + +QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() +{ + if (m_count * 4 > 0xffff) { + printf("CustomParticle: Too many particles... \n");//####Why is this here? + return 0; + } + + if(m_count <= 0) { + printf("CustomParticle: Too few particles... \n"); + return 0; + } + + + //Create Particle Geometry + int vCount = m_count * 4; + int iCount = m_count * 6; + QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount); + g->setDrawingMode(GL_TRIANGLES); + PlainVertex *vertices = (PlainVertex *) g->vertexData(); + for (int p=0; pindexDataAsUShort(); + for (int i=0; isetGeometry(g); + node->setMaterial(&m_material); + + /* + //For debugging, just use grid vertices like ShaderEffect + node->setGeometry(0); + QSGShaderEffectMesh* mesh = new QSGGridMesh(); + node->setFlag(QSGNode::OwnsGeometry, false); + + qDebug() << m_source.attributeNames; + QSGGeometry* geometry = node->geometry(); + geometry = mesh->updateGeometry(geometry, m_source.attributeNames, QRectF(0,0,width(),height())); + if(!geometry) + qDebug() << "Should have written the error handling"; + else + qDebug() << "Mesh Loaded"; + node->setGeometry(geometry); + qDebug() << QString("INIT") << geometry << (QObject*)node; + node->setFlag(QSGNode::OwnsGeometry, true); + */ + QSGShaderEffectProgram s = m_source; + if (s.fragmentCode.isEmpty()) + s.fragmentCode = qt_particles_default_fragment_code; + if (s.vertexCode.isEmpty()) + s.vertexCode = qt_particles_default_vertex_code; + m_material.setProgramSource(s); + node->markDirty(QSGNode::DirtyMaterial); + node->markDirty(QSGNode::DirtyAll); + return node; +} + +static const QByteArray timestampName("timestamp"); + +void QSGCustomParticle::buildData() +{ + if(!m_node)//Operates on m_node + return; + QVector > values; + QVector > > textures; + const QVector > > &oldTextures = m_material.textureProviders(); + + for (QSet::const_iterator it = m_source.uniformNames.begin(); + it != m_source.uniformNames.end(); ++it) { + values.append(qMakePair(*it, property(*it))); + } + for (int i = 0; i < oldTextures.size(); ++i) { + QSGTextureProvider *oldSource = QSGTextureProvider::from(oldTextures.at(i).second); + if (oldSource && oldSource->textureChangedSignal()) + disconnect(oldTextures.at(i).second, oldSource->textureChangedSignal(), m_node, SLOT(markDirtyTexture())); + } + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + textures.append(qMakePair(source.name, source.item)); + QSGTextureProvider *t = QSGTextureProvider::from(source.item); + if (t && t->textureChangedSignal()) + connect(source.item, t->textureChangedSignal(), m_node, SLOT(markDirtyTexture()), Qt::DirectConnection); + } + values.append(qMakePair(timestampName, QVariant(m_lastTime))); + m_material.setUniforms(values); + m_material.setTextureProviders(textures); + m_node->markDirty(QSGNode::DirtyMaterial); + m_dirtyData = false; +} + +void QSGCustomParticle::load(QSGParticleData *d) +{ + reload(d);//We don't do anything special in C++ here. +} + +void QSGCustomParticle::reload(QSGParticleData *d) +{ + if (m_node == 0) + return; + + PlainVertices *particles = (PlainVertices *) m_node->geometry()->vertexData(); + + int pos = particleTypeIndex(d); + + PlainVertices &p = particles[pos]; + + //Perhaps we could be more efficient? + vertexCopy(p.v1, d->pv); + vertexCopy(p.v2, d->pv); + vertexCopy(p.v3, d->pv); + vertexCopy(p.v4, d->pv); + +} + +QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgcustomparticle_p.h b/src/declarative/particles/qsgcustomparticle_p.h new file mode 100644 index 0000000000..95144ef638 --- /dev/null +++ b/src/declarative/particles/qsgcustomparticle_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CUSTOM_PARTICLE_H +#define CUSTOM_PARTICLE_H +#include "qsgparticlepainter_p.h" +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGNode; + +//Genealogy: Hybrid of UltraParticle and ShaderEffectItem +class QSGCustomParticle : public QSGParticlePainter +{ + Q_OBJECT + Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged) + Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged) + +public: + explicit QSGCustomParticle(QSGItem* parent=0); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); + virtual void setCount(int c); + + QByteArray fragmentShader() const { return m_source.fragmentCode; } + void setFragmentShader(const QByteArray &code); + + QByteArray vertexShader() const { return m_source.vertexCode; } + void setVertexShader(const QByteArray &code); +public Q_SLOTS: + void updateData(); + void changeSource(int); +Q_SIGNALS: + void fragmentShaderChanged(); + void vertexShaderChanged(); +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + void prepareNextFrame(); + void setSource(const QVariant &var, int index); + void disconnectPropertySignals(); + void connectPropertySignals(); + void reset(); + void updateProperties(); + void lookThroughShaderCode(const QByteArray &code); + virtual void componentComplete(); + QSGShaderEffectNode *buildCustomNode(); + +private: + void buildData(); + + bool m_pleaseReset; + bool m_dirtyData; + QSGShaderEffectProgram m_source; + struct SourceData + { + QSignalMapper *mapper; + QPointer item; + QByteArray name; + }; + QVector m_sources; + QSGShaderEffectMaterial m_material; + QSGShaderEffectNode* m_node; + qreal m_lastTime; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //HEADER_GUARD diff --git a/src/imports/particles/ellipseextruder.cpp b/src/declarative/particles/qsgellipseextruder.cpp similarity index 88% rename from src/imports/particles/ellipseextruder.cpp rename to src/declarative/particles/qsgellipseextruder.cpp index 1a0d70594b..76925895b9 100644 --- a/src/imports/particles/ellipseextruder.cpp +++ b/src/declarative/particles/qsgellipseextruder.cpp @@ -39,16 +39,16 @@ ** ****************************************************************************/ -#include "ellipseextruder.h" +#include "qsgellipseextruder_p.h" #include QT_BEGIN_NAMESPACE -EllipseExtruder::EllipseExtruder(QObject *parent) : - ParticleExtruder(parent) +QSGEllipseExtruder::QSGEllipseExtruder(QObject *parent) : + QSGParticleExtruder(parent) , m_fill(true) { } -QPointF EllipseExtruder::extrude(const QRectF & r) +QPointF QSGEllipseExtruder::extrude(const QRectF & r) { qreal theta = ((qreal)rand()/RAND_MAX) * 6.2831853071795862; qreal mag = m_fill ? ((qreal)rand()/RAND_MAX) : 1; @@ -56,7 +56,7 @@ QPointF EllipseExtruder::extrude(const QRectF & r) r.y() + r.height()/2 + mag * (r.height()/2) * sin(theta)); } -bool EllipseExtruder::contains(const QRectF &bounds, const QPointF &point) +bool QSGEllipseExtruder::contains(const QRectF &bounds, const QPointF &point) { return bounds.contains(point);//TODO: Ellipse } diff --git a/src/imports/particles/ellipseextruder.h b/src/declarative/particles/qsgellipseextruder_p.h similarity index 93% rename from src/imports/particles/ellipseextruder.h rename to src/declarative/particles/qsgellipseextruder_p.h index 25cc9bc16a..9f770a7ee6 100644 --- a/src/imports/particles/ellipseextruder.h +++ b/src/declarative/particles/qsgellipseextruder_p.h @@ -41,7 +41,7 @@ #ifndef ELLIPSEEXTRUDER_H #define ELLIPSEEXTRUDER_H -#include "particleextruder.h" +#include "qsgparticleextruder_p.h" QT_BEGIN_HEADER @@ -50,12 +50,12 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class EllipseExtruder : public ParticleExtruder +class QSGEllipseExtruder : public QSGParticleExtruder { Q_OBJECT Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Use base class? If it's still box public: - explicit EllipseExtruder(QObject *parent = 0); + explicit QSGEllipseExtruder(QObject *parent = 0); virtual QPointF extrude(const QRectF &); virtual bool contains(const QRectF &bounds, const QPointF &point); diff --git a/src/imports/particles/trailsemitter.cpp b/src/declarative/particles/qsgemitter.cpp similarity index 93% rename from src/imports/particles/trailsemitter.cpp rename to src/declarative/particles/qsgemitter.cpp index 41635a4299..081dd8dec5 100644 --- a/src/imports/particles/trailsemitter.cpp +++ b/src/declarative/particles/qsgemitter.cpp @@ -39,13 +39,13 @@ ** ****************************************************************************/ -#include "trailsemitter.h" -#include "particlesystem.h" -#include "particle.h" +#include "qsgemitter_p.h" +#include "qsgparticlesystem_p.h" +#include "qsgparticlepainter_p.h"//TODO: What was this for again? QT_BEGIN_NAMESPACE -TrailsEmitter::TrailsEmitter(QSGItem* parent) - : ParticleEmitter(parent) +QSGBasicEmitter::QSGBasicEmitter(QSGItem* parent) + : QSGParticleEmitter(parent) , m_speed_from_movement(0) , m_particle_count(0) , m_reset_last(true) @@ -55,7 +55,7 @@ TrailsEmitter::TrailsEmitter(QSGItem* parent) // setFlag(ItemHasContents); } -void TrailsEmitter::setSpeedFromMovement(qreal t) +void QSGBasicEmitter::setSpeedFromMovement(qreal t) { if (t == m_speed_from_movement) return; @@ -63,12 +63,12 @@ void TrailsEmitter::setSpeedFromMovement(qreal t) emit speedFromMovementChanged(); } -void TrailsEmitter::reset() +void QSGBasicEmitter::reset() { m_reset_last = true; } -void TrailsEmitter::emitWindow(int timeStamp) +void QSGBasicEmitter::emitWindow(int timeStamp) { if (m_system == 0) return; @@ -121,7 +121,7 @@ void TrailsEmitter::emitWindow(int timeStamp) pt = time; while (pt < time || !m_burstQueue.isEmpty()) { //int pos = m_last_particle % m_particle_count; - ParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_particle]); + QSGParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_particle]); if(datum){//actually emit(otherwise we've been asked to skip this one) datum->e = this;//###useful? ParticleVertex &p = datum->pv; diff --git a/src/imports/particles/trailsemitter.h b/src/declarative/particles/qsgemitter_p.h similarity index 93% rename from src/imports/particles/trailsemitter.h rename to src/declarative/particles/qsgemitter_p.h index 1ae150c0d2..3988c71cdf 100644 --- a/src/imports/particles/trailsemitter.h +++ b/src/declarative/particles/qsgemitter_p.h @@ -45,7 +45,7 @@ #include #include -#include "particleemitter.h" +#include "qsgparticleemitter_p.h" QT_BEGIN_HEADER @@ -54,18 +54,17 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class ParticleTrailsMaterial; class QSGGeometryNode; -class TrailsEmitter : public ParticleEmitter +class QSGBasicEmitter : public QSGParticleEmitter { Q_OBJECT Q_PROPERTY(qreal speedFromMovement READ speedFromMovement WRITE setSpeedFromMovement NOTIFY speedFromMovementChanged) public: - explicit TrailsEmitter(QSGItem* parent=0); - virtual ~TrailsEmitter(){} + explicit QSGBasicEmitter(QSGItem* parent=0); + virtual ~QSGBasicEmitter(){} virtual void emitWindow(int timeStamp); diff --git a/src/imports/particles/followemitter.cpp b/src/declarative/particles/qsgfollowemitter.cpp similarity index 91% rename from src/imports/particles/followemitter.cpp rename to src/declarative/particles/qsgfollowemitter.cpp index 17a544f62d..442cff9ec8 100644 --- a/src/imports/particles/followemitter.cpp +++ b/src/declarative/particles/qsgfollowemitter.cpp @@ -39,20 +39,20 @@ ** ****************************************************************************/ -#include "followemitter.h" -#include "particle.h" +#include "qsgfollowemitter_p.h" +#include "qsgparticlepainter_p.h"//TODO: What was this for again? #include QT_BEGIN_NAMESPACE -FollowEmitter::FollowEmitter(QSGItem *parent) : - ParticleEmitter(parent) +QSGFollowEmitter::QSGFollowEmitter(QSGItem *parent) : + QSGParticleEmitter(parent) , m_particlesPerParticlePerSecond(0) , m_lastTimeStamp(0) , m_emitterXVariation(0) , m_emitterYVariation(0) , m_followCount(0) , m_emissionExtruder(0) - , m_defaultEmissionExtruder(new ParticleExtruder(this)) + , m_defaultEmissionExtruder(new QSGParticleExtruder(this)) { connect(this, SIGNAL(followChanged(QString)), this, SLOT(recalcParticlesPerSecond())); @@ -62,7 +62,7 @@ FollowEmitter::FollowEmitter(QSGItem *parent) : this, SLOT(recalcParticlesPerSecond())); } -void FollowEmitter::recalcParticlesPerSecond(){ +void QSGFollowEmitter::recalcParticlesPerSecond(){ if(!m_system) return; m_followCount = m_system->m_groupData[m_system->m_groupIds[m_follow]]->size; @@ -75,12 +75,12 @@ void FollowEmitter::recalcParticlesPerSecond(){ } } -void FollowEmitter::reset() +void QSGFollowEmitter::reset() { m_followCount = 0; } -void FollowEmitter::emitWindow(int timeStamp) +void QSGFollowEmitter::emitWindow(int timeStamp) { if (m_system == 0) return; @@ -113,7 +113,7 @@ void FollowEmitter::emitWindow(int timeStamp) int gId2 = m_system->m_groupIds[m_particle]; for(int i=0; im_groupData[gId]->size; i++){ pt = m_lastEmission[i]; - ParticleData* d = m_system->m_data[i + m_system->m_groupData[gId]->start]; + QSGParticleData* d = m_system->m_data[i + m_system->m_groupData[gId]->start]; if(!d || !d->stillAlive()) continue; if(pt < d->pv.t) @@ -124,7 +124,7 @@ void FollowEmitter::emitWindow(int timeStamp) continue; } while(pt < time || !m_burstQueue.isEmpty()){ - ParticleData* datum = m_system->newDatum(gId2); + QSGParticleData* datum = m_system->newDatum(gId2); if(datum){//else, skip this emission datum->e = this;//###useful? ParticleVertex &p = datum->pv; @@ -152,7 +152,7 @@ void FollowEmitter::emitWindow(int timeStamp) // sizeOffset*2, // sizeOffset*2); - ParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder; + QSGParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder; const QPointF &newPos = effectiveEmissionExtruder->extrude(boundsRect); p.x = newPos.x(); p.y = newPos.y(); diff --git a/src/imports/particles/followemitter.h b/src/declarative/particles/qsgfollowemitter_p.h similarity index 88% rename from src/imports/particles/followemitter.h rename to src/declarative/particles/qsgfollowemitter_p.h index 6df293e2e1..314bd4e7c1 100644 --- a/src/imports/particles/followemitter.h +++ b/src/declarative/particles/qsgfollowemitter_p.h @@ -41,8 +41,8 @@ #ifndef FOLLOWEMITTER_H #define FOLLOWEMITTER_H -#include "particleemitter.h" -#include "particleaffector.h" +#include "qsgparticleemitter_p.h" +#include "qsgparticleaffector_p.h" QT_BEGIN_HEADER @@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class FollowEmitter : public ParticleEmitter +class QSGFollowEmitter : public QSGParticleEmitter { Q_OBJECT Q_PROPERTY(QString follow READ follow WRITE setFollow NOTIFY followChanged) @@ -60,12 +60,12 @@ class FollowEmitter : public ParticleEmitter //TODO: Document that FollowEmitter's box is where it follows. It emits in a rect centered on the followed particle //TODO: A set of properties that can involve the particle size of the followed - Q_PROPERTY(ParticleExtruder* emissionShape READ emissonShape WRITE setEmissionShape NOTIFY emissionShapeChanged) + Q_PROPERTY(QSGParticleExtruder* emissionShape READ emissonShape WRITE setEmissionShape NOTIFY emissionShapeChanged) Q_PROPERTY(qreal emissionHeight READ emitterYVariation WRITE setEmitterYVariation NOTIFY emitterYVariationChanged) Q_PROPERTY(qreal emissionWidth READ emitterXVariation WRITE setEmitterXVariation NOTIFY emitterXVariationChanged) public: - explicit FollowEmitter(QSGItem *parent = 0); + explicit QSGFollowEmitter(QSGItem *parent = 0); virtual void emitWindow(int timeStamp); virtual void reset(); @@ -89,7 +89,7 @@ public: return m_follow; } - ParticleExtruder* emissonShape() const + QSGParticleExtruder* emissonShape() const { return m_emissionExtruder; } @@ -104,7 +104,7 @@ signals: void followChanged(QString arg); - void emissionShapeChanged(ParticleExtruder* arg); + void emissionShapeChanged(QSGParticleExtruder* arg); public slots: @@ -139,7 +139,7 @@ public slots: } } - void setEmissionShape(ParticleExtruder* arg) + void setEmissionShape(QSGParticleExtruder* arg) { if (m_emissionExtruder != arg) { m_emissionExtruder = arg; @@ -151,7 +151,7 @@ private slots: void recalcParticlesPerSecond(); private: - QSet m_pending; + QSet m_pending; QVector m_lastEmission; int m_particlesPerParticlePerSecond; qreal m_lastTimeStamp; @@ -159,8 +159,8 @@ private: qreal m_emitterYVariation; QString m_follow; int m_followCount; - ParticleExtruder* m_emissionExtruder; - ParticleExtruder* m_defaultEmissionExtruder; + QSGParticleExtruder* m_emissionExtruder; + QSGParticleExtruder* m_defaultEmissionExtruder; }; QT_END_NAMESPACE diff --git a/src/imports/particles/frictionaffector.cpp b/src/declarative/particles/qsgfriction.cpp similarity index 89% rename from src/imports/particles/frictionaffector.cpp rename to src/declarative/particles/qsgfriction.cpp index 057bb20958..828d20556d 100644 --- a/src/imports/particles/frictionaffector.cpp +++ b/src/declarative/particles/qsgfriction.cpp @@ -39,14 +39,14 @@ ** ****************************************************************************/ -#include "frictionaffector.h" +#include "qsgfriction_p.h" QT_BEGIN_NAMESPACE -FrictionAffector::FrictionAffector(QSGItem *parent) : - ParticleAffector(parent), m_factor(0.0) +QSGFrictionAffector::QSGFrictionAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_factor(0.0) { } -bool FrictionAffector::affectParticle(ParticleData *d, qreal dt) +bool QSGFrictionAffector::affectParticle(QSGParticleData *d, qreal dt) { if(!m_factor) return false; diff --git a/src/imports/particles/frictionaffector.h b/src/declarative/particles/qsgfriction_p.h similarity index 90% rename from src/imports/particles/frictionaffector.h rename to src/declarative/particles/qsgfriction_p.h index 67b5f1029c..6b5a86f3f2 100644 --- a/src/imports/particles/frictionaffector.h +++ b/src/declarative/particles/qsgfriction_p.h @@ -41,7 +41,7 @@ #ifndef FRICTIONAFFECTOR_H #define FRICTIONAFFECTOR_H -#include "particleaffector.h" +#include "qsgparticleaffector_p.h" QT_BEGIN_HEADER @@ -50,19 +50,19 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class FrictionAffector : public ParticleAffector +class QSGFrictionAffector : public QSGParticleAffector { Q_OBJECT Q_PROPERTY(qreal factor READ factor WRITE setFactor NOTIFY factorChanged) public: - explicit FrictionAffector(QSGItem *parent = 0); + explicit QSGFrictionAffector(QSGItem *parent = 0); qreal factor() const { return m_factor; } protected: - virtual bool affectParticle(ParticleData *d, qreal dt); + virtual bool affectParticle(QSGParticleData *d, qreal dt); signals: void factorChanged(qreal arg); diff --git a/src/imports/particles/gravityaffector.cpp b/src/declarative/particles/qsggravity.cpp similarity index 88% rename from src/imports/particles/gravityaffector.cpp rename to src/declarative/particles/qsggravity.cpp index 02edbacd68..de735da5ad 100644 --- a/src/imports/particles/gravityaffector.cpp +++ b/src/declarative/particles/qsggravity.cpp @@ -39,12 +39,12 @@ ** ****************************************************************************/ -#include "gravityaffector.h" +#include "qsggravity_p.h" #include QT_BEGIN_NAMESPACE const qreal CONV = 0.017453292520444443; -GravityAffector::GravityAffector(QSGItem *parent) : - ParticleAffector(parent), m_acceleration(-10), m_angle(90), m_xAcc(0), m_yAcc(0) +QSGGravityAffector::QSGGravityAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_acceleration(-10), m_angle(90), m_xAcc(0), m_yAcc(0) { connect(this, SIGNAL(accelerationChanged(qreal)), this, SLOT(recalc())); @@ -53,14 +53,14 @@ GravityAffector::GravityAffector(QSGItem *parent) : recalc(); } -void GravityAffector::recalc() +void QSGGravityAffector::recalc() { qreal theta = m_angle * CONV; m_xAcc = m_acceleration * cos(theta); m_yAcc = m_acceleration * sin(theta); } -bool GravityAffector::affectParticle(ParticleData *d, qreal dt) +bool QSGGravityAffector::affectParticle(QSGParticleData *d, qreal dt) { Q_UNUSED(dt); bool changed = false; diff --git a/src/imports/particles/gravityaffector.h b/src/declarative/particles/qsggravity_p.h similarity index 92% rename from src/imports/particles/gravityaffector.h rename to src/declarative/particles/qsggravity_p.h index 004b59e182..57b2511911 100644 --- a/src/imports/particles/gravityaffector.h +++ b/src/declarative/particles/qsggravity_p.h @@ -41,7 +41,7 @@ #ifndef GRAVITYAFFECTOR_H #define GRAVITYAFFECTOR_H -#include "particleaffector.h" +#include "qsgparticleaffector_p.h" QT_BEGIN_HEADER @@ -50,13 +50,13 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class GravityAffector : public ParticleAffector +class QSGGravityAffector : public QSGParticleAffector { Q_OBJECT Q_PROPERTY(qreal acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) public: - explicit GravityAffector(QSGItem *parent = 0); + explicit QSGGravityAffector(QSGItem *parent = 0); qreal acceleration() const { return m_acceleration; @@ -67,7 +67,7 @@ public: return m_angle; } protected: - virtual bool affectParticle(ParticleData *d, qreal dt); + virtual bool affectParticle(QSGParticleData *d, qreal dt); signals: void accelerationChanged(qreal arg); diff --git a/src/imports/particles/ultraparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp similarity index 91% rename from src/imports/particles/ultraparticle.cpp rename to src/declarative/particles/qsgimageparticle.cpp index fd49523aff..c9df5f4dbd 100644 --- a/src/imports/particles/ultraparticle.cpp +++ b/src/declarative/particles/qsgimageparticle.cpp @@ -45,10 +45,10 @@ #include #include #include -#include "ultraparticle.h" -#include "particleemitter.h" -#include "spritestate.h" -#include "spriteengine.h" +#include "qsgimageparticle_p.h" +#include "qsgparticleemitter_p.h" +#include "qsgsprite_p.h" +#include "qsgspriteengine_p.h" #include #include @@ -97,11 +97,11 @@ class UltraMaterialData : public QSGMaterialShader public: UltraMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) { - QFile vf(vertexFile ? vertexFile : ":resources/ultravertex.shader"); + QFile vf(vertexFile ? vertexFile : ":defaultshaders/ultravertex.shader"); vf.open(QFile::ReadOnly); m_vertex_code = vf.readAll(); - QFile ff(fragmentFile ? fragmentFile : ":resources/ultrafragment.shader"); + QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/ultrafragment.shader"); ff.open(QFile::ReadOnly); m_fragment_code = ff.readAll(); @@ -211,11 +211,11 @@ class SimpleMaterialData : public QSGMaterialShader public: SimpleMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) { - QFile vf(vertexFile ? vertexFile : ":resources/simplevertex.shader"); + QFile vf(vertexFile ? vertexFile : ":defaultshaders/simplevertex.shader"); vf.open(QFile::ReadOnly); m_vertex_code = vf.readAll(); - QFile ff(fragmentFile ? fragmentFile : ":resources/simplefragment.shader"); + QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/simplefragment.shader"); ff.open(QFile::ReadOnly); m_fragment_code = ff.readAll(); @@ -281,8 +281,8 @@ QSGMaterialShader *SimpleMaterial::createShader() const { return new SimpleMaterialData; } -UltraParticle::UltraParticle(QSGItem* parent) - : ParticleType(parent) +QSGImageParticle::QSGImageParticle(QSGItem* parent) + : QSGParticlePainter(parent) , m_do_reset(false) , m_color_variation(0.0) , m_node(0) @@ -307,12 +307,12 @@ UltraParticle::UltraParticle(QSGItem* parent) setFlag(ItemHasContents); } -QDeclarativeListProperty UltraParticle::sprites() +QDeclarativeListProperty QSGImageParticle::sprites() { - return QDeclarativeListProperty(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); + return QDeclarativeListProperty(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); } -void UltraParticle::setImage(const QUrl &image) +void QSGImageParticle::setImage(const QUrl &image) { if (image == m_image_name) return; @@ -322,7 +322,7 @@ void UltraParticle::setImage(const QUrl &image) } -void UltraParticle::setColortable(const QUrl &table) +void QSGImageParticle::setColortable(const QUrl &table) { if (table == m_colortable_name) return; @@ -331,7 +331,7 @@ void UltraParticle::setColortable(const QUrl &table) reset(); } -void UltraParticle::setSizetable(const QUrl &table) +void QSGImageParticle::setSizetable(const QUrl &table) { if (table == m_sizetable_name) return; @@ -340,7 +340,7 @@ void UltraParticle::setSizetable(const QUrl &table) reset(); } -void UltraParticle::setOpacitytable(const QUrl &table) +void QSGImageParticle::setOpacitytable(const QUrl &table) { if (table == m_opacitytable_name) return; @@ -349,7 +349,7 @@ void UltraParticle::setOpacitytable(const QUrl &table) reset(); } -void UltraParticle::setColor(const QColor &color) +void QSGImageParticle::setColor(const QColor &color) { if (color == m_color) return; @@ -359,7 +359,7 @@ void UltraParticle::setColor(const QColor &color) reset(); } -void UltraParticle::setColorVariation(qreal var) +void QSGImageParticle::setColorVariation(qreal var) { if (var == m_color_variation) return; @@ -369,7 +369,7 @@ void UltraParticle::setColorVariation(qreal var) reset(); } -void UltraParticle::setAlphaVariation(qreal arg) +void QSGImageParticle::setAlphaVariation(qreal arg) { if (m_alphaVariation != arg) { m_alphaVariation = arg; @@ -379,7 +379,7 @@ void UltraParticle::setAlphaVariation(qreal arg) reset(); } -void UltraParticle::setAlpha(qreal arg) +void QSGImageParticle::setAlpha(qreal arg) { if (m_alpha != arg) { m_alpha = arg; @@ -389,7 +389,7 @@ void UltraParticle::setAlpha(qreal arg) reset(); } -void UltraParticle::setRedVariation(qreal arg) +void QSGImageParticle::setRedVariation(qreal arg) { if (m_redVariation != arg) { m_redVariation = arg; @@ -399,7 +399,7 @@ void UltraParticle::setRedVariation(qreal arg) reset(); } -void UltraParticle::setGreenVariation(qreal arg) +void QSGImageParticle::setGreenVariation(qreal arg) { if (m_greenVariation != arg) { m_greenVariation = arg; @@ -409,7 +409,7 @@ void UltraParticle::setGreenVariation(qreal arg) reset(); } -void UltraParticle::setBlueVariation(qreal arg) +void QSGImageParticle::setBlueVariation(qreal arg) { if (m_blueVariation != arg) { m_blueVariation = arg; @@ -419,7 +419,7 @@ void UltraParticle::setBlueVariation(qreal arg) reset(); } -void UltraParticle::setRotation(qreal arg) +void QSGImageParticle::setRotation(qreal arg) { if (m_rotation != arg) { m_rotation = arg; @@ -429,7 +429,7 @@ void UltraParticle::setRotation(qreal arg) reset(); } -void UltraParticle::setRotationVariation(qreal arg) +void QSGImageParticle::setRotationVariation(qreal arg) { if (m_rotationVariation != arg) { m_rotationVariation = arg; @@ -439,7 +439,7 @@ void UltraParticle::setRotationVariation(qreal arg) reset(); } -void UltraParticle::setRotationSpeed(qreal arg) +void QSGImageParticle::setRotationSpeed(qreal arg) { if (m_rotationSpeed != arg) { m_rotationSpeed = arg; @@ -449,7 +449,7 @@ void UltraParticle::setRotationSpeed(qreal arg) reset(); } -void UltraParticle::setRotationSpeedVariation(qreal arg) +void QSGImageParticle::setRotationSpeedVariation(qreal arg) { if (m_rotationSpeedVariation != arg) { m_rotationSpeedVariation = arg; @@ -459,7 +459,7 @@ void UltraParticle::setRotationSpeedVariation(qreal arg) reset(); } -void UltraParticle::setAutoRotation(bool arg) +void QSGImageParticle::setAutoRotation(bool arg) { if (m_autoRotation != arg) { m_autoRotation = arg; @@ -469,7 +469,7 @@ void UltraParticle::setAutoRotation(bool arg) reset(); } -void UltraParticle::setXVector(VaryingVector* arg) +void QSGImageParticle::setXVector(QSGStochasticDirection* arg) { if (m_xVector != arg) { m_xVector = arg; @@ -479,7 +479,7 @@ void UltraParticle::setXVector(VaryingVector* arg) reset(); } -void UltraParticle::setYVector(VaryingVector* arg) +void QSGImageParticle::setYVector(QSGStochasticDirection* arg) { if (m_yVector != arg) { m_yVector = arg; @@ -489,7 +489,7 @@ void UltraParticle::setYVector(VaryingVector* arg) reset(); } -void UltraParticle::setBloat(bool arg) +void QSGImageParticle::setBloat(bool arg) { if (m_bloat != arg) { m_bloat = arg; @@ -498,24 +498,24 @@ void UltraParticle::setBloat(bool arg) if(perfLevel < 9999) reset(); } -void UltraParticle::setCount(int c) +void QSGImageParticle::setCount(int c) { - ParticleType::setCount(c); + QSGParticlePainter::setCount(c); m_pleaseReset = true; } -void UltraParticle::reset() +void QSGImageParticle::reset() { - ParticleType::reset(); + QSGParticlePainter::reset(); m_pleaseReset = true; } -void UltraParticle::createEngine() +void QSGImageParticle::createEngine() { if(m_spriteEngine) delete m_spriteEngine; if(m_sprites.count()) - m_spriteEngine = new SpriteEngine(m_sprites, this); + m_spriteEngine = new QSGSpriteEngine(m_sprites, this); else m_spriteEngine = 0; reset(); @@ -553,7 +553,7 @@ static QSGGeometry::AttributeSet UltraParticle_AttributeSet = UltraParticle_Attributes }; -QSGGeometryNode* UltraParticle::buildSimpleParticleNode() +QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode() { perfLevel = Simple;//TODO: Intermediate levels QImage image = QImage(m_image_name.toLocalFile()); @@ -628,7 +628,7 @@ QSGGeometryNode* UltraParticle::buildSimpleParticleNode() return m_node; } -QSGGeometryNode* UltraParticle::buildParticleNode() +QSGGeometryNode* QSGImageParticle::buildParticleNode() { if (m_count * 4 > 0xffff) { printf("UltraParticle: Too many particles... \n");//####Why is this here? @@ -783,11 +783,11 @@ QSGGeometryNode* UltraParticle::buildParticleNode() QImage opacitytable(m_opacitytable_name.toLocalFile()); m_material = new UltraMaterial(); if(colortable.isNull()) - colortable = QImage(":resources/identitytable.png"); + colortable = QImage(":defaultshaders/identitytable.png"); if(sizetable.isNull()) - sizetable = QImage(":resources/identitytable.png"); + sizetable = QImage(":defaultshaders/identitytable.png"); if(opacitytable.isNull()) - opacitytable = QImage(":resources/defaultFadeInOut.png"); + opacitytable = QImage(":defaultshaders/defaultFadeInOut.png"); Q_ASSERT(!colortable.isNull()); Q_ASSERT(!sizetable.isNull()); Q_ASSERT(!opacitytable.isNull()); @@ -813,7 +813,7 @@ QSGGeometryNode* UltraParticle::buildParticleNode() return m_node; } -QSGNode *UltraParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) +QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) { if(m_pleaseReset){ if(m_node){ @@ -843,7 +843,7 @@ QSGNode *UltraParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) return m_node; } -void UltraParticle::prepareNextFrame() +void QSGImageParticle::prepareNextFrame() { if (m_node == 0){ //TODO: Staggered loading (as emitted) m_node = buildParticleNode(); @@ -886,7 +886,7 @@ IntermediateVertices* transplant(IntermediateVertices* iv, VT &v) return iv; } -IntermediateVertices* UltraParticle::fetchIntermediateVertices(int pos) +IntermediateVertices* QSGImageParticle::fetchIntermediateVertices(int pos) { //Note that this class ruins typesafety for you. Maybe even thread safety. //TODO: Something better, possibly with templates or inheritance @@ -907,7 +907,7 @@ IntermediateVertices* UltraParticle::fetchIntermediateVertices(int pos) } } -void UltraParticle::reloadColor(const Color4ub &c, ParticleData* d) +void QSGImageParticle::reloadColor(const Color4ub &c, QSGParticleData* d) { UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData(); int pos = particleTypeIndex(d); @@ -915,7 +915,7 @@ void UltraParticle::reloadColor(const Color4ub &c, ParticleData* d) p.v1.color = p.v2.color = p.v3.color = p.v4.color = c; } -void UltraParticle::reload(ParticleData *d) +void QSGImageParticle::reload(QSGParticleData *d) { if (m_node == 0) return; @@ -930,7 +930,7 @@ void UltraParticle::reload(ParticleData *d) vertexCopy(*p->v4, d->pv); } -void UltraParticle::load(ParticleData *d) +void QSGImageParticle::load(QSGParticleData *d) { if (m_node == 0) return; diff --git a/src/imports/particles/ultraparticle.h b/src/declarative/particles/qsgimageparticle_p.h similarity index 87% rename from src/imports/particles/ultraparticle.h rename to src/declarative/particles/qsgimageparticle_p.h index cb120f9273..1318647cc9 100644 --- a/src/imports/particles/ultraparticle.h +++ b/src/declarative/particles/qsgimageparticle_p.h @@ -41,12 +41,10 @@ #ifndef ULTRAPARTICLE_H #define ULTRAPARTICLE_H -#include "particle.h" -#include "varyingvector.h" +#include "qsgparticlepainter_p.h" +#include "qsgstochasticdirection_p.h" #include -#include "coloredparticle.h" - QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -56,15 +54,15 @@ QT_MODULE(Declarative) class UltraMaterial; class QSGGeometryNode; -class SpriteState; -class SpriteEngine; +class QSGSprite; +class QSGSpriteEngine; -/*struct Color4ub {//in coloredparticle +struct Color4ub { uchar r; uchar g; uchar b; uchar a; -};*/ +}; struct SimpleVertex { float x; @@ -129,7 +127,7 @@ struct IntermediateVertices { UltraVertex* v4; }; -class UltraParticle : public ParticleType +class QSGImageParticle : public QSGParticlePainter { Q_OBJECT Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged) @@ -158,21 +156,21 @@ class UltraParticle : public ParticleType //###Call i/j? Makes more sense to those with vector calculus experience, and I could even add the cirumflex in QML? //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size - Q_PROPERTY(VaryingVector* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged) + Q_PROPERTY(QSGStochasticDirection* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged) //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram. - Q_PROPERTY(VaryingVector* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged) - Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) + Q_PROPERTY(QSGStochasticDirection* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged) + Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) Q_PROPERTY(bool bloat READ bloat WRITE setBloat NOTIFY bloatChanged)//Just a debugging property to bypass optimizations public: - explicit UltraParticle(QSGItem *parent = 0); - virtual ~UltraParticle(){} + explicit QSGImageParticle(QSGItem *parent = 0); + virtual ~QSGImageParticle(){} - virtual void load(ParticleData*); - virtual void reload(ParticleData*); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); virtual void setCount(int c); - QDeclarativeListProperty sprites(); - SpriteEngine* spriteEngine() {return m_spriteEngine;} + QDeclarativeListProperty sprites(); + QSGSpriteEngine* spriteEngine() {return m_spriteEngine;} enum PerformanceLevel{//TODO: Expose? Unknown = 0, @@ -223,9 +221,9 @@ public: bool autoRotation() const { return m_autoRotation; } - VaryingVector* xVector() const { return m_xVector; } + QSGStochasticDirection* xVector() const { return m_xVector; } - VaryingVector* yVector() const { return m_yVector; } + QSGStochasticDirection* yVector() const { return m_yVector; } bool bloat() const { return m_bloat; } @@ -260,14 +258,14 @@ signals: void autoRotationChanged(bool arg); - void xVectorChanged(VaryingVector* arg); + void xVectorChanged(QSGStochasticDirection* arg); - void yVectorChanged(VaryingVector* arg); + void yVectorChanged(QSGStochasticDirection* arg); void bloatChanged(bool arg); public slots: - void reloadColor(const Color4ub &c, ParticleData* d); + void reloadColor(const Color4ub &c, QSGParticleData* d); void setAlphaVariation(qreal arg); void setAlpha(qreal arg); @@ -288,9 +286,9 @@ public slots: void setAutoRotation(bool arg); - void setXVector(VaryingVector* arg); + void setXVector(QSGStochasticDirection* arg); - void setYVector(VaryingVector* arg); + void setYVector(QSGStochasticDirection* arg); void setBloat(bool arg); @@ -336,11 +334,11 @@ private: qreal m_rotationSpeed; qreal m_rotationSpeedVariation; bool m_autoRotation; - VaryingVector* m_xVector; - VaryingVector* m_yVector; + QSGStochasticDirection* m_xVector; + QSGStochasticDirection* m_yVector; - QList m_sprites; - SpriteEngine* m_spriteEngine; + QList m_sprites; + QSGSpriteEngine* m_spriteEngine; bool m_bloat; PerformanceLevel perfLevel; diff --git a/src/imports/particles/itemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp similarity index 77% rename from src/imports/particles/itemparticle.cpp rename to src/declarative/particles/qsgitemparticle.cpp index e31309cf21..819c823155 100644 --- a/src/imports/particles/itemparticle.cpp +++ b/src/declarative/particles/qsgitemparticle.cpp @@ -39,32 +39,32 @@ ** ****************************************************************************/ -#include "itemparticle.h" +#include "qsgitemparticle_p.h" #include #include #include QT_BEGIN_NAMESPACE -ItemParticle::ItemParticle(QSGItem *parent) : - ParticleType(parent), m_fade(true) +QSGItemParticle::QSGItemParticle(QSGItem *parent) : + QSGParticlePainter(parent), m_fade(true) { setFlag(QSGItem::ItemHasContents); } -void ItemParticle::freeze(QSGItem* item) +void QSGItemParticle::freeze(QSGItem* item) { m_stasis << item; } -void ItemParticle::unfreeze(QSGItem* item) +void QSGItemParticle::unfreeze(QSGItem* item) { m_stasis.remove(item); } -void ItemParticle::take(QSGItem *item, bool prioritize) +void QSGItemParticle::take(QSGItem *item, bool prioritize) { if(prioritize) m_pendingItems.push_front(item); @@ -72,12 +72,12 @@ void ItemParticle::take(QSGItem *item, bool prioritize) m_pendingItems.push_back(item); } -void ItemParticle::give(QSGItem *item) +void QSGItemParticle::give(QSGItem *item) { //TODO: This } -void ItemParticle::load(ParticleData* d) +void QSGItemParticle::load(QSGParticleData* d) { if(m_pendingItems.isEmpty()) return; @@ -87,8 +87,8 @@ void ItemParticle::load(ParticleData* d) qWarning() << "Current model particles prefers overwrite:false"; //remove old item from the particle that is dying to make room for this one m_items[pos]->setOpacity(0.); - ItemParticleAttached* mpa; - if((mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos], false)))) + QSGItemParticleAttached* mpa; + if((mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos], false)))) mpa->detach();//reparent as well? m_items[pos] = 0; m_data[pos] = 0; @@ -98,7 +98,7 @@ void ItemParticle::load(ParticleData* d) m_pendingItems.pop_front(); m_items[pos]->setX(d->curX() - m_items[pos]->width()/2); m_items[pos]->setY(d->curY() - m_items[pos]->height()/2); - ItemParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); + QSGItemParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); if(mpa){ mpa->m_mp = this; mpa->attach(); @@ -108,26 +108,26 @@ void ItemParticle::load(ParticleData* d) m_activeCount++; } -void ItemParticle::reload(ParticleData* d) +void QSGItemParticle::reload(QSGParticleData* d) { //No-op unless we start copying the data. } -void ItemParticle::setCount(int c) +void QSGItemParticle::setCount(int c) { - ParticleType::setCount(c);//###Do we need our own? + QSGParticlePainter::setCount(c);//###Do we need our own? m_particleCount = c; reset(); } -int ItemParticle::count() +int QSGItemParticle::count() { return m_particleCount; } -void ItemParticle::reset() +void QSGItemParticle::reset() { - ParticleType::reset(); + QSGParticlePainter::reset(); //TODO: Cleanup items? m_items.resize(m_particleCount); m_data.resize(m_particleCount); @@ -137,7 +137,7 @@ void ItemParticle::reset() } -QSGNode* ItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) +QSGNode* QSGItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) { //Dummy update just to get painting tick if(m_pleaseReset){ @@ -152,7 +152,7 @@ QSGNode* ItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) return QSGItem::updatePaintNode(n,d); } -void ItemParticle::prepareNextFrame() +void QSGItemParticle::prepareNextFrame() { qint64 timeStamp = m_system->systemSync(this); qreal curT = timeStamp/1000.0; @@ -164,7 +164,7 @@ void ItemParticle::prepareNextFrame() //TODO: Size, better fade? for(int i=0; ipv.t) / data->pv.lifeSpan; @@ -174,8 +174,8 @@ void ItemParticle::prepareNextFrame() } if(t >= 1.0){//Usually happens from load item->setOpacity(0.); - ItemParticleAttached* mpa; - if((mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[i])))) + QSGItemParticleAttached* mpa; + if((mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[i])))) mpa->detach();//reparent as well? m_items[i] = 0; m_data[i] = 0; @@ -197,9 +197,9 @@ void ItemParticle::prepareNextFrame() } } -ItemParticleAttached *ItemParticle::qmlAttachedProperties(QObject *object) +QSGItemParticleAttached *QSGItemParticle::qmlAttachedProperties(QObject *object) { - return new ItemParticleAttached(object); + return new QSGItemParticleAttached(object); } QT_END_NAMESPACE diff --git a/src/imports/particles/itemparticle.h b/src/declarative/particles/qsgitemparticle_p.h similarity index 81% rename from src/imports/particles/itemparticle.h rename to src/declarative/particles/qsgitemparticle_p.h index 50414c77b6..fa3516b631 100644 --- a/src/imports/particles/itemparticle.h +++ b/src/declarative/particles/qsgitemparticle_p.h @@ -41,7 +41,7 @@ #ifndef ITEMPARTICLE_H #define ITEMPARTICLE_H -#include "particle.h" +#include "qsgparticlepainter_p.h" #include #include QT_BEGIN_HEADER @@ -50,25 +50,25 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QSGVisualDataModel; -class ItemParticleAttached; +class QSGItemParticleAttached; -class ItemParticle : public ParticleType +class QSGItemParticle : public QSGParticlePainter { Q_OBJECT Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged) public: - explicit ItemParticle(QSGItem *parent = 0); + explicit QSGItemParticle(QSGItem *parent = 0); bool fade() const { return m_fade; } virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - virtual void load(ParticleData*); - virtual void reload(ParticleData*); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); virtual void setCount(int c); virtual int count(); - static ItemParticleAttached *qmlAttachedProperties(QObject *object); + static QSGItemParticleAttached *qmlAttachedProperties(QObject *object); signals: void fadeChanged(); @@ -90,7 +90,7 @@ private: QList m_pendingItems; QVector m_items; - QVector m_data; + QVector m_data; QVector m_idx; QList m_available; QSet m_stasis; @@ -98,20 +98,20 @@ private: int m_activeCount; }; -class ItemParticleAttached : public QObject +class QSGItemParticleAttached : public QObject { Q_OBJECT - Q_PROPERTY(ItemParticle* particle READ particle CONSTANT); + Q_PROPERTY(QSGItemParticle* particle READ particle CONSTANT); public: - ItemParticleAttached(QObject* parent) + QSGItemParticleAttached(QObject* parent) : QObject(parent), m_mp(0) {;} - ItemParticle* particle() {return m_mp;} + QSGItemParticle* particle() {return m_mp;} void detach(){emit detached();} void attach(){emit attached();} private: - ItemParticle* m_mp; - friend class ItemParticle; + QSGItemParticle* m_mp; + friend class QSGItemParticle; Q_SIGNALS: void detached(); void attached(); @@ -119,7 +119,7 @@ Q_SIGNALS: QT_END_NAMESPACE -QML_DECLARE_TYPEINFO(ItemParticle, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPEINFO(QSGItemParticle, QML_HAS_ATTACHED_PROPERTIES) QT_END_HEADER #endif // ITEMPARTICLE_H diff --git a/src/imports/particles/killaffector.cpp b/src/declarative/particles/qsgkill.cpp similarity index 88% rename from src/imports/particles/killaffector.cpp rename to src/declarative/particles/qsgkill.cpp index c98a2f44e2..1321898dc9 100644 --- a/src/imports/particles/killaffector.cpp +++ b/src/declarative/particles/qsgkill.cpp @@ -39,16 +39,16 @@ ** ****************************************************************************/ -#include "killaffector.h" -#include "particleemitter.h" +#include "qsgkill_p.h" +#include "qsgparticleemitter_p.h" QT_BEGIN_NAMESPACE -KillAffector::KillAffector(QSGItem *parent) : - ParticleAffector(parent) +QSGKillAffector::QSGKillAffector(QSGItem *parent) : + QSGParticleAffector(parent) { } -bool KillAffector::affectParticle(ParticleData *d, qreal dt) +bool QSGKillAffector::affectParticle(QSGParticleData *d, qreal dt) { Q_UNUSED(dt); if(d->stillAlive()){ diff --git a/src/imports/particles/killaffector.h b/src/declarative/particles/qsgkill_p.h similarity index 89% rename from src/imports/particles/killaffector.h rename to src/declarative/particles/qsgkill_p.h index 937ef321a3..1b24b2fb40 100644 --- a/src/imports/particles/killaffector.h +++ b/src/declarative/particles/qsgkill_p.h @@ -41,7 +41,7 @@ #ifndef KILLAFFECTOR_H #define KILLAFFECTOR_H -#include "particleaffector.h" +#include "qsgparticleaffector_p.h" QT_BEGIN_HEADER @@ -50,13 +50,13 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class KillAffector : public ParticleAffector +class QSGKillAffector : public QSGParticleAffector { Q_OBJECT public: - explicit KillAffector(QSGItem *parent = 0); + explicit QSGKillAffector(QSGItem *parent = 0); protected: - virtual bool affectParticle(ParticleData *d, qreal dt); + virtual bool affectParticle(QSGParticleData *d, qreal dt); signals: public slots: diff --git a/src/imports/particles/lineextruder.cpp b/src/declarative/particles/qsglineextruder.cpp similarity index 91% rename from src/imports/particles/lineextruder.cpp rename to src/declarative/particles/qsglineextruder.cpp index 399bdae046..f32b01402a 100644 --- a/src/imports/particles/lineextruder.cpp +++ b/src/declarative/particles/qsglineextruder.cpp @@ -38,15 +38,15 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include "lineextruder.h" +#include "qsglineextruder_p.h" #include -LineExtruder::LineExtruder(QObject *parent) : - ParticleExtruder(parent), m_mirrored(false) +QSGLineExtruder::QSGLineExtruder(QObject *parent) : + QSGParticleExtruder(parent), m_mirrored(false) { } -QPointF LineExtruder::extrude(const QRectF &r) +QPointF QSGLineExtruder::extrude(const QRectF &r) { qreal x,y; if(!r.height()){ diff --git a/src/imports/particles/lineextruder.h b/src/declarative/particles/qsglineextruder_p.h similarity index 93% rename from src/imports/particles/lineextruder.h rename to src/declarative/particles/qsglineextruder_p.h index 925f1b3361..f356ca332a 100644 --- a/src/imports/particles/lineextruder.h +++ b/src/declarative/particles/qsglineextruder_p.h @@ -41,16 +41,16 @@ #ifndef LINEEXTRUDER_H #define LINEEXTRUDER_H -#include "particleextruder.h" +#include "qsgparticleextruder_p.h" -class LineExtruder : public ParticleExtruder +class QSGLineExtruder : public QSGParticleExtruder { Q_OBJECT //Default is topleft to bottom right. Flipped makes it topright to bottom left Q_PROPERTY(bool mirrored READ mirrored WRITE setmirrored NOTIFY mirroredChanged) public: - explicit LineExtruder(QObject *parent = 0); + explicit QSGLineExtruder(QObject *parent = 0); virtual QPointF extrude(const QRectF &); bool mirrored() const { diff --git a/src/imports/particles/maskextruder.cpp b/src/declarative/particles/qsgmaskextruder.cpp similarity index 90% rename from src/imports/particles/maskextruder.cpp rename to src/declarative/particles/qsgmaskextruder.cpp index 53dacf4214..3fe28b30a3 100644 --- a/src/imports/particles/maskextruder.cpp +++ b/src/declarative/particles/qsgmaskextruder.cpp @@ -39,18 +39,18 @@ ** ****************************************************************************/ -#include "maskextruder.h" +#include "qsgmaskextruder_p.h" #include #include QT_BEGIN_NAMESPACE -MaskExtruder::MaskExtruder(QObject *parent) : - ParticleExtruder(parent) +QSGMaskExtruder::QSGMaskExtruder(QObject *parent) : + QSGParticleExtruder(parent) , m_lastWidth(-1) , m_lastHeight(-1) { } -QPointF MaskExtruder::extrude(const QRectF &r) +QPointF QSGMaskExtruder::extrude(const QRectF &r) { ensureInitialized(r); if(!m_mask.count()) @@ -60,14 +60,14 @@ QPointF MaskExtruder::extrude(const QRectF &r) return p + r.topLeft(); } -bool MaskExtruder::contains(const QRectF &bounds, const QPointF &point) +bool QSGMaskExtruder::contains(const QRectF &bounds, const QPointF &point) { ensureInitialized(bounds);//###Current usage patterns WILL lead to different bounds/r calls. Separate list? QPoint p = point.toPoint() - bounds.topLeft().toPoint(); return m_img.rect().contains(p) && (bool)m_img.pixelIndex(p); } -void MaskExtruder::ensureInitialized(const QRectF &r) +void QSGMaskExtruder::ensureInitialized(const QRectF &r) { if(m_lastWidth == r.width() && m_lastHeight == r.height()) return; diff --git a/src/imports/particles/maskextruder.h b/src/declarative/particles/qsgmaskextruder_p.h similarity index 94% rename from src/imports/particles/maskextruder.h rename to src/declarative/particles/qsgmaskextruder_p.h index 6aaa79a0f9..b564efa6dc 100644 --- a/src/imports/particles/maskextruder.h +++ b/src/declarative/particles/qsgmaskextruder_p.h @@ -41,7 +41,7 @@ #ifndef MASKEXTRUDER_H #define MASKEXTRUDER_H -#include "particleextruder.h" +#include "qsgparticleextruder_p.h" #include #include @@ -51,12 +51,12 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class MaskExtruder : public ParticleExtruder +class QSGMaskExtruder : public QSGParticleExtruder { Q_OBJECT Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) public: - explicit MaskExtruder(QObject *parent = 0); + explicit QSGMaskExtruder(QObject *parent = 0); virtual QPointF extrude(const QRectF &); virtual bool contains(const QRectF &bounds, const QPointF &point); diff --git a/src/imports/particles/dataparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp similarity index 81% rename from src/imports/particles/dataparticle.cpp rename to src/declarative/particles/qsgmodelparticle.cpp index a2965e8c71..94ce082c9d 100644 --- a/src/imports/particles/dataparticle.cpp +++ b/src/declarative/particles/qsgmodelparticle.cpp @@ -39,25 +39,25 @@ ** ****************************************************************************/ -#include "dataparticle.h" +#include "qsgmodelparticle_p.h" #include #include #include QT_BEGIN_NAMESPACE -DataParticle::DataParticle(QSGItem *parent) : - ParticleType(parent), m_ownModel(false), m_comp(0), m_model(0), m_fade(true), m_modelCount(0) +QSGModelParticle::QSGModelParticle(QSGItem *parent) : + QSGParticlePainter(parent), m_ownModel(false), m_comp(0), m_model(0), m_fade(true), m_modelCount(0) { setFlag(QSGItem::ItemHasContents); } -QVariant DataParticle::model() const +QVariant QSGModelParticle::model() const { return m_dataSource; } -void DataParticle::setModel(const QVariant &arg) +void QSGModelParticle::setModel(const QVariant &arg) { if(arg == m_dataSource) return; @@ -84,7 +84,7 @@ void DataParticle::setModel(const QVariant &arg) updateCount(); } -void DataParticle::updateCount() +void QSGModelParticle::updateCount() { int newCount = 0; if(m_model) @@ -105,14 +105,14 @@ void DataParticle::updateCount() m_modelCount = newCount; } -QDeclarativeComponent *DataParticle::delegate() const +QDeclarativeComponent *QSGModelParticle::delegate() const { if(m_model) return m_model->delegate(); return 0; } -void DataParticle::setDelegate(QDeclarativeComponent *comp) +void QSGModelParticle::setDelegate(QDeclarativeComponent *comp) { if (QSGVisualDataModel *dataModel = qobject_cast(m_model)) if (comp == dataModel->delegate()) @@ -123,26 +123,26 @@ void DataParticle::setDelegate(QDeclarativeComponent *comp) emit delegateChanged(); } -int DataParticle::modelCount() const +int QSGModelParticle::modelCount() const { if(m_model) - const_cast(this)->updateCount();//TODO: Investigate why this doesn't get called properly + const_cast(this)->updateCount();//TODO: Investigate why this doesn't get called properly return m_modelCount; } -void DataParticle::freeze(QSGItem* item) +void QSGModelParticle::freeze(QSGItem* item) { m_stasis << item; } -void DataParticle::unfreeze(QSGItem* item) +void QSGModelParticle::unfreeze(QSGItem* item) { m_stasis.remove(item); } -void DataParticle::load(ParticleData* d) +void QSGModelParticle::load(QSGParticleData* d) { if(!m_model || !m_model->count()) return; @@ -164,7 +164,7 @@ void DataParticle::load(ParticleData* d) m_items[pos] = m_model->item(m_available.first()); m_idx[pos] = m_available.first(); m_available.pop_front(); - DataParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); + QSGModelParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); if(mpa){ mpa->m_mp = this; mpa->attach(); @@ -174,26 +174,26 @@ void DataParticle::load(ParticleData* d) m_activeCount++; } -void DataParticle::reload(ParticleData* d) +void QSGModelParticle::reload(QSGParticleData* d) { //No-op unless we start copying the data. } -void DataParticle::setCount(int c) +void QSGModelParticle::setCount(int c) { - ParticleType::setCount(c);//###Do we need our own? + QSGParticlePainter::setCount(c);//###Do we need our own? m_particleCount = c; reset(); } -int DataParticle::count() +int QSGModelParticle::count() { return m_particleCount; } -void DataParticle::reset() +void QSGModelParticle::reset() { - ParticleType::reset(); + QSGParticlePainter::reset(); //TODO: Cleanup items? m_items.resize(m_particleCount); m_data.resize(m_particleCount); @@ -206,7 +206,7 @@ void DataParticle::reset() } -QSGNode* DataParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) +QSGNode* QSGModelParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) { //Dummy update just to get painting tick if(m_pleaseReset){ @@ -221,7 +221,7 @@ QSGNode* DataParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) return QSGItem::updatePaintNode(n,d); } -void DataParticle::prepareNextFrame() +void QSGModelParticle::prepareNextFrame() { qint64 timeStamp = m_system->systemSync(this); qreal curT = timeStamp/1000.0; @@ -233,7 +233,7 @@ void DataParticle::prepareNextFrame() //TODO: Size, better fade? for(int i=0; ipv.t) / data->pv.lifeSpan; @@ -266,9 +266,9 @@ void DataParticle::prepareNextFrame() } } -DataParticleAttached *DataParticle::qmlAttachedProperties(QObject *object) +QSGModelParticleAttached *QSGModelParticle::qmlAttachedProperties(QObject *object) { - return new DataParticleAttached(object); + return new QSGModelParticleAttached(object); } QT_END_NAMESPACE diff --git a/src/imports/particles/dataparticle.h b/src/declarative/particles/qsgmodelparticle_p.h similarity index 83% rename from src/imports/particles/dataparticle.h rename to src/declarative/particles/qsgmodelparticle_p.h index 84422e70c3..4dd8bfa710 100644 --- a/src/imports/particles/dataparticle.h +++ b/src/declarative/particles/qsgmodelparticle_p.h @@ -41,7 +41,7 @@ #ifndef DATAPARTICLE_H #define DATAPARTICLE_H -#include "particle.h" +#include "qsgparticlepainter_p.h" #include #include QT_BEGIN_HEADER @@ -50,9 +50,9 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QSGVisualDataModel; -class DataParticleAttached; +class QSGModelParticleAttached; -class DataParticle : public ParticleType +class QSGModelParticle : public QSGParticlePainter { Q_OBJECT @@ -62,7 +62,7 @@ class DataParticle : public ParticleType Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged) Q_CLASSINFO("DefaultProperty", "delegate") public: - explicit DataParticle(QSGItem *parent = 0); + explicit QSGModelParticle(QSGItem *parent = 0); QVariant model() const; void setModel(const QVariant &); @@ -74,12 +74,12 @@ public: bool fade() const { return m_fade; } virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - virtual void load(ParticleData*); - virtual void reload(ParticleData*); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); virtual void setCount(int c); virtual int count(); - static DataParticleAttached *qmlAttachedProperties(QObject *object); + static QSGModelParticleAttached *qmlAttachedProperties(QObject *object); signals: void modelChanged(); void delegateChanged(); @@ -107,7 +107,7 @@ private: QList m_pendingItems; QVector m_items; - QVector m_data; + QVector m_data; QVector m_idx; QList m_available; QSet m_stasis; @@ -116,20 +116,20 @@ private: int m_modelCount; }; -class DataParticleAttached : public QObject +class QSGModelParticleAttached : public QObject { Q_OBJECT - Q_PROPERTY(DataParticle* particle READ particle CONSTANT); + Q_PROPERTY(QSGModelParticle* particle READ particle CONSTANT); public: - DataParticleAttached(QObject* parent) + QSGModelParticleAttached(QObject* parent) : QObject(parent), m_mp(0) {;} - DataParticle* particle() {return m_mp;} + QSGModelParticle* particle() {return m_mp;} void detach(){emit detached();} void attach(){emit attached();} private: - DataParticle* m_mp; - friend class DataParticle; + QSGModelParticle* m_mp; + friend class QSGModelParticle; Q_SIGNALS: void detached(); void attached(); @@ -137,7 +137,7 @@ Q_SIGNALS: QT_END_NAMESPACE -QML_DECLARE_TYPEINFO(DataParticle, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPEINFO(QSGModelParticle, QML_HAS_ATTACHED_PROPERTIES) QT_END_HEADER #endif // DATAPARTICLE_H diff --git a/src/imports/particles/particleaffector.cpp b/src/declarative/particles/qsgparticleaffector.cpp similarity index 87% rename from src/imports/particles/particleaffector.cpp rename to src/declarative/particles/qsgparticleaffector.cpp index 73564a940a..d4b588a44f 100644 --- a/src/imports/particles/particleaffector.cpp +++ b/src/declarative/particles/qsgparticleaffector.cpp @@ -39,14 +39,14 @@ ** ****************************************************************************/ -#include "particleaffector.h" +#include "qsgparticleaffector_p.h" #include QT_BEGIN_NAMESPACE -ParticleAffector::ParticleAffector(QSGItem *parent) : +QSGParticleAffector::QSGParticleAffector(QSGItem *parent) : QSGItem(parent), m_needsReset(false), m_system(0), m_active(true) - , m_updateIntSet(false), m_shape(new ParticleExtruder(this)), m_signal(false) + , m_updateIntSet(false), m_shape(new QSGParticleExtruder(this)), m_signal(false) { - connect(this, SIGNAL(systemChanged(ParticleSystem*)), + connect(this, SIGNAL(systemChanged(QSGParticleSystem*)), this, SLOT(updateOffsets())); connect(this, SIGNAL(xChanged()), this, SLOT(updateOffsets())); @@ -54,14 +54,14 @@ ParticleAffector::ParticleAffector(QSGItem *parent) : this, SLOT(updateOffsets()));//TODO: in componentComplete and all relevant signals } -void ParticleAffector::componentComplete() +void QSGParticleAffector::componentComplete() { if(!m_system) qWarning() << "Affector created without a particle system specified";//TODO: useful QML warnings, like line number? QSGItem::componentComplete(); } -void ParticleAffector::affectSystem(qreal dt) +void QSGParticleAffector::affectSystem(qreal dt) { if(!m_active) return; @@ -79,7 +79,7 @@ void ParticleAffector::affectSystem(qreal dt) } //foreach(ParticleData* d, m_system->m_data){ for(int i=0; im_particle_count; i++){ - ParticleData* d = m_system->m_data[i]; + QSGParticleData* d = m_system->m_data[i]; if(!d || (m_onceOff && m_onceOffed.contains(d->systemIndex))) continue; if(m_groups.isEmpty() || m_groups.contains(d->group)){ @@ -99,20 +99,20 @@ void ParticleAffector::affectSystem(qreal dt) } } -bool ParticleAffector::affectParticle(ParticleData *d, qreal dt) +bool QSGParticleAffector::affectParticle(QSGParticleData *d, qreal dt) { Q_UNUSED(d); Q_UNUSED(dt); return false; } -void ParticleAffector::reset(int idx) +void QSGParticleAffector::reset(int idx) {//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass if(m_onceOff) m_onceOffed.remove(idx); } -void ParticleAffector::updateOffsets() +void QSGParticleAffector::updateOffsets() { if(m_system) m_offset = m_system->mapFromItem(this, QPointF(0, 0)); diff --git a/src/imports/particles/particleaffector.h b/src/declarative/particles/qsgparticleaffector_p.h similarity index 83% rename from src/imports/particles/particleaffector.h rename to src/declarative/particles/qsgparticleaffector_p.h index 3a92263092..1572ad8102 100644 --- a/src/imports/particles/particleaffector.h +++ b/src/declarative/particles/qsgparticleaffector_p.h @@ -43,8 +43,8 @@ #define PARTICLEAFFECTOR_H #include -#include "particlesystem.h" -#include "particleextruder.h" +#include "qsgparticlesystem_p.h" +#include "qsgparticleextruder_p.h" QT_BEGIN_HEADER @@ -53,21 +53,21 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class ParticleAffector : public QSGItem +class QSGParticleAffector : public QSGItem { Q_OBJECT - Q_PROPERTY(ParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) Q_PROPERTY(QStringList particles READ particles WRITE setParticles NOTIFY particlesChanged) Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) Q_PROPERTY(bool onceOff READ onceOff WRITE setOnceOff NOTIFY onceOffChanged) - Q_PROPERTY(ParticleExtruder* shape READ shape WRITE setShape NOTIFY shapeChanged) + Q_PROPERTY(QSGParticleExtruder* shape READ shape WRITE setShape NOTIFY shapeChanged) Q_PROPERTY(bool signal READ signal WRITE setSignal NOTIFY signalChanged) public: - explicit ParticleAffector(QSGItem *parent = 0); + explicit QSGParticleAffector(QSGItem *parent = 0); virtual void affectSystem(qreal dt); virtual void reset(int systemIdx);//As some store their own data per idx? - ParticleSystem* system() const + QSGParticleSystem* system() const { return m_system; } @@ -87,7 +87,7 @@ public: return m_onceOff; } - ParticleExtruder* shape() const + QSGParticleExtruder* shape() const { return m_shape; } @@ -99,7 +99,7 @@ public: signals: - void systemChanged(ParticleSystem* arg); + void systemChanged(QSGParticleSystem* arg); void particlesChanged(QStringList arg); @@ -107,13 +107,13 @@ signals: void onceOffChanged(bool arg); - void shapeChanged(ParticleExtruder* arg); + void shapeChanged(QSGParticleExtruder* arg); void affected(qreal x, qreal y);//###Idx too? void signalChanged(bool arg); public slots: -void setSystem(ParticleSystem* arg) +void setSystem(QSGParticleSystem* arg) { if (m_system != arg) { m_system = arg; @@ -147,7 +147,7 @@ void setOnceOff(bool arg) } } -void setShape(ParticleExtruder* arg) +void setShape(QSGParticleExtruder* arg) { if (m_shape != arg) { m_shape = arg; @@ -164,10 +164,10 @@ void setSignal(bool arg) } protected: - friend class ParticleSystem; - virtual bool affectParticle(ParticleData *d, qreal dt); + friend class QSGParticleSystem; + virtual bool affectParticle(QSGParticleData *d, qreal dt); bool m_needsReset;//### What is this really saving? - ParticleSystem* m_system; + QSGParticleSystem* m_system; QStringList m_particles; bool activeGroup(int g) {return m_groups.isEmpty() || m_groups.contains(g);} bool m_active; @@ -180,7 +180,7 @@ private: bool m_onceOff; - ParticleExtruder* m_shape; + QSGParticleExtruder* m_shape; bool m_signal; diff --git a/src/imports/particles/particleemitter.cpp b/src/declarative/particles/qsgparticleemitter.cpp similarity index 87% rename from src/imports/particles/particleemitter.cpp rename to src/declarative/particles/qsgparticleemitter.cpp index dd7d73749b..c04c86cfe5 100644 --- a/src/imports/particles/particleemitter.cpp +++ b/src/declarative/particles/qsgparticleemitter.cpp @@ -39,9 +39,9 @@ ** ****************************************************************************/ -#include "particleemitter.h" +#include "qsgparticleemitter_p.h" QT_BEGIN_NAMESPACE -ParticleEmitter::ParticleEmitter(QSGItem *parent) : +QSGParticleEmitter::QSGParticleEmitter(QSGItem *parent) : QSGItem(parent) , m_particlesPerSecond(10) , m_particleDuration(1000) @@ -68,25 +68,25 @@ ParticleEmitter::ParticleEmitter(QSGItem *parent) : this, SIGNAL(particleCountChanged())); } -ParticleEmitter::~ParticleEmitter() +QSGParticleEmitter::~QSGParticleEmitter() { if(m_defaultExtruder) delete m_defaultExtruder; } -void ParticleEmitter::componentComplete() +void QSGParticleEmitter::componentComplete() { if(!m_system) qWarning() << "Emitter created without a particle system specified";//TODO: useful QML warnings, like line number? QSGItem::componentComplete(); } -void ParticleEmitter::emitWindow(int timeStamp) +void QSGParticleEmitter::emitWindow(int timeStamp) { Q_UNUSED(timeStamp); } -void ParticleEmitter::setEmitting(bool arg) +void QSGParticleEmitter::setEmitting(bool arg) { if (m_emitting != arg) { m_emitting = arg; @@ -95,16 +95,16 @@ void ParticleEmitter::setEmitting(bool arg) } -ParticleExtruder* ParticleEmitter::effectiveExtruder() +QSGParticleExtruder* QSGParticleEmitter::effectiveExtruder() { if(m_extruder) return m_extruder; if(!m_defaultExtruder) - m_defaultExtruder = new ParticleExtruder; + m_defaultExtruder = new QSGParticleExtruder; return m_defaultExtruder; } -void ParticleEmitter::pulse(qreal seconds) +void QSGParticleEmitter::pulse(qreal seconds) { if(!particleCount()) qWarning() << "pulse called on an emitter with a particle count of zero"; @@ -112,14 +112,14 @@ void ParticleEmitter::pulse(qreal seconds) m_burstLeft = seconds*1000.0;//TODO: Change name to match } -void ParticleEmitter::burst(int num) +void QSGParticleEmitter::burst(int num) { if(!particleCount()) qWarning() << "burst called on an emitter with a particle count of zero"; m_burstQueue << qMakePair(num, QPointF(x(), y())); } -void ParticleEmitter::setMaxParticleCount(int arg) +void QSGParticleEmitter::setMaxParticleCount(int arg) { if (m_maxParticleCount != arg) { if(arg < 0 && m_maxParticleCount >= 0){ @@ -138,7 +138,7 @@ void ParticleEmitter::setMaxParticleCount(int arg) } } -int ParticleEmitter::particleCount() const +int QSGParticleEmitter::particleCount() const { if(m_maxParticleCount >= 0) return m_maxParticleCount; diff --git a/src/imports/particles/particleemitter.h b/src/declarative/particles/qsgparticleemitter_p.h similarity index 82% rename from src/imports/particles/particleemitter.h rename to src/declarative/particles/qsgparticleemitter_p.h index e272ae51ab..ad6a5f645f 100644 --- a/src/imports/particles/particleemitter.h +++ b/src/declarative/particles/qsgparticleemitter_p.h @@ -44,9 +44,9 @@ #include #include -#include "particlesystem.h" -#include "particleextruder.h" -#include "varyingvector.h" +#include "qsgparticlesystem_p.h" +#include "qsgparticleextruder_p.h" +#include "qsgstochasticdirection_p.h" #include #include @@ -57,13 +57,13 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class ParticleEmitter : public QSGItem +class QSGParticleEmitter : public QSGItem { Q_OBJECT //###currently goes in emitters OR sets system. Pick one? - Q_PROPERTY(ParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) Q_PROPERTY(QString particle READ particle WRITE setParticle NOTIFY particleChanged) - Q_PROPERTY(ParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged) + Q_PROPERTY(QSGParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged) Q_PROPERTY(bool emitting READ emitting WRITE setEmitting NOTIFY emittingChanged) Q_PROPERTY(qreal particlesPerSecond READ particlesPerSecond WRITE setParticlesPerSecond NOTIFY particlesPerSecondChanged) @@ -75,11 +75,11 @@ class ParticleEmitter : public QSGItem Q_PROPERTY(qreal particleEndSize READ particleEndSize WRITE setParticleEndSize NOTIFY particleEndSizeChanged) Q_PROPERTY(qreal particleSizeVariation READ particleSizeVariation WRITE setParticleSizeVariation NOTIFY particleSizeVariationChanged) - Q_PROPERTY(VaryingVector *speed READ speed WRITE setSpeed NOTIFY speedChanged) - Q_PROPERTY(VaryingVector *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) + Q_PROPERTY(QSGStochasticDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged) + Q_PROPERTY(QSGStochasticDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) public: - explicit ParticleEmitter(QSGItem *parent = 0); - virtual ~ParticleEmitter(); + explicit QSGParticleEmitter(QSGItem *parent = 0); + virtual ~QSGParticleEmitter(); virtual void emitWindow(int timeStamp); bool emitting() const @@ -97,7 +97,7 @@ public: return m_particleDuration; } - ParticleSystem* system() const + QSGParticleSystem* system() const { return m_system; } @@ -118,13 +118,13 @@ signals: void particleDurationChanged(int); void emittingChanged(bool); - void systemChanged(ParticleSystem* arg); + void systemChanged(QSGParticleSystem* arg); void particleChanged(QString arg); void particleDurationVariationChanged(int arg); - void extruderChanged(ParticleExtruder* arg); + void extruderChanged(QSGParticleExtruder* arg); void particleSizeChanged(qreal arg); @@ -132,9 +132,9 @@ signals: void particleSizeVariationChanged(qreal arg); - void speedChanged(VaryingVector * arg); + void speedChanged(QSGStochasticDirection * arg); - void accelerationChanged(VaryingVector * arg); + void accelerationChanged(QSGStochasticDirection * arg); void maxParticleCountChanged(int arg); void particleCountChanged(); @@ -161,7 +161,7 @@ public slots: } } - void setSystem(ParticleSystem* arg) + void setSystem(QSGParticleSystem* arg) { if (m_system != arg) { m_system = arg; @@ -185,7 +185,7 @@ public slots: emit particleDurationVariationChanged(arg); } } - void setExtruder(ParticleExtruder* arg) + void setExtruder(QSGParticleExtruder* arg) { if (m_extruder != arg) { m_extruder = arg; @@ -217,7 +217,7 @@ public slots: } } - void setSpeed(VaryingVector * arg) + void setSpeed(QSGStochasticDirection * arg) { if (m_speed != arg) { m_speed = arg; @@ -225,7 +225,7 @@ public slots: } } - void setAcceleration(VaryingVector * arg) + void setAcceleration(QSGStochasticDirection * arg) { if (m_acceleration != arg) { m_acceleration = arg; @@ -239,7 +239,7 @@ public: int particleCount() const; virtual void reset(){;} - ParticleExtruder* extruder() const + QSGParticleExtruder* extruder() const { return m_extruder; } @@ -259,12 +259,12 @@ public: return m_particleSizeVariation; } - VaryingVector * speed() const + QSGStochasticDirection * speed() const { return m_speed; } - VaryingVector * acceleration() const + QSGStochasticDirection * acceleration() const { return m_acceleration; } @@ -279,13 +279,13 @@ protected: int m_particleDuration; int m_particleDurationVariation; bool m_emitting; - ParticleSystem* m_system; + QSGParticleSystem* m_system; QString m_particle; - ParticleExtruder* m_extruder; - ParticleExtruder* m_defaultExtruder; - ParticleExtruder* effectiveExtruder(); - VaryingVector * m_speed; - VaryingVector * m_acceleration; + QSGParticleExtruder* m_extruder; + QSGParticleExtruder* m_defaultExtruder; + QSGParticleExtruder* effectiveExtruder(); + QSGStochasticDirection * m_speed; + QSGStochasticDirection * m_acceleration; qreal m_particleSize; qreal m_particleEndSize; qreal m_particleSizeVariation; @@ -294,7 +294,7 @@ protected: QList > m_burstQueue; int m_maxParticleCount; private: - VaryingVector m_nullVector; + QSGStochasticDirection m_nullVector; }; QT_END_NAMESPACE diff --git a/src/imports/particles/particleextruder.cpp b/src/declarative/particles/qsgparticleextruder.cpp similarity index 91% rename from src/imports/particles/particleextruder.cpp rename to src/declarative/particles/qsgparticleextruder.cpp index 3ff5abf996..91b968c8af 100644 --- a/src/imports/particles/particleextruder.cpp +++ b/src/declarative/particles/qsgparticleextruder.cpp @@ -39,16 +39,16 @@ ** ****************************************************************************/ -#include "particleextruder.h" +#include "qsgparticleextruder_p.h" QT_BEGIN_NAMESPACE -ParticleExtruder::ParticleExtruder(QObject *parent) : +QSGParticleExtruder::QSGParticleExtruder(QObject *parent) : QObject(parent), m_fill(true) { } -QPointF ParticleExtruder::extrude(const QRectF &rect) +QPointF QSGParticleExtruder::extrude(const QRectF &rect) { if(m_fill) return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), @@ -70,7 +70,7 @@ QPointF ParticleExtruder::extrude(const QRectF &rect) } } -bool ParticleExtruder::contains(const QRectF &bounds, const QPointF &point) +bool QSGParticleExtruder::contains(const QRectF &bounds, const QPointF &point) { return bounds.contains(point); } diff --git a/src/imports/particles/particleextruder.h b/src/declarative/particles/qsgparticleextruder_p.h similarity index 96% rename from src/imports/particles/particleextruder.h rename to src/declarative/particles/qsgparticleextruder_p.h index 2c417d3f92..41e27eb2fa 100644 --- a/src/imports/particles/particleextruder.h +++ b/src/declarative/particles/qsgparticleextruder_p.h @@ -52,13 +52,13 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class ParticleExtruder : public QObject +class QSGParticleExtruder : public QObject { Q_OBJECT Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Should this be base class, or a BoxExtruder? public: - explicit ParticleExtruder(QObject *parent = 0); + explicit QSGParticleExtruder(QObject *parent = 0); virtual QPointF extrude(const QRectF &); virtual bool contains(const QRectF &bounds, const QPointF &point);//###Needed for follow emitter, but does it belong? Only marginally conceptually valid, and that's from user's perspective bool fill() const diff --git a/src/imports/particles/particle.cpp b/src/declarative/particles/qsgparticlepainter.cpp similarity index 84% rename from src/imports/particles/particle.cpp rename to src/declarative/particles/qsgparticlepainter.cpp index 8f4ecbf733..8814c61b56 100644 --- a/src/imports/particles/particle.cpp +++ b/src/declarative/particles/qsgparticlepainter.cpp @@ -39,10 +39,10 @@ ** ****************************************************************************/ -#include "particle.h" +#include "qsgparticlepainter_p.h" #include QT_BEGIN_NAMESPACE -ParticleType::ParticleType(QSGItem *parent) : +QSGParticlePainter::QSGParticlePainter(QSGItem *parent) : QSGItem(parent), m_system(0) { @@ -52,7 +52,7 @@ ParticleType::ParticleType(QSGItem *parent) : this, SLOT(calcSystemOffset())); } -void ParticleType::componentComplete() +void QSGParticlePainter::componentComplete() { if(!m_system) qWarning() << "Particle created without a particle system specified";//TODO: useful QML warnings, like line number? @@ -60,7 +60,7 @@ void ParticleType::componentComplete() } -void ParticleType::setSystem(ParticleSystem *arg) +void QSGParticlePainter::setSystem(QSGParticleSystem *arg) { if (m_system != arg) { m_system = arg; @@ -76,22 +76,22 @@ void ParticleType::setSystem(ParticleSystem *arg) } } -void ParticleType::load(ParticleData*) +void QSGParticlePainter::load(QSGParticleData*) { } -void ParticleType::reload(ParticleData*) +void QSGParticlePainter::reload(QSGParticleData*) { } -void ParticleType::reset() +void QSGParticlePainter::reset() { //Have to every time because what it's emitting may have changed and that affects particleTypeIndex m_particleStarts.clear(); m_lastStart = 0; } -void ParticleType::setCount(int c) +void QSGParticlePainter::setCount(int c) { if(c == m_count) return; @@ -99,13 +99,13 @@ void ParticleType::setCount(int c) emit countChanged(); } -int ParticleType::count() +int QSGParticlePainter::count() { return m_count; } -int ParticleType::particleTypeIndex(ParticleData* d) +int QSGParticlePainter::particleTypeIndex(QSGParticleData* d) { if(!m_particleStarts.contains(d->group)){ m_particleStarts.insert(d->group, m_lastStart); @@ -117,14 +117,14 @@ int ParticleType::particleTypeIndex(ParticleData* d) } -void ParticleType::calcSystemOffset() +void QSGParticlePainter::calcSystemOffset() { if(!m_system) return; QPointF lastOffset = m_systemOffset; - m_systemOffset = this->mapFromItem(m_system, QPointF()); + m_systemOffset = -1 * this->mapFromItem(m_system, QPointF()); if(lastOffset != m_systemOffset){ - //Reload all particles + //Reload all particles//TODO: Necessary? foreach(const QString &g, m_particles){ int gId = m_system->m_groupIds[g]; for(int i=0; im_groupData[gId]->size; i++) diff --git a/src/imports/particles/particle.h b/src/declarative/particles/qsgparticlepainter_p.h similarity index 84% rename from src/imports/particles/particle.h rename to src/declarative/particles/qsgparticlepainter_p.h index fbb9665a64..8f1e13b70f 100644 --- a/src/imports/particles/particle.h +++ b/src/declarative/particles/qsgparticlepainter_p.h @@ -44,7 +44,7 @@ #include #include -#include "particlesystem.h" +#include "qsgparticlesystem_p.h" QT_BEGIN_HEADER @@ -53,19 +53,19 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class ParticleType : public QSGItem +class QSGParticlePainter : public QSGItem { Q_OBJECT - Q_PROPERTY(ParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) Q_PROPERTY(QStringList particles READ particles WRITE setParticles NOTIFY particlesChanged) public: - explicit ParticleType(QSGItem *parent = 0); - virtual void load(ParticleData*); - virtual void reload(ParticleData*); + explicit QSGParticlePainter(QSGItem *parent = 0); + virtual void load(QSGParticleData*); + virtual void reload(QSGParticleData*); virtual void setCount(int c); virtual int count(); - ParticleSystem* system() const + QSGParticleSystem* system() const { return m_system; } @@ -76,16 +76,15 @@ public: return m_particles; } - int particleTypeIndex(ParticleData*); - virtual void componentComplete(); + int particleTypeIndex(QSGParticleData*); signals: void countChanged(); - void systemChanged(ParticleSystem* arg); + void systemChanged(QSGParticleSystem* arg); void particlesChanged(QStringList arg); public slots: -void setSystem(ParticleSystem* arg); +void setSystem(QSGParticleSystem* arg); void setParticles(QStringList arg) { @@ -98,14 +97,15 @@ private slots: void calcSystemOffset(); protected: virtual void reset(); + virtual void componentComplete(); // virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *){ // qDebug() << "Shouldn't be here..." << this; // return 0; // } - ParticleSystem* m_system; - friend class ParticleSystem; + QSGParticleSystem* m_system; + friend class QSGParticleSystem; int m_count; bool m_pleaseReset; QStringList m_particles; diff --git a/src/declarative/particles/qsgparticlesmodule.cpp b/src/declarative/particles/qsgparticlesmodule.cpp new file mode 100644 index 0000000000..a7a9a9253f --- /dev/null +++ b/src/declarative/particles/qsgparticlesmodule.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgangleddirection_p.h" +#include "qsgcustomparticle_p.h" +#include "qsgellipseextruder_p.h" +#include "qsgemitter_p.h" +#include "qsgfollowemitter_p.h" +#include "qsgfriction_p.h" +#include "qsggravity_p.h" +#include "qsgimageparticle_p.h" +#include "qsgitemparticle_p.h" +#include "qsgkill_p.h" +#include "qsglineextruder_p.h" +#include "qsgmaskextruder_p.h" +#include "qsgmodelparticle_p.h" +#include "qsgparticleaffector_p.h" +#include "qsgparticleemitter_p.h" +#include "qsgparticleextruder_p.h" +#include "qsgparticlepainter_p.h" +#include "qsgparticlesmodule_p.h" +#include "qsgparticlesystem_p.h" +#include "qsgpointattractor_p.h" +#include "qsgpointdirection_p.h" +#include "qsgspritegoal_p.h" +#include "qsgstochasticdirection_p.h" +#include "qsgtargeteddirection_p.h" +#include "qsgturbulence_p.h" +#include "qsgwander_p.h" + +QT_BEGIN_NAMESPACE + +void QSGParticlesModule::defineModule() +{ + const char* uri = "QtQuick.Particles"; + //Debugging only exposition + qmlRegisterType(uri, 2, 0, "ParticlePainter"); + qmlRegisterType(uri, 2, 0, "ParticleEmitter"); + qmlRegisterType(uri, 2, 0, "ParticleExtruder"); + qmlRegisterType(uri, 2, 0, "NullVector"); + //Probably should be nocreate types + + qmlRegisterType(uri, 2, 0, "ParticleSystem"); + + qmlRegisterType(uri, 2, 0, "ImageParticle"); + qmlRegisterType(uri, 2, 0, "CustomParticle"); + qmlRegisterType(uri, 2, 0, "ItemParticle"); + qmlRegisterType(uri, 2, 0, "ModelParticle"); + + qmlRegisterType(uri, 2, 0, "Emitter"); + qmlRegisterType(uri, 2, 0, "FollowEmitter"); + + qmlRegisterType(uri, 2, 0, "EllipseShape"); + qmlRegisterType(uri, 2, 0, "LineShape"); + qmlRegisterType(uri, 2, 0, "MaskShape"); + + qmlRegisterType(uri, 2, 0, "PointDirection"); + qmlRegisterType(uri, 2, 0, "AngledDirection"); + qmlRegisterType(uri, 2, 0, "TargetedDirection"); + + qmlRegisterType(uri, 2, 0, "ParticleAffector");//if it has a triggered signal, it's useful + qmlRegisterType(uri, 2, 0, "Wander"); + qmlRegisterType(uri, 2, 0, "Friction"); + qmlRegisterType(uri, 2, 0, "PointAttractor"); + qmlRegisterType(uri, 2, 0, "Gravity"); + qmlRegisterType(uri, 2, 0, "Kill"); + qmlRegisterType(uri, 2, 0, "SpriteGoal"); + qmlRegisterType(uri, 2, 0 , "Turbulence"); +} + +QT_END_NAMESPACE + +//Q_EXPORT_PLUGIN2(Particles, QT_PREPEND_NAMESPACE(ParticlesModule)) diff --git a/src/imports/particles/burstemitter.cpp b/src/declarative/particles/qsgparticlesmodule_p.h similarity index 86% rename from src/imports/particles/burstemitter.cpp rename to src/declarative/particles/qsgparticlesmodule_p.h index a817172486..1afe0654f6 100644 --- a/src/imports/particles/burstemitter.cpp +++ b/src/declarative/particles/qsgparticlesmodule_p.h @@ -38,18 +38,26 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include "burstemitter.h" + +#ifndef QSGPARTICLESMODULE_H +#define QSGPARTICLESMODULE_H + +#include + +QT_BEGIN_HEADER + QT_BEGIN_NAMESPACE -void BurstEmitter::burst(int count, qreal x, qreal y) +QT_MODULE(Declarative) + +class QSGParticlesModule { - qreal oldX = QSGItem::x(); - qreal oldY = QSGItem::y(); - setX(x); - setY(y); - TrailsEmitter::burst(count); - setX(oldX); - setY(oldY); -} +public: + static void defineModule(); +}; QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGPARTICLESMODULE_H diff --git a/src/imports/particles/particlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp similarity index 81% rename from src/imports/particles/particlesystem.cpp rename to src/declarative/particles/qsgparticlesystem.cpp index 0c9180c2d8..a2aa377c82 100644 --- a/src/imports/particles/particlesystem.cpp +++ b/src/declarative/particles/qsgparticlesystem.cpp @@ -39,17 +39,17 @@ ** ****************************************************************************/ -#include "particlesystem.h" +#include "qsgparticlesystem_p.h" #include -#include "particleemitter.h" -#include "particleaffector.h" -#include "particle.h" +#include "qsgparticleemitter_p.h" +#include "qsgparticleaffector_p.h" +#include "qsgparticlepainter_p.h" #include #include QT_BEGIN_NAMESPACE -ParticleData::ParticleData() +QSGParticleData::QSGParticleData() : group(0) , e(0) , particleIndex(0) @@ -66,21 +66,21 @@ ParticleData::ParticleData() pv.ay = 0; } -ParticleSystem::ParticleSystem(QSGItem *parent) : +QSGParticleSystem::QSGParticleSystem(QSGItem *parent) : QSGItem(parent), m_particle_count(0), m_running(true) , m_startTime(0), m_overwrite(false) { m_groupIds = QHash(); } -void ParticleSystem::registerParticleType(ParticleType* p) +void QSGParticleSystem::registerParticleType(QSGParticlePainter* p) { - m_particles << QPointer(p);//###Set or uniqueness checking? + m_particles << QPointer(p);//###Set or uniqueness checking? reset(); } -void ParticleSystem::registerParticleEmitter(ParticleEmitter* e) +void QSGParticleSystem::registerParticleEmitter(QSGParticleEmitter* e) { - m_emitters << QPointer(e);//###How to get them out? + m_emitters << QPointer(e);//###How to get them out? connect(e, SIGNAL(particleCountChanged()), this, SLOT(countChanged())); connect(e, SIGNAL(particleChanged(QString)), @@ -88,18 +88,18 @@ void ParticleSystem::registerParticleEmitter(ParticleEmitter* e) reset(); } -void ParticleSystem::registerParticleAffector(ParticleAffector* a) +void QSGParticleSystem::registerParticleAffector(QSGParticleAffector* a) { - m_affectors << QPointer(a); + m_affectors << QPointer(a); //reset();//TODO: Slim down the huge batch of resets at the start } -void ParticleSystem::countChanged() +void QSGParticleSystem::countChanged() { reset();//Need to give Particles new Count } -void ParticleSystem::setRunning(bool arg) +void QSGParticleSystem::setRunning(bool arg) { if (m_running != arg) { m_running = arg; @@ -108,13 +108,13 @@ void ParticleSystem::setRunning(bool arg) } } -void ParticleSystem::componentComplete() +void QSGParticleSystem::componentComplete() { QSGItem::componentComplete(); reset(); } -void ParticleSystem::initializeSystem() +void QSGParticleSystem::initializeSystem() { int oldCount = m_particle_count; m_particle_count = 0;//TODO: Only when changed? @@ -143,7 +143,7 @@ void ParticleSystem::initializeSystem() if(!m_emitters.count() || !m_particles.count()) return; - foreach(ParticleEmitter* e, m_emitters){ + foreach(QSGParticleEmitter* e, m_emitters){ if(!m_groupIds.contains(e->particle()) || (!e->particle().isEmpty() && !m_groupIds[e->particle()])){//or it was accidentally inserted by a failed lookup earlier GroupData* gd = new GroupData; @@ -168,7 +168,7 @@ void ParticleSystem::initializeSystem() if(m_particle_count > 16000) qWarning() << "Particle system contains a vast number of particles (>16000). Expect poor performance"; - foreach(ParticleType* particle, m_particles){ + foreach(QSGParticlePainter* particle, m_particles){ int particleCount = 0; if(particle->particles().isEmpty()){//Uses default particle particleCount += m_groupData[0]->size; @@ -189,7 +189,7 @@ void ParticleSystem::initializeSystem() qDebug() << "System Initialized. Size:" << m_particle_count; } -void ParticleSystem::reset() +void QSGParticleSystem::reset() { //Clear guarded pointers which have been deleted int cleared = 0; @@ -197,20 +197,20 @@ void ParticleSystem::reset() cleared += m_particles.removeAll(0); cleared += m_affectors.removeAll(0); //qDebug() << "Reset" << m_emitters.count() << m_particles.count() << "Cleared" << cleared; - foreach(ParticleType* p, m_particles) + foreach(QSGParticlePainter* p, m_particles) p->reset(); - foreach(ParticleEmitter* e, m_emitters) + foreach(QSGParticleEmitter* e, m_emitters) e->reset(); if(!m_running) return; initializeSystem(); - foreach(ParticleType* p, m_particles) + foreach(QSGParticlePainter* p, m_particles) p->update(); - foreach(ParticleEmitter* e, m_emitters) + foreach(QSGParticleEmitter* e, m_emitters) e->emitWindow(0);//Start, so that starttime factors appropriately } -ParticleData* ParticleSystem::newDatum(int groupId) +QSGParticleData* QSGParticleSystem::newDatum(int groupId) { Q_ASSERT(groupId < m_groupData.count());//XXX shouldn't really be an assert Q_ASSERT(m_groupData[groupId]->size); @@ -219,14 +219,14 @@ ParticleData* ParticleSystem::newDatum(int groupId) m_groupData[groupId]->nextIdx = 0; Q_ASSERT(nextIdx < m_data.size()); - ParticleData* ret; + QSGParticleData* ret; if(m_data[nextIdx]){//Recycle, it's faster. ret = m_data[nextIdx]; if(!m_overwrite && ret->stillAlive()){ return 0;//Artificial longevity (or too fast emission) means this guy hasn't died. To maintain count, don't emit a new one }//###Reset? }else{ - ret = new ParticleData; + ret = new QSGParticleData; m_data[nextIdx] = ret; } @@ -237,7 +237,7 @@ ParticleData* ParticleSystem::newDatum(int groupId) return ret; } -void ParticleSystem::emitParticle(ParticleData* pd) +void QSGParticleSystem::emitParticle(QSGParticleData* pd) {// called from prepareNextFrame()->emitWindow - enforce? //Account for relative emitter position QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0)); @@ -246,17 +246,17 @@ void ParticleSystem::emitParticle(ParticleData* pd) pd->pv.y += offset.y(); } - foreach(ParticleAffector *a, m_affectors) + foreach(QSGParticleAffector *a, m_affectors) if(a && a->m_needsReset) a->reset(pd->systemIndex); - foreach(ParticleType* p, m_groupData[pd->group]->types) + foreach(QSGParticlePainter* p, m_groupData[pd->group]->types) if(p) p->load(pd); } -qint64 ParticleSystem::systemSync(ParticleType* p) +qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p) { if (!m_running) return 0; @@ -272,14 +272,14 @@ qint64 ParticleSystem::systemSync(ParticleType* p) qreal time = m_timeInt / 1000.; dt = time - dt; m_needsReset.clear(); - foreach(ParticleEmitter* emitter, m_emitters) + foreach(QSGParticleEmitter* emitter, m_emitters) if(emitter) emitter->emitWindow(m_timeInt); - foreach(ParticleAffector* a, m_affectors) + foreach(QSGParticleAffector* a, m_affectors) if(a) a->affectSystem(dt); - foreach(ParticleData* d, m_needsReset) - foreach(ParticleType* p, m_groupData[d->group]->types) + foreach(QSGParticleData* d, m_needsReset) + foreach(QSGParticlePainter* p, m_groupData[d->group]->types) if(p && d) p->reload(d); } @@ -288,7 +288,7 @@ qint64 ParticleSystem::systemSync(ParticleType* p) } //sets the x accleration without affecting the instantaneous x velocity or position -void ParticleData::setInstantaneousAX(qreal ax) +void QSGParticleData::setInstantaneousAX(qreal ax) { qreal t = (system->m_timeInt / 1000.0) - pv.t; qreal sx = (pv.sx + t*pv.ax) - t*ax; @@ -301,7 +301,7 @@ void ParticleData::setInstantaneousAX(qreal ax) } //sets the x velocity without affecting the instantaneous x postion -void ParticleData::setInstantaneousSX(qreal vx) +void QSGParticleData::setInstantaneousSX(qreal vx) { qreal t = (system->m_timeInt / 1000.0) - pv.t; qreal sx = vx - t*pv.ax; @@ -313,14 +313,14 @@ void ParticleData::setInstantaneousSX(qreal vx) } //sets the instantaneous x postion -void ParticleData::setInstantaneousX(qreal x) +void QSGParticleData::setInstantaneousX(qreal x) { qreal t = (system->m_timeInt / 1000.0) - pv.t; pv.x = x - t*pv.sx - 0.5 * t*t*pv.ax; } //sets the y accleration without affecting the instantaneous y velocity or position -void ParticleData::setInstantaneousAY(qreal ay) +void QSGParticleData::setInstantaneousAY(qreal ay) { qreal t = (system->m_timeInt / 1000.0) - pv.t; qreal sy = (pv.sy + t*pv.ay) - t*ay; @@ -333,7 +333,7 @@ void ParticleData::setInstantaneousAY(qreal ay) } //sets the y velocity without affecting the instantaneous y position -void ParticleData::setInstantaneousSY(qreal vy) +void QSGParticleData::setInstantaneousSY(qreal vy) { qreal t = (system->m_timeInt / 1000.0) - pv.t; //qDebug() << t << (system->m_timeInt/1000.0) << pv.x << pv.sx << pv.ax << pv.x + pv.sx * t + 0.5 * pv.ax * t * t; @@ -346,37 +346,37 @@ void ParticleData::setInstantaneousSY(qreal vy) } //sets the instantaneous Y position -void ParticleData::setInstantaneousY(qreal y) +void QSGParticleData::setInstantaneousY(qreal y) { qreal t = (system->m_timeInt / 1000.0) - pv.t; pv.y = y - t*pv.sy - 0.5 * t*t*pv.ay; } -qreal ParticleData::curX() const +qreal QSGParticleData::curX() const { qreal t = (system->m_timeInt / 1000.0) - pv.t; return pv.x + pv.sx * t + 0.5 * pv.ax * t * t; } -qreal ParticleData::curSX() const +qreal QSGParticleData::curSX() const { qreal t = (system->m_timeInt / 1000.0) - pv.t; return pv.sx + t*pv.ax; } -qreal ParticleData::curY() const +qreal QSGParticleData::curY() const { qreal t = (system->m_timeInt / 1000.0) - pv.t; return pv.y + pv.sy * t + 0.5 * pv.ay * t * t; } -qreal ParticleData::curSY() const +qreal QSGParticleData::curSY() const { qreal t = (system->m_timeInt / 1000.0) - pv.t; return pv.sy + t*pv.ay; } -void ParticleData::debugDump() +void QSGParticleData::debugDump() { qDebug() << "Particle" << group << "Pos: " << pv.x << "," << pv.y @@ -386,7 +386,7 @@ void ParticleData::debugDump() << "Time: " << pv.t << "," < types; + QList types; }; -class ParticleSystem : public QSGItem +class QSGParticleSystem : public QSGItem { Q_OBJECT Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) @@ -80,7 +80,7 @@ class ParticleSystem : public QSGItem */ public: - explicit ParticleSystem(QSGItem *parent = 0); + explicit QSGParticleSystem(QSGItem *parent = 0); bool isRunning() const { @@ -133,20 +133,20 @@ protected: private slots: void countChanged(); public://but only really for related class usage. Perhaps we should all be friends? - void emitParticle(ParticleData* p); - ParticleData* newDatum(int groupId); - qint64 systemSync(ParticleType* p); + void emitParticle(QSGParticleData* p); + QSGParticleData* newDatum(int groupId); + qint64 systemSync(QSGParticlePainter* p); QElapsedTimer m_timestamp; - QVector m_data; - QSet m_needsReset; + QVector m_data; + QSet m_needsReset; QHash m_groupIds; QHash m_groupData;//id, size, start qint64 m_timeInt; bool m_initialized; - void registerParticleType(ParticleType* p); - void registerParticleEmitter(ParticleEmitter* e); - void registerParticleAffector(ParticleAffector* a); + void registerParticleType(QSGParticlePainter* p); + void registerParticleEmitter(QSGParticleEmitter* e); + void registerParticleAffector(QSGParticleAffector* a); bool overwrite() const { return m_overwrite; @@ -156,10 +156,10 @@ public://but only really for related class usage. Perhaps we should all be frien private: void initializeSystem(); bool m_running; - QList > m_emitters; - QList > m_affectors; - QList > m_particles; - QList > m_syncList; + QList > m_emitters; + QList > m_affectors; + QList > m_particles; + QList > m_syncList; qint64 m_startTime; int m_nextGroupId; bool m_overwrite; @@ -181,9 +181,9 @@ struct ParticleVertex { //TODO: Need opacity over life control. More variable size over life? }; -class ParticleData{ +class QSGParticleData{ public: - ParticleData(); + QSGParticleData(); ParticleVertex pv; @@ -210,8 +210,8 @@ public: qreal curSY() const; int group; - ParticleEmitter* e; - ParticleSystem* system; + QSGParticleEmitter* e; + QSGParticleSystem* system; int particleIndex; int systemIndex; diff --git a/src/imports/particles/attractoraffector.cpp b/src/declarative/particles/qsgpointattractor.cpp similarity index 88% rename from src/imports/particles/attractoraffector.cpp rename to src/declarative/particles/qsgpointattractor.cpp index 847cb2c471..3c3ca33086 100644 --- a/src/imports/particles/attractoraffector.cpp +++ b/src/declarative/particles/qsgpointattractor.cpp @@ -39,16 +39,16 @@ ** ****************************************************************************/ -#include "attractoraffector.h" +#include "qsgpointattractor_p.h" #include #include QT_BEGIN_NAMESPACE -AttractorAffector::AttractorAffector(QSGItem *parent) : - ParticleAffector(parent), m_strength(0.0), m_x(0), m_y(0) +QSGPointAttractorAffector::QSGPointAttractorAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_strength(0.0), m_x(0), m_y(0) { } -bool AttractorAffector::affectParticle(ParticleData *d, qreal dt) +bool QSGPointAttractorAffector::affectParticle(QSGParticleData *d, qreal dt) { if(m_strength == 0.0) return false; diff --git a/src/imports/particles/attractoraffector.h b/src/declarative/particles/qsgpointattractor_p.h similarity index 92% rename from src/imports/particles/attractoraffector.h rename to src/declarative/particles/qsgpointattractor_p.h index f41e9ad5e4..d4f715928a 100644 --- a/src/imports/particles/attractoraffector.h +++ b/src/declarative/particles/qsgpointattractor_p.h @@ -41,7 +41,7 @@ #ifndef ATTRACTORAFFECTOR_H #define ATTRACTORAFFECTOR_H -#include "particleaffector.h" +#include "qsgparticleaffector_p.h" QT_BEGIN_HEADER @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class AttractorAffector : public ParticleAffector +class QSGPointAttractorAffector : public QSGParticleAffector { Q_OBJECT //Like Gravitational singularity, but linear to distance instead of quadratic @@ -58,7 +58,7 @@ class AttractorAffector : public ParticleAffector Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) public: - explicit AttractorAffector(QSGItem *parent = 0); + explicit QSGPointAttractorAffector(QSGItem *parent = 0); qreal strength() const { @@ -108,7 +108,7 @@ void setY(qreal arg) } } protected: - virtual bool affectParticle(ParticleData *d, qreal dt); + virtual bool affectParticle(QSGParticleData *d, qreal dt); private: qreal m_strength; qreal m_x; diff --git a/src/imports/particles/pointvector.cpp b/src/declarative/particles/qsgpointdirection.cpp similarity index 90% rename from src/imports/particles/pointvector.cpp rename to src/declarative/particles/qsgpointdirection.cpp index e222965943..c3c4f1c5de 100644 --- a/src/imports/particles/pointvector.cpp +++ b/src/declarative/particles/qsgpointdirection.cpp @@ -39,12 +39,12 @@ ** ****************************************************************************/ -#include "pointvector.h" +#include "qsgpointdirection_p.h" QT_BEGIN_NAMESPACE -PointVector::PointVector(QObject *parent) : - VaryingVector(parent) +QSGPointDirection::QSGPointDirection(QObject *parent) : + QSGStochasticDirection(parent) , m_x(0) , m_y(0) , m_xVariation(0) @@ -52,7 +52,7 @@ PointVector::PointVector(QObject *parent) : { } -const QPointF &PointVector::sample(const QPointF &) +const QPointF &QSGPointDirection::sample(const QPointF &) { m_ret.setX(m_x - m_xVariation + rand() / float(RAND_MAX) * m_xVariation * 2); m_ret.setY(m_y - m_yVariation + rand() / float(RAND_MAX) * m_yVariation * 2); diff --git a/src/imports/particles/pointvector.h b/src/declarative/particles/qsgpointdirection_p.h similarity index 95% rename from src/imports/particles/pointvector.h rename to src/declarative/particles/qsgpointdirection_p.h index 5ffa896680..5e5b052744 100644 --- a/src/imports/particles/pointvector.h +++ b/src/declarative/particles/qsgpointdirection_p.h @@ -41,7 +41,7 @@ #ifndef POINTVECTOR_H #define POINTVECTOR_H -#include "varyingvector.h" +#include "qsgstochasticdirection_p.h" QT_BEGIN_HEADER @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class PointVector : public VaryingVector +class QSGPointDirection : public QSGStochasticDirection { Q_OBJECT Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) @@ -57,7 +57,7 @@ class PointVector : public VaryingVector Q_PROPERTY(qreal xVariation READ xVariation WRITE setXVariation NOTIFY xVariationChanged) Q_PROPERTY(qreal yVariation READ yVariation WRITE setYVariation NOTIFY yVariationChanged) public: - explicit PointVector(QObject *parent = 0); + explicit QSGPointDirection(QObject *parent = 0); virtual const QPointF &sample(const QPointF &from); qreal x() const { diff --git a/src/imports/particles/spritegoalaffector.cpp b/src/declarative/particles/qsgspritegoal.cpp similarity index 78% rename from src/imports/particles/spritegoalaffector.cpp rename to src/declarative/particles/qsgspritegoal.cpp index 2bd56c4a07..8dc98ae314 100644 --- a/src/imports/particles/spritegoalaffector.cpp +++ b/src/declarative/particles/qsgspritegoal.cpp @@ -39,20 +39,20 @@ ** ****************************************************************************/ -#include "spritegoalaffector.h" -#include "spriteparticle.h" -#include "spriteengine.h" -#include "spritestate.h" +#include "qsgspritegoal_p.h" +#include "private/qsgspriteengine_p.h" +#include "private/qsgsprite_p.h" +#include "qsgimageparticle_p.h" #include QT_BEGIN_NAMESPACE -SpriteGoalAffector::SpriteGoalAffector(QSGItem *parent) : - ParticleAffector(parent), m_goalIdx(-1), m_jump(false) +QSGSpriteGoalAffector::QSGSpriteGoalAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_goalIdx(-1), m_jump(false) { } -void SpriteGoalAffector::updateStateIndex(SpriteEngine* e) +void QSGSpriteGoalAffector::updateStateIndex(QSGSpriteEngine* e) { m_lastEngine = e; for(int i=0; istateCount(); i++){ @@ -64,7 +64,7 @@ void SpriteGoalAffector::updateStateIndex(SpriteEngine* e) m_goalIdx = -1;//Can't find it } -void SpriteGoalAffector::setGoalState(QString arg) +void QSGSpriteGoalAffector::setGoalState(QString arg) { if (m_goalState != arg) { m_goalState = arg; @@ -76,14 +76,14 @@ void SpriteGoalAffector::setGoalState(QString arg) } } -bool SpriteGoalAffector::affectParticle(ParticleData *d, qreal dt) +bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt) { Q_UNUSED(dt); //TODO: Affect all engines - SpriteEngine *engine = 0; - foreach(ParticleType *p, m_system->m_groupData[d->group]->types) - if(qobject_cast(p)) - engine = qobject_cast(p)->spriteEngine(); + QSGSpriteEngine *engine = 0; + foreach(QSGParticlePainter *p, m_system->m_groupData[d->group]->types) + if(qobject_cast(p)) + engine = qobject_cast(p)->spriteEngine(); if(!engine) return false; diff --git a/src/imports/particles/spritegoalaffector.h b/src/declarative/particles/qsgspritegoal_p.h similarity index 88% rename from src/imports/particles/spritegoalaffector.h rename to src/declarative/particles/qsgspritegoal_p.h index 3a51562be2..28fb2939e6 100644 --- a/src/imports/particles/spritegoalaffector.h +++ b/src/declarative/particles/qsgspritegoal_p.h @@ -41,7 +41,7 @@ #ifndef SPRITEGOALAFFECTOR_H #define SPRITEGOALAFFECTOR_H -#include "particleaffector.h" +#include "qsgparticleaffector_p.h" QT_BEGIN_HEADER @@ -49,15 +49,15 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class SpriteEngine; +class QSGSpriteEngine; -class SpriteGoalAffector : public ParticleAffector +class QSGSpriteGoalAffector : public QSGParticleAffector { Q_OBJECT Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged) Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged) public: - explicit SpriteGoalAffector(QSGItem *parent = 0); + explicit QSGSpriteGoalAffector(QSGItem *parent = 0); QString goalState() const { @@ -69,7 +69,7 @@ public: return m_jump; } protected: - virtual bool affectParticle(ParticleData *d, qreal dt); + virtual bool affectParticle(QSGParticleData *d, qreal dt); signals: void goalStateChanged(QString arg); @@ -90,10 +90,10 @@ void setJump(bool arg) } private: - void updateStateIndex(SpriteEngine* e); + void updateStateIndex(QSGSpriteEngine* e); QString m_goalState; int m_goalIdx; - SpriteEngine* m_lastEngine; + QSGSpriteEngine* m_lastEngine; bool m_jump; }; diff --git a/src/imports/particles/varyingvector.cpp b/src/declarative/particles/qsgstochasticdirection.cpp similarity index 90% rename from src/imports/particles/varyingvector.cpp rename to src/declarative/particles/qsgstochasticdirection.cpp index ab09f47f79..3673b9c7a7 100644 --- a/src/imports/particles/varyingvector.cpp +++ b/src/declarative/particles/qsgstochasticdirection.cpp @@ -39,16 +39,16 @@ ** ****************************************************************************/ -#include "varyingvector.h" +#include "qsgstochasticdirection_p.h" QT_BEGIN_NAMESPACE -VaryingVector::VaryingVector(QObject *parent) : +QSGStochasticDirection::QSGStochasticDirection(QObject *parent) : QObject(parent) { } -const QPointF &VaryingVector::sample(const QPointF &from) +const QPointF &QSGStochasticDirection::sample(const QPointF &from) { return m_ret; } diff --git a/src/imports/particles/varyingvector.h b/src/declarative/particles/qsgstochasticdirection_p.h similarity index 94% rename from src/imports/particles/varyingvector.h rename to src/declarative/particles/qsgstochasticdirection_p.h index 9f80366d2e..da3a4302b1 100644 --- a/src/imports/particles/varyingvector.h +++ b/src/declarative/particles/qsgstochasticdirection_p.h @@ -52,11 +52,11 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class VaryingVector : public QObject +class QSGStochasticDirection : public QObject { Q_OBJECT public: - explicit VaryingVector(QObject *parent = 0); + explicit QSGStochasticDirection(QObject *parent = 0); virtual const QPointF &sample(const QPointF &from); signals: diff --git a/src/imports/particles/directedvector.cpp b/src/declarative/particles/qsgtargeteddirection.cpp similarity index 90% rename from src/imports/particles/directedvector.cpp rename to src/declarative/particles/qsgtargeteddirection.cpp index c1aeba3ad2..9f1a868512 100644 --- a/src/imports/particles/directedvector.cpp +++ b/src/declarative/particles/qsgtargeteddirection.cpp @@ -39,14 +39,14 @@ ** ****************************************************************************/ -#include "directedvector.h" -#include "particleemitter.h" +#include "qsgtargeteddirection_p.h" +#include "qsgparticleemitter_p.h" #include #include QT_BEGIN_NAMESPACE -DirectedVector::DirectedVector(QObject *parent) : - VaryingVector(parent) +QSGTargetedDirection::QSGTargetedDirection(QObject *parent) : + QSGStochasticDirection(parent) , m_targetX(0) , m_targetY(0) , m_targetVariation(0) @@ -57,13 +57,13 @@ DirectedVector::DirectedVector(QObject *parent) : { } -const QPointF &DirectedVector::sample(const QPointF &from) +const QPointF &QSGTargetedDirection::sample(const QPointF &from) { //###This approach loses interpolating the last position of the target (like we could with the emitter) is it worthwhile? qreal targetX; qreal targetY; if(m_targetItem){ - ParticleEmitter* parentEmitter = qobject_cast(parent()); + QSGParticleEmitter* parentEmitter = qobject_cast(parent()); targetX = m_targetItem->width()/2; targetY = m_targetItem->height()/2; if(!parentEmitter){ diff --git a/src/imports/particles/directedvector.h b/src/declarative/particles/qsgtargeteddirection_p.h similarity index 96% rename from src/imports/particles/directedvector.h rename to src/declarative/particles/qsgtargeteddirection_p.h index f1d0919bc3..4010505858 100644 --- a/src/imports/particles/directedvector.h +++ b/src/declarative/particles/qsgtargeteddirection_p.h @@ -41,7 +41,7 @@ #ifndef DIRECTEDVECTOR_H #define DIRECTEDVECTOR_H -#include "varyingvector.h" +#include "qsgstochasticdirection_p.h" QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QSGItem; -class DirectedVector : public VaryingVector +class QSGTargetedDirection : public QSGStochasticDirection { Q_OBJECT Q_PROPERTY(qreal targetX READ targetX WRITE setTargetX NOTIFY targetXChanged) @@ -64,7 +64,7 @@ class DirectedVector : public VaryingVector Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged) public: - explicit DirectedVector(QObject *parent = 0); + explicit QSGTargetedDirection(QObject *parent = 0); virtual const QPointF &sample(const QPointF &from); qreal targetX() const diff --git a/src/imports/particles/turbulenceaffector.cpp b/src/declarative/particles/qsgturbulence.cpp similarity index 91% rename from src/imports/particles/turbulenceaffector.cpp rename to src/declarative/particles/qsgturbulence.cpp index d29f09d974..476db9c4b0 100644 --- a/src/imports/particles/turbulenceaffector.cpp +++ b/src/declarative/particles/qsgturbulence.cpp @@ -39,21 +39,21 @@ ** ****************************************************************************/ -#include "turbulenceaffector.h" -#include "particle.h" +#include "qsgturbulence_p.h" +#include "qsgparticlepainter_p.h"//TODO: Why was this needed again? #include #include #include QT_BEGIN_NAMESPACE -TurbulenceAffector::TurbulenceAffector(QSGItem *parent) : - ParticleAffector(parent), +QSGTurbulenceAffector::QSGTurbulenceAffector(QSGItem *parent) : + QSGParticleAffector(parent), m_strength(10), m_lastT(0), m_frequency(64), m_gridSize(10), m_field(0), m_inited(false) { //TODO: Update grid on size change } -TurbulenceAffector::~TurbulenceAffector() +QSGTurbulenceAffector::~QSGTurbulenceAffector() { if (m_field) { for(int i=0; im_data){ + foreach(QSGParticleData *d, m_system->m_data){ if(!d || !activeGroup(d->group)) return; qreal fx = 0.0; diff --git a/src/imports/particles/turbulenceaffector.h b/src/declarative/particles/qsgturbulence_p.h similarity index 93% rename from src/imports/particles/turbulenceaffector.h rename to src/declarative/particles/qsgturbulence_p.h index 2dc2ddcdfd..29483fbc70 100644 --- a/src/imports/particles/turbulenceaffector.h +++ b/src/declarative/particles/qsgturbulence_p.h @@ -41,7 +41,7 @@ #ifndef TURBULENCEAFFECTOR_H #define TURBULENCEAFFECTOR_H -#include "particleaffector.h" +#include "qsgparticleaffector_p.h" #include QT_BEGIN_HEADER @@ -51,17 +51,17 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class ParticleType; +class QSGParticlePainter; -class TurbulenceAffector : public ParticleAffector +class QSGTurbulenceAffector : public QSGParticleAffector { Q_OBJECT Q_PROPERTY(int strength READ strength WRITE setStrength NOTIFY strengthChanged) Q_PROPERTY(int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged) Q_PROPERTY(int gridSize READ size WRITE setSize NOTIFY sizeChanged) public: - explicit TurbulenceAffector(QSGItem *parent = 0); - ~TurbulenceAffector(); + explicit QSGTurbulenceAffector(QSGItem *parent = 0); + ~QSGTurbulenceAffector(); virtual void affectSystem(qreal dt); int strength() const diff --git a/src/imports/particles/wanderaffector.cpp b/src/declarative/particles/qsgwander.cpp similarity index 89% rename from src/imports/particles/wanderaffector.cpp rename to src/declarative/particles/qsgwander.cpp index 4d3ba5f7ce..e6787f2570 100644 --- a/src/imports/particles/wanderaffector.cpp +++ b/src/declarative/particles/qsgwander.cpp @@ -39,24 +39,24 @@ ** ****************************************************************************/ -#include "wanderaffector.h" -#include "particlesystem.h"//for ParticlesVertices +#include "qsgwander_p.h" +#include "qsgparticlesystem_p.h"//for ParticlesVertices QT_BEGIN_NAMESPACE -WanderAffector::WanderAffector(QSGItem *parent) : - ParticleAffector(parent) +QSGWanderAffector::QSGWanderAffector(QSGItem *parent) : + QSGParticleAffector(parent) { m_needsReset = true; } -WanderAffector::~WanderAffector() +QSGWanderAffector::~QSGWanderAffector() { for(QHash::const_iterator iter=m_wanderData.constBegin(); iter != m_wanderData.constEnd(); iter++) delete (*iter); } -WanderData* WanderAffector::getData(int idx) +WanderData* QSGWanderAffector::getData(int idx) { if(m_wanderData.contains(idx)) return m_wanderData[idx]; @@ -72,14 +72,14 @@ WanderData* WanderAffector::getData(int idx) return d; } -void WanderAffector::reset(int systemIdx) +void QSGWanderAffector::reset(int systemIdx) { if(m_wanderData.contains(systemIdx)) delete m_wanderData[systemIdx]; m_wanderData.remove(systemIdx); } -bool WanderAffector::affectParticle(ParticleData* data, qreal dt) +bool QSGWanderAffector::affectParticle(QSGParticleData* data, qreal dt) { WanderData* d = getData(data->systemIndex); if (m_xVariance != 0.) { diff --git a/src/imports/particles/wanderaffector.h b/src/declarative/particles/qsgwander_p.h similarity index 92% rename from src/imports/particles/wanderaffector.h rename to src/declarative/particles/qsgwander_p.h index 612872830b..45c8ca8b20 100644 --- a/src/imports/particles/wanderaffector.h +++ b/src/declarative/particles/qsgwander_p.h @@ -42,7 +42,7 @@ #ifndef WANDERAFFECTOR_H #define WANDERAFFECTOR_H #include -#include "particleaffector.h" +#include "qsgparticleaffector_p.h" QT_BEGIN_HEADER @@ -51,8 +51,6 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class SpriteEmitter; - struct WanderData{ qreal x_vel; qreal y_vel; @@ -62,7 +60,7 @@ struct WanderData{ qreal y_var; }; -class WanderAffector : public ParticleAffector +class QSGWanderAffector : public QSGParticleAffector { Q_OBJECT Q_PROPERTY(qreal xVariance READ xVariance WRITE setXVariance NOTIFY xVarianceChanged) @@ -70,8 +68,8 @@ class WanderAffector : public ParticleAffector Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged) public: - explicit WanderAffector(QSGItem *parent = 0); - ~WanderAffector(); + explicit QSGWanderAffector(QSGItem *parent = 0); + ~QSGWanderAffector(); virtual void reset(int systemIdx); qreal xVariance() const @@ -89,7 +87,7 @@ public: return m_pace; } protected: - virtual bool affectParticle(ParticleData *d, qreal dt); + virtual bool affectParticle(QSGParticleData *d, qreal dt); signals: void xVarianceChanged(qreal arg); diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index 80845b4c81..ab481ac87d 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -106,6 +106,7 @@ #include #include #include +#include #include #ifdef Q_OS_WIN // for %APPDATA% @@ -362,6 +363,7 @@ QDeclarativeEnginePrivate::QDeclarativeEnginePrivate(QDeclarativeEngine *e) QDeclarativeUtilModule::defineModule(); QDeclarativeEnginePrivate::defineModule(); QSGItemsModule::defineModule(); + QSGParticlesModule::defineModule(); QDeclarativeValueTypeFactory::registerValueTypes(); } globalClass = new QDeclarativeGlobalScriptClass(&scriptEngine); diff --git a/src/imports/particles/burstemitter.h b/src/imports/particles/burstemitter.h deleted file mode 100644 index cffa7911af..0000000000 --- a/src/imports/particles/burstemitter.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef BURSTEMITTER_H -#define BURSTEMITTER_H - -#include "trailsemitter.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -//Convenience Class - can't do function overloads in QML? -class BurstEmitter : public TrailsEmitter -{ - Q_OBJECT -public: - explicit BurstEmitter(QSGItem* parent = 0):TrailsEmitter(parent){} -public slots: - void burst(int count, qreal x, qreal y); -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif diff --git a/src/imports/particles/coloredparticle.cpp b/src/imports/particles/coloredparticle.cpp deleted file mode 100644 index 22ef2d2eba..0000000000 --- a/src/imports/particles/coloredparticle.cpp +++ /dev/null @@ -1,540 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include "coloredparticle.h" -#include "particleemitter.h" -#include -#include - -QT_BEGIN_NAMESPACE - -class ParticleTrailsMaterial : public QSGMaterial -{ -public: - ParticleTrailsMaterial() - : timestamp(0) - { - setFlag(Blending, true); - } - - ~ParticleTrailsMaterial() - { - delete texture; - } - - virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } - virtual QSGMaterialShader *createShader() const; - virtual int compare(const QSGMaterial *other) const - { - return this - static_cast(other); - } - - QSGTexture *texture; - - qreal timestamp; -}; - - -class ParticleTrailsMaterialData : public QSGMaterialShader -{ -public: - ParticleTrailsMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) - { - QFile vf(vertexFile ? vertexFile : ":resources/trailsvertex.shader"); - vf.open(QFile::ReadOnly); - m_vertex_code = vf.readAll(); - - QFile ff(fragmentFile ? fragmentFile : ":resources/trailsfragment.shader"); - ff.open(QFile::ReadOnly); - m_fragment_code = ff.readAll(); - - Q_ASSERT(!m_vertex_code.isNull()); - Q_ASSERT(!m_fragment_code.isNull()); - } - - void deactivate() { - QSGMaterialShader::deactivate(); - - for (int i=0; i<8; ++i) { - program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); - } - } - - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) - { - ParticleTrailsMaterial *m = static_cast(newEffect); - state.context()->functions()->glActiveTexture(GL_TEXTURE0); - m->texture->bind(); - - program()->setUniformValue(m_opacity_id, state.opacity()); - program()->setUniformValue(m_timestamp_id, (float) m->timestamp); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrix_id, state.combinedMatrix()); - } - - virtual void initialize() { - m_matrix_id = program()->uniformLocation("matrix"); - m_opacity_id = program()->uniformLocation("opacity"); - m_timestamp_id = program()->uniformLocation("timestamp"); - } - - virtual const char *vertexShader() const { return m_vertex_code.constData(); } - virtual const char *fragmentShader() const { return m_fragment_code.constData(); } - - virtual char const *const *attributeNames() const { - static const char *attr[] = { - "vPos", - "vTex", - "vData", - "vVec", - "vColor", - 0 - }; - return attr; - } - - virtual bool isColorTable() const { return false; } - - int m_matrix_id; - int m_opacity_id; - int m_timestamp_id; - - QByteArray m_vertex_code; - QByteArray m_fragment_code; - - static float chunkOfBytes[1024]; -}; -float ParticleTrailsMaterialData::chunkOfBytes[1024]; - - -QSGMaterialShader *ParticleTrailsMaterial::createShader() const -{ - return new ParticleTrailsMaterialData; -} - - -class ParticleTrailsMaterialCT : public ParticleTrailsMaterial -{ -public: - ParticleTrailsMaterialCT() - { - } - - ~ParticleTrailsMaterialCT() - { - delete colortable; - delete sizetable; - delete opacitytable; - } - - virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } - virtual QSGMaterialShader *createShader() const; - - QSGTexture *colortable; - QSGTexture *sizetable; - QSGTexture *opacitytable; -}; - - -class ParticleTrailsMaterialDataCT : public ParticleTrailsMaterialData -{ -public: - ParticleTrailsMaterialDataCT() - : ParticleTrailsMaterialData(":resources/ctvertex.shader", ":resources/ctfragment.shader") - { - } - - bool isColorTable() const { return true; } - - virtual void initialize() { - ParticleTrailsMaterialData::initialize(); - m_colortable_id = program()->uniformLocation("colortable"); - m_sizetable_id = program()->uniformLocation("sizetable"); - m_opacitytable_id = program()->uniformLocation("opacitytable"); - } - - virtual void updateState(const RenderState &state, QSGMaterial *current, QSGMaterial *old) - { - // Bind the texture to unit 1 before calling the base class, so that the - // base class can set active texture back to 0. - ParticleTrailsMaterialCT *m = static_cast(current); - state.context()->functions()->glActiveTexture(GL_TEXTURE1); - m->colortable->bind(); - program()->setUniformValue(m_colortable_id, 1); - - state.context()->functions()->glActiveTexture(GL_TEXTURE2); - m->sizetable->bind(); - program()->setUniformValue(m_sizetable_id, 2); - - state.context()->functions()->glActiveTexture(GL_TEXTURE3); - m->opacitytable->bind(); - program()->setUniformValue(m_opacitytable_id, 3); - - ParticleTrailsMaterialData::updateState(state, current, old); - } - - int m_colortable_id; - int m_sizetable_id; - int m_opacitytable_id; -}; - - -QSGMaterialShader *ParticleTrailsMaterialCT::createShader() const -{ - return new ParticleTrailsMaterialDataCT; -} - -ColoredParticle::ColoredParticle(QSGItem* parent) - : ParticleType(parent) - , m_do_reset(false) - , m_color(Qt::white) - , m_color_variation(0.5) - , m_node(0) - , m_material(0) - , m_alphaVariation(0.0) - , m_alpha(1.0) - , m_redVariation(0.0) - , m_greenVariation(0.0) - , m_blueVariation(0.0) -{ - setFlag(ItemHasContents); -} - -void ColoredParticle::setImage(const QUrl &image) -{ - if (image == m_image_name) - return; - m_image_name = image; - emit imageChanged(); - reset(); -} - - -void ColoredParticle::setColortable(const QUrl &table) -{ - if (table == m_colortable_name) - return; - m_colortable_name = table; - emit colortableChanged(); - reset(); -} - -void ColoredParticle::setSizetable(const QUrl &table) -{ - if (table == m_sizetable_name) - return; - m_sizetable_name = table; - emit sizetableChanged(); - reset(); -} - -void ColoredParticle::setOpacitytable(const QUrl &table) -{ - if (table == m_opacitytable_name) - return; - m_opacitytable_name = table; - emit opacitytableChanged(); - reset(); -} - -void ColoredParticle::setColor(const QColor &color) -{ - if (color == m_color) - return; - m_color = color; - emit colorChanged(); - //m_system->pleaseReset();//XXX -} - -void ColoredParticle::setColorVariation(qreal var) -{ - if (var == m_color_variation) - return; - m_color_variation = var; - emit colorVariationChanged(); - //m_system->pleaseReset();//XXX -} - -void ColoredParticle::setCount(int c) -{ - ParticleType::setCount(c); - m_pleaseReset = true; -} - -void ColoredParticle::reset() -{ - ParticleType::reset(); - m_pleaseReset = true; -} - -static QSGGeometry::Attribute ColoredParticle_Attributes[] = { - { 0, 2, GL_FLOAT }, // Position - { 1, 2, GL_FLOAT }, // TexCoord - { 2, 4, GL_FLOAT }, // Data - { 3, 4, GL_FLOAT }, // Vectors - { 4, 4, GL_UNSIGNED_BYTE } // Colors -}; - -static QSGGeometry::AttributeSet ColoredParticle_AttributeSet = -{ - 5, // Attribute Count - (2 + 2 + 4 + 4) * sizeof(float) + 4 * sizeof(uchar), - ColoredParticle_Attributes -}; - -QSGGeometryNode* ColoredParticle::buildParticleNode() -{ - if (m_count * 4 > 0xffff) { - printf("ColoredParticle: Too many particles... \n"); - return 0; - } - - if(m_count <= 0) { - printf("ColoredParticle: Too few particles... \n"); - return 0; - } - - QImage image(m_image_name.toLocalFile()); - if (image.isNull()) { - printf("ParticleTrails: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile())); - return 0; - } - - int vCount = m_count * 4; - int iCount = m_count * 6; - - QSGGeometry *g = new QSGGeometry(ColoredParticle_AttributeSet, vCount, iCount); - g->setDrawingMode(GL_TRIANGLES); - - ColoredParticleVertex *vertices = (ColoredParticleVertex *) g->vertexData(); - for (int p=0; pindexDataAsUShort(); - for (int i=0; i(m_material); - ct_material->colortable = sceneGraphEngine()->createTextureFromImage(colortable); - ct_material->sizetable = sceneGraphEngine()->createTextureFromImage(sizetable); - ct_material->opacitytable = sceneGraphEngine()->createTextureFromImage(opacitytable); - } - - if (!m_material) - m_material = new ParticleTrailsMaterial(); - - - m_material->texture = sceneGraphEngine()->createTextureFromImage(image); - m_material->texture->setFiltering(QSGTexture::Linear); - - m_node = new QSGGeometryNode(); - m_node->setGeometry(g); - m_node->setMaterial(m_material); - - m_last_particle = 0; - - return m_node; -} - -QSGNode *ColoredParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) -{ - if(m_pleaseReset){ - if(m_node) - delete m_node; - if(m_material) - delete m_material; - - m_node = 0; - m_material = 0; - m_pleaseReset = false; - } - - if(m_system && m_system->isRunning()) - prepareNextFrame(); - if (m_node){ - update(); - m_node->markDirty(QSGNode::DirtyMaterial); - } - - return m_node; -} - -void ColoredParticle::prepareNextFrame() -{ - if (m_node == 0){ //TODO: Staggered loading (as emitted) - m_node = buildParticleNode(); - if(m_node == 0) - return; - } - qint64 timeStamp = m_system->systemSync(this); - - qreal time = timeStamp / 1000.; - m_material->timestamp = time; -} - -void ColoredParticle::reloadColor(const Color4ub &c, ParticleData* d) -{ - ColoredParticleVertices *particles = (ColoredParticleVertices *) m_node->geometry()->vertexData(); - int pos = particleTypeIndex(d); - ColoredParticleVertices &p = particles[pos]; - p.v1.color = p.v2.color = p.v3.color = p.v4.color = c; -} - -void ColoredParticle::vertexCopy(ColoredParticleVertex &b,const ParticleVertex& a) -{ - b.x = a.x - m_systemOffset.x(); - b.y = a.y - m_systemOffset.y(); - b.t = a.t; - b.lifeSpan = a.lifeSpan; - b.size = a.size; - b.endSize = a.endSize; - b.sx = a.sx; - b.sy = a.sy; - b.ax = a.ax; - b.ay = a.ay; -} - -void ColoredParticle::reload(ParticleData *d) -{ - if (m_node == 0) - return; - - ColoredParticleVertices *particles = (ColoredParticleVertices *) m_node->geometry()->vertexData(); - - int pos = particleTypeIndex(d); - - ColoredParticleVertices &p = particles[pos]; - - //Perhaps we could be more efficient? - vertexCopy(p.v1, d->pv); - vertexCopy(p.v2, d->pv); - vertexCopy(p.v3, d->pv); - vertexCopy(p.v4, d->pv); -} - -void ColoredParticle::load(ParticleData *d) -{ - if (m_node == 0) - return; - - //Color initialization - // Particle color - Color4ub color; - qreal redVariation = m_color_variation + m_redVariation; - qreal greenVariation = m_color_variation + m_greenVariation; - qreal blueVariation = m_color_variation + m_blueVariation; - color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation; - color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation; - color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation; - color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation; - ColoredParticleVertices *particles = (ColoredParticleVertices *) m_node->geometry()->vertexData(); - ColoredParticleVertices &p = particles[particleTypeIndex(d)]; - p.v1.color = p.v2.color = p.v3.color = p.v4.color = color; - - vertexCopy(p.v1, d->pv); - vertexCopy(p.v2, d->pv); - vertexCopy(p.v3, d->pv); - vertexCopy(p.v4, d->pv); -} - -QT_END_NAMESPACE diff --git a/src/imports/particles/coloredparticle.h b/src/imports/particles/coloredparticle.h deleted file mode 100644 index 446b764941..0000000000 --- a/src/imports/particles/coloredparticle.h +++ /dev/null @@ -1,254 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef COLOREDPARTICLE_H -#define COLOREDPARTICLE_H -#include "particle.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class ParticleTrailsMaterial; -class QSGGeometryNode; - -struct Color4ub { - uchar r; - uchar g; - uchar b; - uchar a; -}; - -struct ColoredParticleVertex { - float x; - float y; - float tx; - float ty; - float t; - float lifeSpan; - float size; - float endSize; - float sx; - float sy; - float ax; - float ay; - Color4ub color; -}; - -struct ColoredParticleVertices { - ColoredParticleVertex v1; - ColoredParticleVertex v2; - ColoredParticleVertex v3; - ColoredParticleVertex v4; -}; - -class ColoredParticle : public ParticleType -{ - Q_OBJECT - Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged) - Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged) - Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged) - Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged) - - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) - //Stacks (added) with individual colorVariations - Q_PROPERTY(qreal colorVariation READ colorVariation WRITE setColorVariation NOTIFY colorVariationChanged) - Q_PROPERTY(qreal redVariation READ redVariation WRITE setRedVariation NOTIFY redVariationChanged) - Q_PROPERTY(qreal greenVariation READ greenVariation WRITE setGreenVariation NOTIFY greenVariationChanged) - Q_PROPERTY(qreal blueVariation READ blueVariation WRITE setBlueVariation NOTIFY blueVariationChanged) - //Stacks (multiplies) with the Alpha in the color, mostly here so you can use svg color names (which have full alpha) - Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged) - Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged) - -public: - explicit ColoredParticle(QSGItem *parent = 0); - virtual ~ColoredParticle(){} - - virtual void load(ParticleData*); - virtual void reload(ParticleData*); - virtual void setCount(int c); - - QUrl image() const { return m_image_name; } - void setImage(const QUrl &image); - - QUrl colortable() const { return m_colortable_name; } - void setColortable(const QUrl &table); - - QUrl sizetable() const { return m_sizetable_name; } - void setSizetable (const QUrl &table); - - QUrl opacitytable() const { return m_opacitytable_name; } - void setOpacitytable(const QUrl &table); - - QColor color() const { return m_color; } - void setColor(const QColor &color); - - qreal colorVariation() const { return m_color_variation; } - void setColorVariation(qreal var); - - qreal renderOpacity() const { return m_render_opacity; } - - qreal alphaVariation() const - { - return m_alphaVariation; - } - - qreal alpha() const - { - return m_alpha; - } - - qreal redVariation() const - { - return m_redVariation; - } - - qreal greenVariation() const - { - return m_greenVariation; - } - - qreal blueVariation() const - { - return m_blueVariation; - } - -signals: - - void imageChanged(); - void colortableChanged(); - void sizetableChanged(); - void opacitytableChanged(); - - void colorChanged(); - void colorVariationChanged(); - - void particleDurationChanged(); - void alphaVariationChanged(qreal arg); - - void alphaChanged(qreal arg); - - void redVariationChanged(qreal arg); - - void greenVariationChanged(qreal arg); - - void blueVariationChanged(qreal arg); - -public slots: - void setAlphaVariation(qreal arg) - { - if (m_alphaVariation != arg) { - m_alphaVariation = arg; - emit alphaVariationChanged(arg); - } - } - - void setAlpha(qreal arg) - { - if (m_alpha != arg) { - m_alpha = arg; - emit alphaChanged(arg); - } - } - - void setRedVariation(qreal arg) - { - if (m_redVariation != arg) { - m_redVariation = arg; - emit redVariationChanged(arg); - } - } - - void setGreenVariation(qreal arg) - { - if (m_greenVariation != arg) { - m_greenVariation = arg; - emit greenVariationChanged(arg); - } - } - - void setBlueVariation(qreal arg) - { - if (m_blueVariation != arg) { - m_blueVariation = arg; - emit blueVariationChanged(arg); - } - } - - void reloadColor(const Color4ub &c, ParticleData* d); -protected: - QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - void reset(); - void prepareNextFrame(); - QSGGeometryNode* buildParticleNode(); -private: - void vertexCopy(ColoredParticleVertex &b,const ParticleVertex& a); - bool m_do_reset; - - QUrl m_image_name; - QUrl m_colortable_name; - QUrl m_sizetable_name; - QUrl m_opacitytable_name; - - - QColor m_color; - qreal m_color_variation; - qreal m_particleDuration; - - QSGGeometryNode *m_node; - ParticleTrailsMaterial *m_material; - - // derived values... - int m_last_particle; - - qreal m_render_opacity; - qreal m_alphaVariation; - qreal m_alpha; - qreal m_redVariation; - qreal m_greenVariation; - qreal m_blueVariation; -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // COLOREDPARTICLE_H diff --git a/src/imports/particles/deformableparticle.cpp b/src/imports/particles/deformableparticle.cpp deleted file mode 100644 index 768e4eb4b5..0000000000 --- a/src/imports/particles/deformableparticle.cpp +++ /dev/null @@ -1,438 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include "deformableparticle.h" -#include -#include - -QT_BEGIN_NAMESPACE - -const float CONV = 0.017453292519943295; -class DeformableParticleMaterial : public QSGMaterial -{ -public: - DeformableParticleMaterial() - : timestamp(0) - { - setFlag(Blending, true); - } - - ~DeformableParticleMaterial() - { - delete texture; - } - - virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } - virtual QSGMaterialShader *createShader() const; - virtual int compare(const QSGMaterial *other) const - { - return this - static_cast(other); - } - - QSGTexture *texture; - - qreal timestamp; -}; - - -class DeformableParticleMaterialData : public QSGMaterialShader -{ -public: - DeformableParticleMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) - { - QFile vf(vertexFile ? vertexFile : ":resources/deformablevertex.shader"); - vf.open(QFile::ReadOnly); - m_vertex_code = vf.readAll(); - - QFile ff(fragmentFile ? fragmentFile : ":resources/deformablefragment.shader"); - ff.open(QFile::ReadOnly); - m_fragment_code = ff.readAll(); - - Q_ASSERT(!m_vertex_code.isNull()); - Q_ASSERT(!m_fragment_code.isNull()); - } - - void deactivate() { - QSGMaterialShader::deactivate(); - - for (int i=0; i<8; ++i) { - program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); - } - } - - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) - { - DeformableParticleMaterial *m = static_cast(newEffect); - state.context()->functions()->glActiveTexture(GL_TEXTURE0); - m->texture->bind(); - - program()->setUniformValue(m_opacity_id, state.opacity()); - program()->setUniformValue(m_timestamp_id, (float) m->timestamp); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrix_id, state.combinedMatrix()); - } - - virtual void initialize() { - m_matrix_id = program()->uniformLocation("matrix"); - m_opacity_id = program()->uniformLocation("opacity"); - m_timestamp_id = program()->uniformLocation("timestamp"); - } - - virtual const char *vertexShader() const { return m_vertex_code.constData(); } - virtual const char *fragmentShader() const { return m_fragment_code.constData(); } - - virtual char const *const *attributeNames() const { - static const char *attr[] = { - "vPos", - "vTex", - "vData", - "vVec", - "vDeformVec", - "vRotation", - 0 - }; - return attr; - } - - virtual bool isColorTable() const { return false; } - - int m_matrix_id; - int m_opacity_id; - int m_timestamp_id; - - QByteArray m_vertex_code; - QByteArray m_fragment_code; - - static float chunkOfBytes[1024]; -}; -float DeformableParticleMaterialData::chunkOfBytes[1024]; - - -QSGMaterialShader *DeformableParticleMaterial::createShader() const -{ - return new DeformableParticleMaterialData; -} - -struct DeformableParticleVertex { - float x; - float y; - float tx; - float ty; - float t; - float lifeSpan; - float size; - float endSize; - float sx; - float sy; - float ax; - float ay; - float xx; - float xy; - float yx; - float yy; - float rotation; - float rotationSpeed; - float autoRotate;//Assume that GPUs prefer floats to bools -}; - -struct DeformableParticleVertices { - DeformableParticleVertex v1; - DeformableParticleVertex v2; - DeformableParticleVertex v3; - DeformableParticleVertex v4; -}; - - -DeformableParticle::DeformableParticle(QSGItem* parent) - : ParticleType(parent) - , m_do_reset(false) - , m_rotation(0) - , m_autoRotation(false) - , m_xVector(0) - , m_yVector(0) - , m_rotationVariation(0) - , m_rotationSpeed(0) - , m_rotationSpeedVariation(0) -{ - setFlag(ItemHasContents); -} - -void DeformableParticle::setImage(const QUrl &image) -{ - if (image == m_image) - return; - m_image = image; - emit imageChanged(); - reset(); -} - -void DeformableParticle::setCount(int c) -{ - ParticleType::setCount(c); - m_pleaseReset = true; -} - -void DeformableParticle::reset() -{ - ParticleType::reset(); - m_pleaseReset = true; -} - -static QSGGeometry::Attribute DeformableParticle_Attributes[] = { - { 0, 2, GL_FLOAT }, // Position - { 1, 2, GL_FLOAT }, // TexCoord - { 2, 4, GL_FLOAT }, // Data - { 3, 4, GL_FLOAT }, // Vectors - { 4, 4, GL_FLOAT }, // DeformationVectors - { 5, 3, GL_FLOAT } // Rotation -}; - -static QSGGeometry::AttributeSet DeformableParticle_AttributeSet = -{ - 6, // Attribute Count - (2 + 2 + 4 + 4 + 4 + 3) * sizeof(float), - DeformableParticle_Attributes -}; - -QSGGeometryNode* DeformableParticle::buildParticleNode() -{ - if (m_count * 4 > 0xffff) { - printf("DeformableParticle: Too many particles... \n"); - return 0; - } - - if(m_count <= 0) { - printf("DeformableParticle: Too few particles... \n"); - return 0; - } - - QImage image(m_image.toLocalFile()); - if (image.isNull()) { - printf("DeformableParticle: loading image failed... '%s'\n", qPrintable(m_image.toLocalFile())); - return 0; - } - - int vCount = m_count * 4; - int iCount = m_count * 6; - - QSGGeometry *g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount); - g->setDrawingMode(GL_TRIANGLES); - - DeformableParticleVertex *vertices = (DeformableParticleVertex *) g->vertexData(); - for (int p=0; pindexDataAsUShort(); - for (int i=0; itexture = sceneGraphEngine()->createTextureFromImage(image); - m_material->texture->setFiltering(QSGTexture::Linear); - - m_node = new QSGGeometryNode(); - m_node->setGeometry(g); - m_node->setMaterial(m_material); - - m_last_particle = 0; - - return m_node; -} - -QSGNode *DeformableParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) -{ - if(m_pleaseReset){ - if(m_node) - delete m_node; - if(m_material) - delete m_material; - - m_node = 0; - m_material = 0; - m_pleaseReset = false; - } - - if(m_system && m_system->isRunning()) - prepareNextFrame(); - if (m_node){ - update(); - m_node->markDirty(QSGNode::DirtyMaterial); - } - - return m_node; -} - -void DeformableParticle::prepareNextFrame() -{ - if (m_node == 0){ //TODO: Staggered loading (as emitted) - m_node = buildParticleNode(); - if(m_node == 0) - return; - } - qint64 timeStamp = m_system->systemSync(this); - - qreal time = timeStamp / 1000.; - m_material->timestamp = time; - -} - - -void DeformableParticle::vertexCopy(DeformableParticleVertex &b,const ParticleVertex& a) -{ - b.x = a.x - m_systemOffset.x(); - b.y = a.y - m_systemOffset.y(); - b.t = a.t; - b.lifeSpan = a.lifeSpan; - b.size = a.size; - b.endSize = a.endSize; - b.sx = a.sx; - b.sy = a.sy; - b.ax = a.ax; - b.ay = a.ay; -} - -void DeformableParticle::reload(ParticleData *d) -{ - if (m_node == 0) - return; - - DeformableParticleVertices *particles = (DeformableParticleVertices *) m_node->geometry()->vertexData(); - - int pos = particleTypeIndex(d); - - DeformableParticleVertices &p = particles[pos]; - - //Perhaps we could be more efficient? - vertexCopy(p.v1, d->pv); - vertexCopy(p.v2, d->pv); - vertexCopy(p.v3, d->pv); - vertexCopy(p.v4, d->pv); - //TODO: Allow for change of deformation data? -} - -void DeformableParticle::load(ParticleData *d) -{ - if (m_node == 0) - return; - - //Deformation Initialization - DeformableParticleVertices *particles = (DeformableParticleVertices *) m_node->geometry()->vertexData(); - DeformableParticleVertices &p = particles[particleTypeIndex(d)]; - if(m_xVector){ - const QPointF &ret = m_xVector->sample(QPointF(d->pv.x, d->pv.y)); - p.v1.xx = p.v2.xx = p.v3.xx = p.v4.xx = ret.x(); - p.v1.xy = p.v2.xy = p.v3.xy = p.v4.xy = ret.y(); - } - if(m_yVector){ - const QPointF &ret = m_yVector->sample(QPointF(d->pv.x, d->pv.y)); - p.v1.yx = p.v2.yx = p.v3.yx = p.v4.yx = ret.x(); - p.v1.yy = p.v2.yy = p.v3.yy = p.v4.yy = ret.y(); - } - p.v1.rotation = p.v2.rotation = p.v3.rotation = p.v4.rotation = - (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV; - p.v1.rotationSpeed = p.v2.rotationSpeed = p.v3.rotationSpeed = p.v4.rotationSpeed = - (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV; - p.v1.autoRotate = p.v2.autoRotate = p.v3.autoRotate = p.v4.autoRotate = m_autoRotation?1.0:0.0; - - vertexCopy(p.v1, d->pv); - vertexCopy(p.v2, d->pv); - vertexCopy(p.v3, d->pv); - vertexCopy(p.v4, d->pv); -} - -QT_END_NAMESPACE diff --git a/src/imports/particles/deformableparticle.h b/src/imports/particles/deformableparticle.h deleted file mode 100644 index 0de6d8228a..0000000000 --- a/src/imports/particles/deformableparticle.h +++ /dev/null @@ -1,235 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef DEFORMABLEPARTICLE_H -#define DEFORMABLEPARTICLE_H -#include "particle.h" -#include "varyingvector.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class DeformableParticleMaterial; -class QSGGeometryNode; -struct DeformableParticleVertex; - -class DeformableParticle : public ParticleType -{ - Q_OBJECT - //Note that the particle centering can be less accurate with this one - Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged) - - Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) - Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged) - Q_PROPERTY(qreal rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged) - Q_PROPERTY(qreal rotationSpeedVariation READ rotationSpeedVariation WRITE setRotationSpeedVariation NOTIFY rotationSpeedVariationChanged) - //If true, then will face the direction of motion. Stacks with rotation, e.g. setting rotation - //to 180 will lead to facing away from the direction of motion - Q_PROPERTY(bool autoRotation READ autoRotation WRITE autoRotation NOTIFY autoRotationChanged) - - //###Call i/j? Makes more sense to those with vector calculus experience, and I could even add the cirumflex in QML? - //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size - Q_PROPERTY(VaryingVector* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged) - //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram. - Q_PROPERTY(VaryingVector* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged) - - //Do we want to add the tables? - //Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged) - //Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged) - //Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged) - - //Does it need alpha? For convenience only, as images probably don't have it - //Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged) - //Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged) - -public: - explicit DeformableParticle(QSGItem *parent = 0); - virtual ~DeformableParticle(){} - - virtual void load(ParticleData*); - virtual void reload(ParticleData*); - virtual void setCount(int c); - - QUrl image() const { return m_image; } - void setImage(const QUrl &image); - - qreal rotation() const - { - return m_rotation; - } - - bool autoRotation() const - { - return m_autoRotation; - } - - VaryingVector* xVector() const - { - return m_xVector; - } - - VaryingVector* yVector() const - { - return m_yVector; - } - - qreal rotationVariation() const - { - return m_rotationVariation; - } - - qreal rotationSpeed() const - { - return m_rotationSpeed; - } - - qreal rotationSpeedVariation() const - { - return m_rotationSpeedVariation; - } - -signals: - - void imageChanged(); - void rotationChanged(qreal arg); - - void autoRotationChanged(bool arg); - - void xVectorChanged(VaryingVector* arg); - - void yVectorChanged(VaryingVector* arg); - - void rotationVariationChanged(qreal arg); - - void rotationSpeedChanged(qreal arg); - - void rotationSpeedVariationChanged(qreal arg); - -public slots: -void setRotation(qreal arg) -{ - if (m_rotation != arg) { - m_rotation = arg; - emit rotationChanged(arg); - } -} - -void autoRotation(bool arg) -{ - if (m_autoRotation != arg) { - m_autoRotation = arg; - emit autoRotationChanged(arg); - } -} - -void setXVector(VaryingVector* arg) -{ - if (m_xVector != arg) { - m_xVector = arg; - emit xVectorChanged(arg); - } -} - -void setYVector(VaryingVector* arg) -{ - if (m_yVector != arg) { - m_yVector = arg; - emit yVectorChanged(arg); - } -} - -void setRotationVariation(qreal arg) -{ - if (m_rotationVariation != arg) { - m_rotationVariation = arg; - emit rotationVariationChanged(arg); - } -} - -void setRotationSpeed(qreal arg) -{ - if (m_rotationSpeed != arg) { - m_rotationSpeed = arg; - emit rotationSpeedChanged(arg); - } -} - -void setRotationSpeedVariation(qreal arg) -{ - if (m_rotationSpeedVariation != arg) { - m_rotationSpeedVariation = arg; - emit rotationSpeedVariationChanged(arg); - } -} - -protected: - QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - void reset(); - void prepareNextFrame(); - QSGGeometryNode* buildParticleNode(); -private: - void vertexCopy(DeformableParticleVertex &b,const ParticleVertex& a); - bool m_do_reset; - - QUrl m_image; - QSGGeometryNode *m_node; - DeformableParticleMaterial *m_material; - - // derived values... - int m_last_particle; - - qreal m_render_opacity; - // generated vars - qreal m_rotation; - bool m_autoRotation; - VaryingVector* m_xVector; - VaryingVector* m_yVector; - qreal m_rotationVariation; - qreal m_rotationSpeed; - qreal m_rotationSpeedVariation; -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // DEFORMABLEPARTICLE_H diff --git a/src/imports/particles/driftaffector.cpp b/src/imports/particles/driftaffector.cpp deleted file mode 100644 index f88e29936a..0000000000 --- a/src/imports/particles/driftaffector.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "driftaffector.h" -#include "particlesystem.h" -QT_BEGIN_NAMESPACE -DriftAffector::DriftAffector(QSGItem *parent) : - ParticleAffector(parent) -{ -} - -DriftAffector::~DriftAffector() -{ -} - -bool DriftAffector::affectParticle(ParticleData *data, qreal dt) -{ - if(!m_xDrift && !m_yDrift) - return false; - qreal dx = (((qreal)qrand() / (qreal)RAND_MAX) - 0.5) * 2 * m_xDrift * dt; - qreal dy = (((qreal)qrand() / (qreal)RAND_MAX) - 0.5) * 2 * m_yDrift * dt; - if(dx) - data->setInstantaneousSX(data->curSX() + dx); - if(dy) - data->setInstantaneousSY(data->curSY() + dy); - - return true; -} -QT_END_NAMESPACE diff --git a/src/imports/particles/driftaffector.h b/src/imports/particles/driftaffector.h deleted file mode 100644 index 91ef0fbd34..0000000000 --- a/src/imports/particles/driftaffector.h +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef DRIFTAFFECTOR_H -#define DRIFTAFFECTOR_H -#include "particleaffector.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - - -class DriftAffector : public ParticleAffector -{ - Q_OBJECT - Q_PROPERTY(qreal xDrift READ xDrift WRITE setXDrift NOTIFY xDriftChanged) - Q_PROPERTY(qreal yDrift READ yDrift WRITE setYDrift NOTIFY yDriftChanged) -public: - explicit DriftAffector(QSGItem *parent = 0); - ~DriftAffector(); - qreal yDrift() const - { - return m_yDrift; - } - - qreal xDrift() const - { - return m_xDrift; - } -protected: - virtual bool affectParticle(ParticleData *d, qreal dt); - -signals: - - void yDriftChanged(qreal arg); - - void xDriftChanged(qreal arg); - -public slots: - -void setYDrift(qreal arg) -{ - if (m_yDrift != arg) { - m_yDrift = arg; - emit yDriftChanged(arg); - } -} - -void setXDrift(qreal arg) -{ - if (m_xDrift != arg) { - m_xDrift = arg; - emit xDriftChanged(arg); - } -} - -private: - qreal m_yDrift; - qreal m_xDrift; -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // DRIFTAFFECTOR_H diff --git a/src/imports/particles/eternalaffector.cpp b/src/imports/particles/eternalaffector.cpp deleted file mode 100644 index c946709170..0000000000 --- a/src/imports/particles/eternalaffector.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "eternalaffector.h" -#include - -QT_BEGIN_NAMESPACE - -EternalAffector::EternalAffector(QSGItem *parent) : - ParticleAffector(parent) -{ -} - -bool EternalAffector::affectParticle(ParticleData *d, qreal dt) -{ - qreal target = (m_system->m_timeInt - m_targetLife)/1000.0; - if(d->pv.t < target) - d->pv.t = target; - return true; -} - -QT_END_NAMESPACE diff --git a/src/imports/particles/gravitationalsingularityaffector.cpp b/src/imports/particles/gravitationalsingularityaffector.cpp deleted file mode 100644 index 4dd7d94b7b..0000000000 --- a/src/imports/particles/gravitationalsingularityaffector.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "gravitationalsingularityaffector.h" -#include -#include -QT_BEGIN_NAMESPACE -GravitationalSingularityAffector::GravitationalSingularityAffector(QSGItem *parent) : - ParticleAffector(parent), m_strength(0.0), m_x(0), m_y(0) -{ -} - -const qreal LIMIT = 200; -qreal limit(qreal val){ - if(qAbs(val) > LIMIT){ - return val < 0 ? LIMIT * -1 : LIMIT; - }else{ - return val; - } -} - -bool GravitationalSingularityAffector::affectParticle(ParticleData *d, qreal dt) -{ - if(!m_strength) - return false; - qreal dx = m_x - d->curX(); - qreal dy = m_y - d->curY(); - qreal r = sqrt((dx*dx) + (dy*dy)); - if(r < 0.1 ){//Simulated event horizion - It's right on top of it, and will never escape again. just stick it here. - d->pv.ax = 0; - d->pv.ay = 0; - d->pv.sx = 0; - d->pv.sy = 0; - d->pv.x = m_x; - d->pv.y = m_y; - return true; - }else if(r < 50.0){//Too close, typical dt values are far too coarse for simulation. This may kill perf though - int parts = floor(100.0/r); - ParticleData* f = new ParticleData;//Fake, where it's all in real time for convenience - f->pv.x = d->curX(); - f->pv.y = d->curY(); - f->pv.sx = limit(d->curSX()); - f->pv.sy = limit(d->curSY()); - f->pv.ax = d->pv.ax; - f->pv.ay = d->pv.ay; - subaffect(f, dt/parts, true); - for(int i=1; im_timeInt/1000.) - d->pv.t; - qreal sy = limit(f->pv.sy) - t*f->pv.ay; - qreal y = f->pv.y - t*sy - 0.5 * t*t*f->pv.ay; - qreal sx = limit(f->pv.sx) - t*f->pv.ax; - qreal x = f->pv.x - t*sx - 0.5 * t*t*f->pv.ax; - - d->pv.ay = f->pv.ay; - d->pv.sy = sy; - d->pv.y = y; - d->pv.ax = f->pv.ax; - d->pv.sx = sx; - d->pv.x = x; - return true; - } - qreal theta = atan2(dy,dx); - qreal ds = (m_strength / (r*r)) * dt; - dx = ds * cos(theta); - dy = ds * sin(theta); - d->setInstantaneousSX(limit(d->pv.sx + dx)); - d->setInstantaneousSY(limit(d->pv.sy + dy)); - return true; -} - -const qreal EPSILON = 0.1; -bool fuzzyCompare(qreal a, qreal b) -{ - //Not using qFuzzyCompare because I want control of epsilon - return (a >= b - EPSILON && a <= b + EPSILON); -} - -bool fuzzyLess(qreal a, qreal b) -{ - //Not using qFuzzyCompare because I want control of epsilon - return a <= b + EPSILON; -} - -bool fuzzyMore(qreal a, qreal b) -{ - //Not using qFuzzyCompare because I want control of epsilon - return a >= b - EPSILON; -} - -bool lineIntersect(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3) -{ - if(x3 < qMin(x1,x2) || x3 > qMax(x1,x2) || y3 < qMin(y1,y2) || y3 > qMax(y1,y2)) - return false; - qreal m,c; - m = (y2-y1) / (x2-x1); - c = y1 - m*x1; - return (fuzzyCompare(y3, m*x3 + c)); -} - -void GravitationalSingularityAffector::subaffect(ParticleData *d, qreal dt, bool first) -{ - if(!first){ - qreal nextX = d->pv.x + d->pv.sx * dt + d->pv.ax * dt * dt * 0.5; - qreal nextY = d->pv.y + d->pv.sy * dt + d->pv.ay * dt * dt * 0.5; - if(lineIntersect(d->pv.x, d->pv.y, nextX, nextY, m_x, m_y)){ - d->pv.ax = 0; - d->pv.ay = 0; - d->pv.sx = 0; - d->pv.sy = 0; - d->pv.x = m_x; - d->pv.y = m_y; - return; - //Passed center - the near infinite forces cancel out -// d->pv.x = m_x + m_x - d->pv.x; -// d->pv.y = m_y + m_y - d->pv.y; -// d->pv.sx *= -1; -// d->pv.sy *= -1; -// return; - } - //Simulate advancing a dt - d->pv.x = nextX; - d->pv.y = nextY; - d->pv.sx += d->pv.ax * dt; - d->pv.sy += d->pv.ay * dt; - } - qreal dx = m_x - d->pv.x; - qreal dy = m_y - d->pv.y; - qreal r = sqrt((dx*dx) + (dy*dy)); - if(!r) - return; - qreal theta = atan2(dy,dx); - qreal ds = (m_strength / (r*r)) * dt; - dx = ds * cos(theta); - dy = ds * sin(theta); - d->pv.sx = d->pv.sx + dx; - d->pv.sy = d->pv.sy + dy; -} -QT_END_NAMESPACE diff --git a/src/imports/particles/gravitationalsingularityaffector.h b/src/imports/particles/gravitationalsingularityaffector.h deleted file mode 100644 index 7ac5e93e0e..0000000000 --- a/src/imports/particles/gravitationalsingularityaffector.h +++ /dev/null @@ -1,121 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef GRAVITATIONALSINGULARITYAFFECTOR_H -#define GRAVITATIONALSINGULARITYAFFECTOR_H -#include "particleaffector.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - - -class GravitationalSingularityAffector : public ParticleAffector -{ - Q_OBJECT - Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged) - Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) - Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) -public: - explicit GravitationalSingularityAffector(QSGItem *parent = 0); - - qreal strength() const - { - return m_strength; - } - - qreal x() const - { - return m_x; - } - - qreal y() const - { - return m_y; - } -protected: - virtual bool affectParticle(ParticleData *d, qreal dt); - void subaffect(ParticleData *d, qreal dt, bool first); -signals: - - void strengthChanged(qreal arg); - - void xChanged(qreal arg); - - void yChanged(qreal arg); - -public slots: - -void setStrength(qreal arg) -{ - if (m_strength != arg) { - m_strength = arg; - emit strengthChanged(arg); - } -} - -void setX(qreal arg) -{ - if (m_x != arg) { - m_x = arg; - emit xChanged(arg); - } -} - -void setY(qreal arg) -{ - if (m_y != arg) { - m_y = arg; - emit yChanged(arg); - } -} - -private: -qreal m_strength; -qreal m_x; -qreal m_y; -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // GRAVITATIONALSINGULARITYAFFECTOR_H diff --git a/src/imports/particles/main.cpp b/src/imports/particles/main.cpp deleted file mode 100644 index 072025d6bc..0000000000 --- a/src/imports/particles/main.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "V1/qdeclarativeparticles_p.h" -#include "pluginmain.h" -#include "spritestate.h" -#include "spriteengine.h" -#include "particleaffector.h" -#include "wanderaffector.h" -//#include "rockingaffector.h" -//#include "scalingaffector.h" -#include "resetaffector.h" -#include "gravityaffector.h" -#include "driftaffector.h" -#include "gravitationalsingularityaffector.h" -#include "frictionaffector.h" -#include "meanderaffector.h" -#include "attractoraffector.h" -#include "speedlimitaffector.h" -#include "killaffector.h" -//#include "zoneaffector.h" -//#include "toggleaffector.h" -#include "spritegoalaffector.h" -#include "swarmaffector.h" -#include "turbulenceaffector.h" -#include "eternalaffector.h" -#include "particlesystem.h" -#include "particleemitter.h" -//#include "spriteemitter.h" -#include "trailsemitter.h" -#include "burstemitter.h" -#include "particle.h" -#include "coloredparticle.h" -#include "spriteparticle.h" -//#include "modelparticle.h" -#include "dataparticle.h" -#include "itemparticle.h" -#include "superparticle.h" -#include "ultraparticle.h" -//#include "pairedparticle.h" -#include "spriteimage.h" -#include "followemitter.h" -#include "particleextruder.h" -#include "ellipseextruder.h" -#include "lineextruder.h" -#include "maskextruder.h" -#include "varyingvector.h" -#include "pointvector.h" -#include "angledvector.h" -#include "directedvector.h" -//#include "followaffector.h" -#include "deformableparticle.h" -#include "pictureaffector.h" - -QT_BEGIN_NAMESPACE - -void ParticlesPlugin::registerTypes(const char *uri) -{ - Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.particles")); - - qmlRegisterType(uri, 1, 0, "Particles"); - qmlRegisterType(uri,1,0,"ParticleMotion"); - qmlRegisterType(uri,1,0,"ParticleMotionGravity"); - qmlRegisterType(uri,1,0,"ParticleMotionLinear"); - qmlRegisterType(uri,1,0,"ParticleMotionWander"); - qmlRegisterType(uri, 2, 0, "Sprite"); - qmlRegisterType(uri, 2, 0, "SpriteEngine"); - qmlRegisterType(uri, 2, 0, "SpriteImage"); - - qmlRegisterType(uri, 2, 0, "ParticleSystem"); - - qmlRegisterType(uri, 2, 0, "Particle"); - qmlRegisterType(uri, 2, 0, "ColoredParticle"); - qmlRegisterType(uri, 2, 0, "SpriteParticle"); - //qmlRegisterType(uri, 2, 0, "ModelParticle"); - qmlRegisterType(uri, 2, 0, "DataParticle"); - qmlRegisterType(uri, 2, 0, "ItemParticle"); - //qmlRegisterType(uri, 2, 0, "PairedParticle"); - qmlRegisterType(uri, 2, 0, "DeformableParticle"); - qmlRegisterType(uri, 2, 0, "SuperParticle"); - qmlRegisterType(uri, 2, 0, "UltraParticle"); - - qmlRegisterType(uri, 2, 0, "ParticleEmitter"); - qmlRegisterType(uri, 2, 0, "TrailEmitter"); - qmlRegisterType(uri, 2, 0, "BurstEmitter"); - - qmlRegisterType(uri, 2, 0, "FollowEmitter"); - qmlRegisterType(uri, 2, 0, "Box"); - qmlRegisterType(uri, 2, 0, "Ellipse"); - qmlRegisterType(uri, 2, 0, "Line"); - qmlRegisterType(uri, 2, 0, "Mask"); - - qmlRegisterType(uri, 2, 0, "NullVector"); - qmlRegisterType(uri, 2, 0, "PointVector"); - qmlRegisterType(uri, 2, 0, "AngleVector"); - qmlRegisterType(uri, 2, 0, "DirectedVector"); - - qmlRegisterType(uri, 2, 0, "ParticleAffector"); - qmlRegisterType(uri, 2, 0, "Wander"); - //qmlRegisterType(uri, 2, 0, "Scale"); - //qmlRegisterType(uri, 2, 0, "Rocking"); - qmlRegisterType(uri, 2, 0, "Drift"); - qmlRegisterType(uri, 2, 0, "Friction"); - qmlRegisterType(uri, 2, 0, "GravitationalSingularity"); - qmlRegisterType(uri, 2, 0, "Attractor"); - qmlRegisterType(uri, 2, 0, "Meander"); - qmlRegisterType(uri, 2, 0, "SpeedLimit"); - qmlRegisterType(uri, 2, 0, "Gravity"); - qmlRegisterType(uri, 2, 0, "Stasis"); - qmlRegisterType(uri, 2, 0, "Reset"); - //qmlRegisterType(uri, 2, 0, "Zone"); - //qmlRegisterType(uri, 2, 0, "Toggle"); - qmlRegisterType(uri, 2, 0, "Kill"); - qmlRegisterType(uri, 2, 0, "SpriteGoal"); - qmlRegisterType(uri, 2, 0 , "Swarm"); - qmlRegisterType(uri, 2, 0 , "Turbulence"); - qmlRegisterType(uri, 2, 0, "Picture"); -} - -QT_END_NAMESPACE - -Q_EXPORT_PLUGIN2(Particles, QT_PREPEND_NAMESPACE(ParticlesPlugin)) diff --git a/src/imports/particles/meanderaffector.cpp b/src/imports/particles/meanderaffector.cpp deleted file mode 100644 index 8e03cd07fb..0000000000 --- a/src/imports/particles/meanderaffector.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "meanderaffector.h" - -QT_BEGIN_NAMESPACE - -MeanderAffector::MeanderAffector(QSGItem *parent) : - ParticleAffector(parent) -{ -} - -bool MeanderAffector::affectParticle(ParticleData *data, qreal dt) -{ - if(!m_xDrift && !m_yDrift) - return false; - qreal dx = (((qreal)qrand() / (qreal)RAND_MAX) - 0.5) * 2 * m_xDrift * dt; - qreal dy = (((qreal)qrand() / (qreal)RAND_MAX) - 0.5) * 2 * m_yDrift * dt; - if(dx) - data->setInstantaneousAX(data->pv.ax + dx); - if(dy) - data->setInstantaneousAY(data->pv.ay + dy); - - return true; -} - -QT_END_NAMESPACE diff --git a/src/imports/particles/meanderaffector.h b/src/imports/particles/meanderaffector.h deleted file mode 100644 index 203d20430d..0000000000 --- a/src/imports/particles/meanderaffector.h +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef MEANDERAFFECTOR_H -#define MEANDERAFFECTOR_H -#include "particleaffector.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - - -class MeanderAffector : public ParticleAffector -{ - Q_OBJECT - //Like drift, but affects da/dt instead of ds/dt - Q_PROPERTY(qreal xDrift READ xDrift WRITE setXDrift NOTIFY xDriftChanged) - Q_PROPERTY(qreal yDrift READ yDrift WRITE setYDrift NOTIFY yDriftChanged) -public: - explicit MeanderAffector(QSGItem *parent = 0); - - qreal xDrift() const - { - return m_xDrift; - } - - qreal yDrift() const - { - return m_yDrift; - } -protected: - virtual bool affectParticle(ParticleData *d, qreal dt); -signals: - - void xDriftChanged(qreal arg); - - void yDriftChanged(qreal arg); - -public slots: - - void setXDrift(qreal arg) - { - if (m_xDrift != arg) { - m_xDrift = arg; - emit xDriftChanged(arg); - } - } - void setYDrift(qreal arg) - { - if (m_yDrift != arg) { - m_yDrift = arg; - emit yDriftChanged(arg); - } - } - -private: - qreal m_xDrift; - qreal m_yDrift; -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // MEANDERAFFECTOR_H diff --git a/src/imports/particles/eternalaffector.h b/src/imports/particles/particles.cpp similarity index 59% rename from src/imports/particles/eternalaffector.h rename to src/imports/particles/particles.cpp index 834106b53d..b30beb5c74 100644 --- a/src/imports/particles/eternalaffector.h +++ b/src/imports/particles/particles.cpp @@ -1,10 +1,10 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the Declarative module of the Qt Toolkit. +** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,50 +39,31 @@ ** ****************************************************************************/ -#ifndef ETERNALAFFECTOR_H -#define ETERNALAFFECTOR_H -#include "particleaffector.h" +#include +#include -QT_BEGIN_HEADER +#include "V1/qdeclarativeparticles_p.h" QT_BEGIN_NAMESPACE -QT_MODULE(Declarative) - -class EternalAffector : public ParticleAffector +class QParticlesQmlModule : public QDeclarativeExtensionPlugin { Q_OBJECT - Q_PROPERTY(int targetLife READ targetLife WRITE setTargetLife NOTIFY targetLifeChanged) - public: - explicit EternalAffector(QSGItem *parent = 0); - int targetLife() const + virtual void registerTypes(const char *uri) { - return m_targetLife; + Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.particles")); + qmlRegisterType(uri,1,0,"ParticleMotion"); + qmlRegisterType(uri,1,0,"ParticleMotionGravity"); + qmlRegisterType(uri,1,0,"ParticleMotionLinear"); + qmlRegisterType(uri,1,0,"ParticleMotionWander"); + qmlRegisterType(uri,1,0,"Particles"); } - -protected: - virtual bool affectParticle(ParticleData *d, qreal dt); - -signals: - - void targetLifeChanged(int arg); - -public slots: - - void setTargetLife(int arg) - { - if (m_targetLife != arg) { - m_targetLife = arg; - emit targetLifeChanged(arg); - } - } -private: - int m_targetLife; }; QT_END_NAMESPACE -QT_END_HEADER +#include "particles.moc" + +Q_EXPORT_PLUGIN2(qmlparticlesplugin, QT_PREPEND_NAMESPACE(QParticlesQmlModule)); -#endif // ETERNALAFFECTOR_H diff --git a/src/imports/particles/particles.pro b/src/imports/particles/particles.pro index a6930fe95c..56f663f328 100644 --- a/src/imports/particles/particles.pro +++ b/src/imports/particles/particles.pro @@ -3,118 +3,17 @@ TARGETPATH = Qt/labs/particles include(../qimportbase.pri) HEADERS += \ - V1/qdeclarativeparticles_p.h \ - spritestate.h \ - pluginmain.h \ - particleaffector.h \ - wanderaffector.h \ - #rockingaffector.h \ - #scalingaffector.h \ - driftaffector.h \ - particleemitter.h \ - particlesystem.h \ - trailsemitter.h \ - #spriteemitter.h \ - particle.h \ - coloredparticle.h \ - spriteparticle.h \ - spritegoalaffector.h \ - #zoneaffector.h \ - frictionaffector.h \ - gravitationalsingularityaffector.h \ - killaffector.h \ - speedlimitaffector.h \ - spriteengine.h \ - gravityaffector.h \ - attractoraffector.h \ - meanderaffector.h \ - #toggleaffector.h \ - spriteimage.h \ - #pairedparticle.h \ - followemitter.h \ - swarmaffector.h \ - turbulenceaffector.h \ - particleextruder.h \ - ellipseextruder.h \ - maskextruder.h \ - varyingvector.h \ - pointvector.h \ - angledvector.h \ - directedvector.h \ - #modelparticle.h \ - eternalaffector.h \ - lineextruder.h \ - resetaffector.h \ - deformableparticle.h \ - pictureaffector.h \ - superparticle.h \ - ultraparticle.h \ - burstemitter.h \ - dataparticle.h \ - itemparticle.h + V1/qdeclarativeparticles_p.h SOURCES += \ - V1/qdeclarativeparticles.cpp \ - spritestate.cpp \ - main.cpp \ - particleaffector.cpp \ - wanderaffector.cpp \ - #rockingaffector.cpp \ - #scalingaffector.cpp \ - driftaffector.cpp \ - particleemitter.cpp \ - particlesystem.cpp \ - trailsemitter.cpp \ - #spriteemitter.cpp \ - particle.cpp \ - coloredparticle.cpp \ - spriteparticle.cpp \ - spritegoalaffector.cpp \ - #zoneaffector.cpp \ - frictionaffector.cpp \ - gravitationalsingularityaffector.cpp \ - killaffector.cpp \ - speedlimitaffector.cpp \ - spriteengine.cpp \ - gravityaffector.cpp \ - attractoraffector.cpp \ - meanderaffector.cpp \ - #toggleaffector.cpp \ - spriteimage.cpp \ - #pairedparticle.cpp \ - followemitter.cpp \ - swarmaffector.cpp \ - turbulenceaffector.cpp \ - particleextruder.cpp \ - ellipseextruder.cpp \ - maskextruder.cpp \ - varyingvector.cpp \ - pointvector.cpp \ - angledvector.cpp \ - directedvector.cpp \ - #modelparticle.cpp \ - eternalaffector.cpp \ - lineextruder.cpp \ - resetaffector.cpp \ - deformableparticle.cpp \ - pictureaffector.cpp \ - superparticle.cpp \ - ultraparticle.cpp \ - burstemitter.cpp \ - dataparticle.cpp \ - itemparticle.cpp - -QT += declarative opengl -#Because we use QDeclarativePixmapCache once... -QT += core-private gui-private declarative-private script-private + particles.cpp \ + V1/qdeclarativeparticles.cpp +QT += declarative opengl core gui declarative-private core-private gui-private OTHER_FILES += \ qmldir -RESOURCES += \ - spriteparticles.qrc - DESTDIR = $$QT.declarative.imports/$$TARGETPATH target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH diff --git a/src/imports/particles/pictureaffector.cpp b/src/imports/particles/pictureaffector.cpp deleted file mode 100644 index 636e26b830..0000000000 --- a/src/imports/particles/pictureaffector.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "pictureaffector.h" -#include "coloredparticle.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -PictureAffector::PictureAffector(QSGItem *parent) : - ParticleAffector(parent), m_pix(0) -{ - m_needsReset = true; -} - -void PictureAffector::reset(int systemIdx) -{ - ParticleAffector::reset(systemIdx); -} - -void PictureAffector::startLoadImage() -{ - if(m_pix) - m_pix->clear(); - else - m_pix = new QDeclarativePixmap(); - m_pix->load(qmlEngine(this), m_image, QDeclarativePixmap::Cache); - if(m_pix->isReady()) - loadImage(); - else - m_pix->connectFinished(this, SLOT(loadImage())); -} -void PictureAffector::loadImage() -{ - m_loadedImage = m_pix->pixmap().toImage(); - if(m_loadedImage.isNull()) - qWarning() << "PictureAffector could not load picture " << m_image; -} - -bool PictureAffector::affectParticle(ParticleData *d, qreal dt) -{ - Q_UNUSED(dt); - if(!width() || !height()){ - qWarning() << "PictureAffector needs a size"; - return false; - } - - if(m_loadedImage.isNull()) - return false; - - if(m_loadedImage.size()!=QSize(width(), height())) - m_loadedImage = m_loadedImage.scaled(width(), height());//TODO: Aspect Ratio Control? - - bool affected = false; - QPoint pos = QPoint(d->curX() - m_offset.x(), d->curY() - m_offset.y()); - if(!QRect(0,0,width(),height()).contains(pos)){ - //XXX: Just a debugging helper, as I don't think it can get here. - qWarning() << "PictureAffector gives up."; - return false; - } - Color4ub c; - QRgb col = m_loadedImage.pixel(pos); - c.a = qAlpha(col); - c.b = qBlue(col); - c.g = qGreen(col); - c.r = qRed(col); - foreach(ParticleType *p, m_system->m_groupData[d->group]->types){ - if(qobject_cast(p)){ - ColoredParticle* cp = qobject_cast(p); - cp->reloadColor(c, d); - affected = true; - } - } - - return affected;//Doesn't affect particle data, but necessary for onceOff -} - -QT_END_NAMESPACE diff --git a/src/imports/particles/pictureaffector.h b/src/imports/particles/pictureaffector.h deleted file mode 100644 index 4e0141d00a..0000000000 --- a/src/imports/particles/pictureaffector.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PICTUREAFFECTOR_H -#define PICTUREAFFECTOR_H -#include "particleaffector.h" -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QDeclarativePixmap; -class PictureAffector : public ParticleAffector -{ - Q_OBJECT - //Usually want to use "particles" to target just colored stuff, and save performance - //Use onceOff (inherited) to determine if this is an emitter modification or a more constant enforcer - Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged) - //TODO: Bool smooth, where it interpolates -public: - explicit PictureAffector(QSGItem *parent = 0); - - QUrl image() const - { - return m_image; - } - -protected: - virtual void reset(int systemIdx); - virtual bool affectParticle(ParticleData *d, qreal dt); -signals: - - void imageChanged(QUrl arg); - -public slots: - void setImage(QUrl arg) - { - if (m_image != arg) { - m_image = arg; - startLoadImage(); - emit imageChanged(arg); - } - } - -private slots: - void loadImage(); -private: - void startLoadImage(); - QUrl m_image; - QDeclarativePixmap* m_pix; - QImage m_loadedImage; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // PICTUREAFFECTOR_H diff --git a/src/imports/particles/pluginmain.h b/src/imports/particles/pluginmain.h deleted file mode 100644 index cd8760d1a0..0000000000 --- a/src/imports/particles/pluginmain.h +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PLUGINMAIN_H -#define PLUGINMAIN_H - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class ParticlesPlugin : public QDeclarativeExtensionPlugin -{ - Q_OBJECT -public: - virtual void registerTypes(const char *uri); -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // PLUGINMAIN_H diff --git a/src/imports/particles/resetaffector.cpp b/src/imports/particles/resetaffector.cpp deleted file mode 100644 index 0598298f27..0000000000 --- a/src/imports/particles/resetaffector.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "resetaffector.h" -#include -QT_BEGIN_NAMESPACE -ResetAffector::ResetAffector(QSGItem *parent) : - ParticleAffector(parent) -{ -} - -void ResetAffector::reset(int idx) -{ - ParticleAffector::reset(idx); - if(m_data[idx]) - delete m_data[idx]; - m_data.insert(idx, 0);//TODO: Either load with data now, or get data next tick whether active or not -} - -bool ResetAffector::affectParticle(ParticleData *d, qreal dt) -{ - TrajectoryData* trajectory; - if(m_data[d->systemIndex]){ - trajectory = m_data[d->systemIndex]; - //TODO: Faster to calculate once (not 4 times) - d->setInstantaneousSX(trajectory->sx); - d->setInstantaneousSY(trajectory->sy); - d->setInstantaneousAX(trajectory->ax); - d->setInstantaneousAY(trajectory->ay); - }else{ - trajectory = new TrajectoryData; - } - trajectory->sx = d->pv.sx; - trajectory->sy = d->pv.sy; - trajectory->ax = d->pv.ax; - trajectory->ay = d->pv.ay; - m_data.insert(d->systemIndex, trajectory);//overwrites - return true; -} -QT_END_NAMESPACE diff --git a/src/imports/particles/resetaffector.h b/src/imports/particles/resetaffector.h deleted file mode 100644 index 6a4e2b7983..0000000000 --- a/src/imports/particles/resetaffector.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef RESETAFFECTOR_H -#define RESETAFFECTOR_H -#include "particleaffector.h" -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -struct TrajectoryData{ - qreal sx,sy,ax,ay; -}; - -class ResetAffector : public ParticleAffector -{ - Q_OBJECT -public: - explicit ResetAffector(QSGItem *parent = 0); - virtual void reset(int systemIdx); - -signals: - -public slots: -protected: - virtual bool affectParticle(ParticleData *d, qreal dt); -private: - QHash m_data; -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // RESETAFFECTOR_H diff --git a/src/imports/particles/speedlimitaffector.cpp b/src/imports/particles/speedlimitaffector.cpp deleted file mode 100644 index c226404b01..0000000000 --- a/src/imports/particles/speedlimitaffector.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "speedlimitaffector.h" -#include -#include - -QT_BEGIN_NAMESPACE - -SpeedLimitAffector::SpeedLimitAffector(QSGItem *parent) : - ParticleAffector(parent), m_speedLimit(-1) -{ -} - -bool SpeedLimitAffector::affectParticle(ParticleData *d, qreal dt){ - Q_UNUSED(dt); - if(m_speedLimit <= 0) - return false; - - qreal x = d->curSX(); - qreal y = d->curSY(); - qreal s = sqrt(x*x + y*y); - if(s <= m_speedLimit) - return false; - - - if(s >= m_speedLimit*1.01){ - qreal theta = atan2(y,x); - d->setInstantaneousSX(m_speedLimit * cos(theta)); - d->setInstantaneousSY(m_speedLimit * sin(theta)); - } - - d->setInstantaneousAY(0); - d->setInstantaneousAX(0); - - return true; -} - -QT_END_NAMESPACE diff --git a/src/imports/particles/speedlimitaffector.h b/src/imports/particles/speedlimitaffector.h deleted file mode 100644 index b3858a2a9d..0000000000 --- a/src/imports/particles/speedlimitaffector.h +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SPEEDLIMITAFFECTOR_H -#define SPEEDLIMITAFFECTOR_H -#include "particleaffector.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class SpeedLimitAffector : public ParticleAffector -{ - Q_OBJECT - Q_PROPERTY(qreal speedLimit READ speedLimit WRITE setSpeedLimit NOTIFY speedLimitChanged) - - -public: - explicit SpeedLimitAffector(QSGItem *parent = 0); - - qreal speedLimit() const - { - return m_speedLimit; - } - -protected: - virtual bool affectParticle(ParticleData *d, qreal dt); -signals: - - void speedLimitChanged(qreal arg); - -public slots: -void setSpeedLimit(qreal arg) -{ - if (m_speedLimit != arg) { - m_speedLimit = arg; - emit speedLimitChanged(arg); - } -} - -private: -qreal m_speedLimit; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // SPEEDLIMITAFFECTOR_H diff --git a/src/imports/particles/spriteparticle.cpp b/src/imports/particles/spriteparticle.cpp deleted file mode 100644 index 6039d2819b..0000000000 --- a/src/imports/particles/spriteparticle.cpp +++ /dev/null @@ -1,449 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "spriteparticle.h" -#include "spritestate.h" -#include "spriteengine.h" -#include "particleemitter.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class SpriteParticlesMaterial : public QSGMaterial -{ -public: - SpriteParticlesMaterial(); - virtual ~SpriteParticlesMaterial(); - virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } - virtual QSGMaterialShader *createShader() const; - virtual int compare(const QSGMaterial *other) const - { - return this - static_cast(other); - } - - QSGTexture *texture; - - qreal timestamp; - int framecount; - int animcount; -}; - -SpriteParticlesMaterial::SpriteParticlesMaterial() - : timestamp(0) - , framecount(1) - , animcount(1) -{ - setFlag(Blending, true); -} - -SpriteParticlesMaterial::~SpriteParticlesMaterial() -{ - delete texture; -} - -class SpriteParticlesMaterialData : public QSGMaterialShader -{ -public: - SpriteParticlesMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) - { - QFile vf(vertexFile ? vertexFile : ":resources/spritevertex.shader"); - vf.open(QFile::ReadOnly); - m_vertex_code = vf.readAll(); - - QFile ff(fragmentFile ? fragmentFile : ":resources/spritefragment.shader"); - ff.open(QFile::ReadOnly); - m_fragment_code = ff.readAll(); - - Q_ASSERT(!m_vertex_code.isNull()); - Q_ASSERT(!m_fragment_code.isNull()); - } - - void deactivate() { - QSGMaterialShader::deactivate(); - - for (int i=0; i<8; ++i) { - program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); - } - } - - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) - { - SpriteParticlesMaterial *m = static_cast(newEffect); - m->texture->bind(); - - program()->setUniformValue(m_opacity_id, state.opacity()); - program()->setUniformValue(m_timestamp_id, (float) m->timestamp); - program()->setUniformValue(m_framecount_id, (float) m->framecount); - program()->setUniformValue(m_animcount_id, (float) m->animcount); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrix_id, state.combinedMatrix()); - } - - virtual void initialize() { - m_matrix_id = program()->uniformLocation("matrix"); - m_opacity_id = program()->uniformLocation("opacity"); - m_timestamp_id = program()->uniformLocation("timestamp"); - m_framecount_id = program()->uniformLocation("framecount"); - m_animcount_id = program()->uniformLocation("animcount"); - } - - virtual const char *vertexShader() const { return m_vertex_code.constData(); } - virtual const char *fragmentShader() const { return m_fragment_code.constData(); } - - virtual char const *const *attributeNames() const { - static const char *attr[] = { - "vPos", - "vTex", - "vData", - "vVec", - "vAnimData", - 0 - }; - return attr; - } - - virtual bool isColorTable() const { return false; } - - int m_matrix_id; - int m_opacity_id; - int m_timestamp_id; - int m_framecount_id; - int m_animcount_id; - - QByteArray m_vertex_code; - QByteArray m_fragment_code; - - static float chunkOfBytes[1024]; -}; -float SpriteParticlesMaterialData::chunkOfBytes[1024]; - -QSGMaterialShader *SpriteParticlesMaterial::createShader() const -{ - return new SpriteParticlesMaterialData; -} - -struct SpriteParticleVertex { - float x; - float y; - float tx; - float ty; - float t; - float lifeSpan; - float size; - float endSize; - float sx; - float sy; - float ax; - float ay; - float animIdx; - float frameDuration; - float frameCount; - float animT; -}; - -struct SpriteParticleVertices { - SpriteParticleVertex v1; - SpriteParticleVertex v2; - SpriteParticleVertex v3; - SpriteParticleVertex v4; -}; - -SpriteParticle::SpriteParticle(QSGItem *parent) : - ParticleType(parent) - , m_node(0) - , m_material(0) - , m_spriteEngine(0) -{ - setFlag(ItemHasContents); - } -QDeclarativeListProperty SpriteParticle::sprites() -{ - return QDeclarativeListProperty(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); -} - -void SpriteParticle::createEngine() -{ - if(m_spriteEngine) - delete m_spriteEngine; - if(m_sprites.count()) - m_spriteEngine = new SpriteEngine(m_sprites, this); - else - m_spriteEngine = 0; - reset();//###this is probably out of updatePaintNode and shouldn't be -} - -void SpriteParticle::setCount(int c) -{ - ParticleType::setCount(c); - m_pleaseReset = true; -} - -static QSGGeometry::Attribute SpriteParticle_Attributes[] = { - { 0, 2, GL_FLOAT }, // Position - { 1, 2, GL_FLOAT }, // TexCoord - { 2, 4, GL_FLOAT }, // Data - { 3, 4, GL_FLOAT }, // Vectors - { 4, 4, GL_FLOAT } // Colors -}; - -static QSGGeometry::AttributeSet SpriteParticle_AttributeSet = -{ - 5, // Attribute Count - (2 + 2 + 4 + 4 + 4) * sizeof(float), - SpriteParticle_Attributes -}; - - - -QSGGeometryNode* SpriteParticle::buildParticleNode() -{ - if (m_count * 4 > 0xffff) { - qWarning() << "SpriteParticle: too many particles..."; - return 0; - } - - if (m_count * 4 == 0) { - qWarning() << "SpriteParticle: No particles..."; - return 0; - } - - if (!m_spriteEngine) { - qWarning() << "SpriteParticle: No sprite engine..."; - return 0; - } - - if (m_material) { - delete m_material; - m_material = 0; - } - - m_material = new SpriteParticlesMaterial(); - - QImage image = m_spriteEngine->assembledImage(); - if(image.isNull()) - return 0; - m_material->texture = sceneGraphEngine()->createTextureFromImage(image); - m_material->texture->setFiltering(QSGTexture::Linear); - m_material->framecount = m_spriteEngine->maxFrames(); - m_spriteEngine->setCount(m_count); - - int vCount = m_count * 4; - int iCount = m_count * 6; - QSGGeometry *g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount); - g->setDrawingMode(GL_TRIANGLES); - - SpriteParticleVertex *vertices = (SpriteParticleVertex *) g->vertexData(); - for (int p=0; pindexDataAsUShort(); - for (int i=0; isetGeometry(g); - m_node->setMaterial(m_material); - m_last_particle = 0; - return m_node; -} - -void SpriteParticle::vertexCopy(SpriteParticleVertex &b,const ParticleVertex& a) -{ - b.x = a.x + m_systemOffset.x(); - b.y = a.y + m_systemOffset.y(); - b.t = a.t; - b.lifeSpan = a.lifeSpan; - b.size = a.size; - b.endSize = a.endSize; - b.sx = a.sx; - b.sy = a.sy; - b.ax = a.ax; - b.ay = a.ay; -} - -void SpriteParticle::load(ParticleData *d) -{ - if (m_node == 0) //error creating node - return; - - SpriteParticleVertices *particles = (SpriteParticleVertices *) m_node->geometry()->vertexData(); - int pos = particleTypeIndex(d); - SpriteParticleVertices &p = particles[pos]; - - // Initial Sprite State - m_spriteEngine->startSprite(pos); - p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = p.v1.t; - p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = 0; - p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(pos); - p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(pos); - - vertexCopy(p.v1, d->pv); - vertexCopy(p.v2, d->pv); - vertexCopy(p.v3, d->pv); - vertexCopy(p.v4, d->pv); - -} - -void SpriteParticle::reload(ParticleData *d) -{ - if (m_node == 0) //error creating node - return; - - SpriteParticleVertices *particles = (SpriteParticleVertices *) m_node->geometry()->vertexData(); - int pos = particleTypeIndex(d); - SpriteParticleVertices &p = particles[pos]; - - vertexCopy(p.v1, d->pv); - vertexCopy(p.v2, d->pv); - vertexCopy(p.v3, d->pv); - vertexCopy(p.v4, d->pv); -} - - -QSGNode *SpriteParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) -{ - if(m_pleaseReset){ - if(m_node) - delete m_node; - if(m_material) - delete m_material; - - m_node = 0; - m_material = 0; - m_pleaseReset = false; - } - if(m_system&& m_system->isRunning()) - prepareNextFrame(); - if (m_node){ - update(); - m_node->markDirty(QSGNode::DirtyMaterial); - } - - return m_node; -} - -void SpriteParticle::prepareNextFrame() -{ - if (m_node == 0){ //TODO: Staggered loading (as emitted) (is it just moving this check to load()?) - m_node = buildParticleNode(); - if(m_node == 0) - return; - } - qint64 timeStamp = m_system->systemSync(this); - - - qreal time = timeStamp / 1000.; - m_material->timestamp = time; - m_material->animcount = m_spriteEngine->spriteCount(); - - //Advance State - SpriteParticleVertices *particles = (SpriteParticleVertices *) m_node->geometry()->vertexData(); - m_spriteEngine->updateSprites(timeStamp); - for(int i=0; ispriteState(i); - if(curIdx != p.v1.animIdx){ - p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = curIdx; - p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(i)/1000.0; - p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(i); - p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(i); - } - } -} - -void SpriteParticle::reset() -{ - ParticleType::reset(); - m_pleaseReset = true; -} - -QT_END_NAMESPACE diff --git a/src/imports/particles/spriteparticle.h b/src/imports/particles/spriteparticle.h deleted file mode 100644 index f28d198600..0000000000 --- a/src/imports/particles/spriteparticle.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SPRITEPARTICLE_H -#define SPRITEPARTICLE_H -#include "particle.h" -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class SpriteState; -class SpriteEngine; -class QSGGeometryNode; -class SpriteParticlesMaterial; -class SpriteParticleVertex; - -class SpriteParticle : public ParticleType -{ - Q_OBJECT - Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) - Q_CLASSINFO("DefaultProperty", "sprites") -public: - explicit SpriteParticle(QSGItem *parent = 0); - virtual void load(ParticleData*); - virtual void reload(ParticleData*); - virtual void setCount(int c); - - QDeclarativeListProperty sprites(); - SpriteEngine* spriteEngine() {return m_spriteEngine;} -signals: - -public slots: -protected: - QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - void reset(); - void prepareNextFrame(); - QSGGeometryNode* buildParticleNode(); -private slots: - void createEngine(); -private: - QSGGeometryNode *m_node; - SpriteParticlesMaterial *m_material; - - int m_particle_duration; - int m_last_particle; - - QList m_sprites; - SpriteEngine* m_spriteEngine; - - void vertexCopy(SpriteParticleVertex &b,const ParticleVertex& a); -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // SPRITEPARTICLE_H diff --git a/src/imports/particles/spriteparticles.qrc b/src/imports/particles/spriteparticles.qrc deleted file mode 100644 index 0232c3c9f9..0000000000 --- a/src/imports/particles/spriteparticles.qrc +++ /dev/null @@ -1,22 +0,0 @@ - - - resources/spritefragment.shader - resources/spritevertex.shader - resources/ctfragment.shader - resources/ctvertex.shader - resources/trailsfragment.shader - resources/trailsvertex.shader - resources/spriteimagefragment.shader - resources/spriteimagevertex.shader - resources/identitytable.png - resources/defaultFadeInOut.png - resources/deformablefragment.shader - resources/deformablevertex.shader - resources/ultravertex.shader - resources/ultrafragment.shader - resources/supervertex.shader - resources/superfragment.shader - resources/simplevertex.shader - resources/simplefragment.shader - - diff --git a/src/imports/particles/superparticle.cpp b/src/imports/particles/superparticle.cpp deleted file mode 100644 index 811b6a4ba8..0000000000 --- a/src/imports/particles/superparticle.cpp +++ /dev/null @@ -1,511 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include "superparticle.h" -#include "particleemitter.h" -#include -#include - -QT_BEGIN_NAMESPACE - -const float CONV = 0.017453292519943295; -class SuperMaterial : public QSGMaterial -{ -public: - SuperMaterial() - : timestamp(0) - { - setFlag(Blending, true); - } - - ~SuperMaterial() - { - delete texture; - delete colortable; - delete sizetable; - delete opacitytable; - } - - virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } - virtual QSGMaterialShader *createShader() const; - virtual int compare(const QSGMaterial *other) const - { - return this - static_cast(other); - } - - QSGTexture *texture; - QSGTexture *colortable; - QSGTexture *sizetable; - QSGTexture *opacitytable; - - qreal timestamp; -}; - - -class SuperMaterialData : public QSGMaterialShader -{ -public: - SuperMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) - { - QFile vf(vertexFile ? vertexFile : ":resources/supervertex.shader"); - vf.open(QFile::ReadOnly); - m_vertex_code = vf.readAll(); - - QFile ff(fragmentFile ? fragmentFile : ":resources/superfragment.shader"); - ff.open(QFile::ReadOnly); - m_fragment_code = ff.readAll(); - - Q_ASSERT(!m_vertex_code.isNull()); - Q_ASSERT(!m_fragment_code.isNull()); - } - - void deactivate() { - QSGMaterialShader::deactivate(); - - for (int i=0; i<8; ++i) { - program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); - } - } - - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) - { - SuperMaterial *m = static_cast(newEffect); - state.context()->functions()->glActiveTexture(GL_TEXTURE0); - m->texture->bind(); - - state.context()->functions()->glActiveTexture(GL_TEXTURE1); - m->colortable->bind(); - program()->setUniformValue(m_colortable_id, 1); - - state.context()->functions()->glActiveTexture(GL_TEXTURE2); - m->sizetable->bind(); - program()->setUniformValue(m_sizetable_id, 2); - - state.context()->functions()->glActiveTexture(GL_TEXTURE3); - m->opacitytable->bind(); - program()->setUniformValue(m_opacitytable_id, 3); - - program()->setUniformValue(m_opacity_id, state.opacity()); - program()->setUniformValue(m_timestamp_id, (float) m->timestamp); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrix_id, state.combinedMatrix()); - } - - virtual void initialize() { - m_colortable_id = program()->uniformLocation("colortable"); - m_sizetable_id = program()->uniformLocation("sizetable"); - m_opacitytable_id = program()->uniformLocation("opacitytable"); - m_matrix_id = program()->uniformLocation("matrix"); - m_opacity_id = program()->uniformLocation("opacity"); - m_timestamp_id = program()->uniformLocation("timestamp"); - } - - virtual const char *vertexShader() const { return m_vertex_code.constData(); } - virtual const char *fragmentShader() const { return m_fragment_code.constData(); } - - virtual char const *const *attributeNames() const { - static const char *attr[] = { - "vPos", - "vTex", - "vData", - "vVec", - "vColor", - "vDeformVec", - "vRotation", - 0 - }; - return attr; - } - - virtual bool isColorTable() const { return false; } - - int m_matrix_id; - int m_opacity_id; - int m_timestamp_id; - int m_colortable_id; - int m_sizetable_id; - int m_opacitytable_id; - - QByteArray m_vertex_code; - QByteArray m_fragment_code; - - static float chunkOfBytes[1024]; -}; -float SuperMaterialData::chunkOfBytes[1024]; - - -QSGMaterialShader *SuperMaterial::createShader() const -{ - return new SuperMaterialData; -} - -SuperParticle::SuperParticle(QSGItem* parent) - : ParticleType(parent) - , m_do_reset(false) - , m_color(Qt::white) - , m_color_variation(0.5) - , m_node(0) - , m_material(0) - , m_alphaVariation(0.0) - , m_alpha(1.0) - , m_redVariation(0.0) - , m_greenVariation(0.0) - , m_blueVariation(0.0) -{ - setFlag(ItemHasContents); -} - -void SuperParticle::setImage(const QUrl &image) -{ - if (image == m_image_name) - return; - m_image_name = image; - emit imageChanged(); - reset(); -} - - -void SuperParticle::setColortable(const QUrl &table) -{ - if (table == m_colortable_name) - return; - m_colortable_name = table; - emit colortableChanged(); - reset(); -} - -void SuperParticle::setSizetable(const QUrl &table) -{ - if (table == m_sizetable_name) - return; - m_sizetable_name = table; - emit sizetableChanged(); - reset(); -} - -void SuperParticle::setOpacitytable(const QUrl &table) -{ - if (table == m_opacitytable_name) - return; - m_opacitytable_name = table; - emit opacitytableChanged(); - reset(); -} - -void SuperParticle::setColor(const QColor &color) -{ - if (color == m_color) - return; - m_color = color; - emit colorChanged(); - //m_system->pleaseReset();//XXX -} - -void SuperParticle::setColorVariation(qreal var) -{ - if (var == m_color_variation) - return; - m_color_variation = var; - emit colorVariationChanged(); - //m_system->pleaseReset();//XXX -} - -void SuperParticle::setCount(int c) -{ - ParticleType::setCount(c); - m_pleaseReset = true; -} - -void SuperParticle::reset() -{ - ParticleType::reset(); - m_pleaseReset = true; -} - -static QSGGeometry::Attribute SuperParticle_Attributes[] = { - { 0, 2, GL_FLOAT }, // Position - { 1, 2, GL_FLOAT }, // TexCoord - { 2, 4, GL_FLOAT }, // Data - { 3, 4, GL_FLOAT }, // Vectors - { 4, 4, GL_UNSIGNED_BYTE }, // Colors - { 5, 4, GL_FLOAT }, // DeformationVectors - { 6, 3, GL_FLOAT } // Rotation -}; - -static QSGGeometry::AttributeSet SuperParticle_AttributeSet = -{ - 7, // Attribute Count - (2 + 2 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar), - SuperParticle_Attributes -}; - -QSGGeometryNode* SuperParticle::buildParticleNode() -{ - if (m_count * 4 > 0xffff) { - printf("SuperParticle: Too many particles... \n"); - return 0; - } - - if(m_count <= 0) { - printf("SuperParticle: Too few particles... \n"); - return 0; - } - - QImage image(m_image_name.toLocalFile()); - if (image.isNull()) { - printf("SuperParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile())); - return 0; - } - - int vCount = m_count * 4; - int iCount = m_count * 6; - - QSGGeometry *g = new QSGGeometry(SuperParticle_AttributeSet, vCount, iCount); - g->setDrawingMode(GL_TRIANGLES); - - SuperVertex *vertices = (SuperVertex *) g->vertexData(); - for (int p=0; pindexDataAsUShort(); - for (int i=0; icolortable = sceneGraphEngine()->createTextureFromImage(colortable); - m_material->sizetable = sceneGraphEngine()->createTextureFromImage(sizetable); - m_material->opacitytable = sceneGraphEngine()->createTextureFromImage(opacitytable); - - m_material->texture = sceneGraphEngine()->createTextureFromImage(image); - m_material->texture->setFiltering(QSGTexture::Linear); - - m_node = new QSGGeometryNode(); - m_node->setGeometry(g); - m_node->setMaterial(m_material); - - m_last_particle = 0; - - return m_node; -} - -QSGNode *SuperParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) -{ - if(m_pleaseReset){ - if(m_node) - delete m_node; - if(m_material) - delete m_material; - - m_node = 0; - m_material = 0; - m_pleaseReset = false; - } - - if(m_system && m_system->isRunning()) - prepareNextFrame(); - if (m_node){ - update(); - m_node->markDirty(QSGNode::DirtyMaterial); - } - - return m_node; -} - -void SuperParticle::prepareNextFrame() -{ - if (m_node == 0){ //TODO: Staggered loading (as emitted) - m_node = buildParticleNode(); - if(m_node == 0) - return; - } - qint64 timeStamp = m_system->systemSync(this); - - qreal time = timeStamp / 1000.; - m_material->timestamp = time; -} - -void SuperParticle::reloadColor(const Color4ub &c, ParticleData* d) -{ - SuperVertices *particles = (SuperVertices *) m_node->geometry()->vertexData(); - int pos = particleTypeIndex(d); - SuperVertices &p = particles[pos]; - p.v1.color = p.v2.color = p.v3.color = p.v4.color = c; -} - -void SuperParticle::vertexCopy(SuperVertex &b,const ParticleVertex& a) -{ - b.x = a.x - m_systemOffset.x(); - b.y = a.y - m_systemOffset.y(); - b.t = a.t; - b.lifeSpan = a.lifeSpan; - b.size = a.size; - b.endSize = a.endSize; - b.sx = a.sx; - b.sy = a.sy; - b.ax = a.ax; - b.ay = a.ay; -} - -void SuperParticle::reload(ParticleData *d) -{ - if (m_node == 0) - return; - - SuperVertices *particles = (SuperVertices *) m_node->geometry()->vertexData(); - - int pos = particleTypeIndex(d); - - SuperVertices &p = particles[pos]; - - //Perhaps we could be more efficient? - vertexCopy(p.v1, d->pv); - vertexCopy(p.v2, d->pv); - vertexCopy(p.v3, d->pv); - vertexCopy(p.v4, d->pv); -} - -void SuperParticle::load(ParticleData *d) -{ - if (m_node == 0) - return; - - //Color initialization - // Particle color - Color4ub color; - qreal redVariation = m_color_variation + m_redVariation; - qreal greenVariation = m_color_variation + m_greenVariation; - qreal blueVariation = m_color_variation + m_blueVariation; - color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation; - color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation; - color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation; - color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation; - SuperVertices *particles = (SuperVertices *) m_node->geometry()->vertexData(); - SuperVertices &p = particles[particleTypeIndex(d)]; - p.v1.color = p.v2.color = p.v3.color = p.v4.color = color; - if(m_xVector){ - const QPointF &ret = m_xVector->sample(QPointF(d->pv.x, d->pv.y)); - p.v1.xx = p.v2.xx = p.v3.xx = p.v4.xx = ret.x(); - p.v1.xy = p.v2.xy = p.v3.xy = p.v4.xy = ret.y(); - } - if(m_yVector){ - const QPointF &ret = m_yVector->sample(QPointF(d->pv.x, d->pv.y)); - p.v1.yx = p.v2.yx = p.v3.yx = p.v4.yx = ret.x(); - p.v1.yy = p.v2.yy = p.v3.yy = p.v4.yy = ret.y(); - } - p.v1.rotation = p.v2.rotation = p.v3.rotation = p.v4.rotation = - (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV; - p.v1.rotationSpeed = p.v2.rotationSpeed = p.v3.rotationSpeed = p.v4.rotationSpeed = - (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV; - p.v1.autoRotate = p.v2.autoRotate = p.v3.autoRotate = p.v4.autoRotate = m_autoRotation?1.0:0.0; - - vertexCopy(p.v1, d->pv); - vertexCopy(p.v2, d->pv); - vertexCopy(p.v3, d->pv); - vertexCopy(p.v4, d->pv); -} - -QT_END_NAMESPACE diff --git a/src/imports/particles/superparticle.h b/src/imports/particles/superparticle.h deleted file mode 100644 index ac2f9860ef..0000000000 --- a/src/imports/particles/superparticle.h +++ /dev/null @@ -1,389 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SUPERPARTICLE_H -#define SUPERPARTICLE_H -#include "particle.h" -#include "varyingvector.h" - -#include "coloredparticle.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class SuperMaterial; -class QSGGeometryNode; - -/*struct Color4ub {//in coloredparticle - uchar r; - uchar g; - uchar b; - uchar a; -};*/ - -struct SuperVertex { - float x; - float y; - float tx; - float ty; - float t; - float lifeSpan; - float size; - float endSize; - float sx; - float sy; - float ax; - float ay; - Color4ub color; - float xx; - float xy; - float yx; - float yy; - float rotation; - float rotationSpeed; - float autoRotate;//Assume that GPUs prefer floats to bools -}; - -struct SuperVertices { - SuperVertex v1; - SuperVertex v2; - SuperVertex v3; - SuperVertex v4; -}; - -class SuperParticle : public ParticleType -{ - Q_OBJECT - Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged) - Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged) - Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged) - Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged) - - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) - //Stacks (added) with individual colorVariations - Q_PROPERTY(qreal colorVariation READ colorVariation WRITE setColorVariation NOTIFY colorVariationChanged) - Q_PROPERTY(qreal redVariation READ redVariation WRITE setRedVariation NOTIFY redVariationChanged) - Q_PROPERTY(qreal greenVariation READ greenVariation WRITE setGreenVariation NOTIFY greenVariationChanged) - Q_PROPERTY(qreal blueVariation READ blueVariation WRITE setBlueVariation NOTIFY blueVariationChanged) - //Stacks (multiplies) with the Alpha in the color, mostly here so you can use svg color names (which have full alpha) - Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged) - Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged) - - Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) - Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged) - Q_PROPERTY(qreal rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged) - Q_PROPERTY(qreal rotationSpeedVariation READ rotationSpeedVariation WRITE setRotationSpeedVariation NOTIFY rotationSpeedVariationChanged) - //If true, then will face the direction of motion. Stacks with rotation, e.g. setting rotation - //to 180 will lead to facing away from the direction of motion - Q_PROPERTY(bool autoRotation READ autoRotation WRITE autoRotation NOTIFY autoRotationChanged) - - //###Call i/j? Makes more sense to those with vector calculus experience, and I could even add the cirumflex in QML? - //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size - Q_PROPERTY(VaryingVector* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged) - //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram. - Q_PROPERTY(VaryingVector* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged) -public: - explicit SuperParticle(QSGItem *parent = 0); - virtual ~SuperParticle(){} - - virtual void load(ParticleData*); - virtual void reload(ParticleData*); - virtual void setCount(int c); - - QUrl image() const { return m_image_name; } - void setImage(const QUrl &image); - - QUrl colortable() const { return m_colortable_name; } - void setColortable(const QUrl &table); - - QUrl sizetable() const { return m_sizetable_name; } - void setSizetable (const QUrl &table); - - QUrl opacitytable() const { return m_opacitytable_name; } - void setOpacitytable(const QUrl &table); - - QColor color() const { return m_color; } - void setColor(const QColor &color); - - qreal colorVariation() const { return m_color_variation; } - void setColorVariation(qreal var); - - qreal renderOpacity() const { return m_render_opacity; } - - qreal alphaVariation() const - { - return m_alphaVariation; - } - - qreal alpha() const - { - return m_alpha; - } - - qreal redVariation() const - { - return m_redVariation; - } - - qreal greenVariation() const - { - return m_greenVariation; - } - - qreal blueVariation() const - { - return m_blueVariation; - } - - qreal rotation() const - { - return m_rotation; - } - - qreal rotationVariation() const - { - return m_rotationVariation; - } - - qreal rotationSpeed() const - { - return m_rotationSpeed; - } - - qreal rotationSpeedVariation() const - { - return m_rotationSpeedVariation; - } - - bool autoRotation() const - { - return m_autoRotation; - } - - VaryingVector* xVector() const - { - return m_xVector; - } - - VaryingVector* yVector() const - { - return m_yVector; - } - -signals: - - void imageChanged(); - void colortableChanged(); - void sizetableChanged(); - void opacitytableChanged(); - - void colorChanged(); - void colorVariationChanged(); - - void particleDurationChanged(); - void alphaVariationChanged(qreal arg); - - void alphaChanged(qreal arg); - - void redVariationChanged(qreal arg); - - void greenVariationChanged(qreal arg); - - void blueVariationChanged(qreal arg); - - void rotationChanged(qreal arg); - - void rotationVariationChanged(qreal arg); - - void rotationSpeedChanged(qreal arg); - - void rotationSpeedVariationChanged(qreal arg); - - void autoRotationChanged(bool arg); - - void xVectorChanged(VaryingVector* arg); - - void yVectorChanged(VaryingVector* arg); - -public slots: - void setAlphaVariation(qreal arg) - { - if (m_alphaVariation != arg) { - m_alphaVariation = arg; - emit alphaVariationChanged(arg); - } - } - - void setAlpha(qreal arg) - { - if (m_alpha != arg) { - m_alpha = arg; - emit alphaChanged(arg); - } - } - - void setRedVariation(qreal arg) - { - if (m_redVariation != arg) { - m_redVariation = arg; - emit redVariationChanged(arg); - } - } - - void setGreenVariation(qreal arg) - { - if (m_greenVariation != arg) { - m_greenVariation = arg; - emit greenVariationChanged(arg); - } - } - - void setBlueVariation(qreal arg) - { - if (m_blueVariation != arg) { - m_blueVariation = arg; - emit blueVariationChanged(arg); - } - } - - void reloadColor(const Color4ub &c, ParticleData* d); - void setRotation(qreal arg) - { - if (m_rotation != arg) { - m_rotation = arg; - emit rotationChanged(arg); - } - } - - void setRotationVariation(qreal arg) - { - if (m_rotationVariation != arg) { - m_rotationVariation = arg; - emit rotationVariationChanged(arg); - } - } - - void setRotationSpeed(qreal arg) - { - if (m_rotationSpeed != arg) { - m_rotationSpeed = arg; - emit rotationSpeedChanged(arg); - } - } - - void setRotationSpeedVariation(qreal arg) - { - if (m_rotationSpeedVariation != arg) { - m_rotationSpeedVariation = arg; - emit rotationSpeedVariationChanged(arg); - } - } - - void autoRotation(bool arg) - { - if (m_autoRotation != arg) { - m_autoRotation = arg; - emit autoRotationChanged(arg); - } - } - - void setXVector(VaryingVector* arg) - { - if (m_xVector != arg) { - m_xVector = arg; - emit xVectorChanged(arg); - } - } - - void setYVector(VaryingVector* arg) - { - if (m_yVector != arg) { - m_yVector = arg; - emit yVectorChanged(arg); - } - } - -protected: - QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - void reset(); - void prepareNextFrame(); - QSGGeometryNode* buildParticleNode(); -private: - void vertexCopy(SuperVertex &b,const ParticleVertex& a); - bool m_do_reset; - - QUrl m_image_name; - QUrl m_colortable_name; - QUrl m_sizetable_name; - QUrl m_opacitytable_name; - - - QColor m_color; - qreal m_color_variation; - qreal m_particleDuration; - - QSGGeometryNode *m_node; - SuperMaterial *m_material; - - // derived values... - int m_last_particle; - - qreal m_render_opacity; - qreal m_alphaVariation; - qreal m_alpha; - qreal m_redVariation; - qreal m_greenVariation; - qreal m_blueVariation; - qreal m_rotation; - qreal m_rotationVariation; - qreal m_rotationSpeed; - qreal m_rotationSpeedVariation; - bool m_autoRotation; - VaryingVector* m_xVector; - VaryingVector* m_yVector; -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // SUPERPARTICLE_H diff --git a/src/imports/particles/swarmaffector.cpp b/src/imports/particles/swarmaffector.cpp deleted file mode 100644 index 513e8a17a7..0000000000 --- a/src/imports/particles/swarmaffector.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "swarmaffector.h" -#include "particle.h" -#include -#include -QT_BEGIN_NAMESPACE - -SwarmAffector::SwarmAffector(QSGItem *parent) : - ParticleAffector(parent), m_strength(1), m_inited(false) -{ - connect(this, SIGNAL(leadersChanged(QStringList)), - this, SLOT(updateGroupList())); -} - -void SwarmAffector::ensureInit() -{ - if(m_inited) - return; - m_inited = true; - updateGroupList(); - m_lastPos.resize(m_system->count()); -} - -const qreal epsilon = 0.0000001; -bool SwarmAffector::affectParticle(ParticleData *d, qreal dt) -{ - ensureInit(); - QPointF curPos(d->curX(), d->curY()); - if(m_leaders.isEmpty() || m_leadGroups.contains(d->group)){ - m_lastPos[d->systemIndex] = curPos; - if(m_leadGroups.contains(d->group)) - return false; - } - - qreal fx = 0.0; - qreal fy = 0.0; - for(int i=0; i < m_lastPos.count(); i++){ - if(m_lastPos[i].isNull()) - continue; - QPointF diff = m_lastPos[i] - curPos; - qreal r = sqrt(diff.x() * diff.x() + diff.y() * diff.y()); - if(r == 0.0) - continue; - qreal f = m_strength * (1/r); - if(f < epsilon) - continue; - qreal theta = atan2(diff.y(), diff.x()); - fx += cos(theta) * f; - fy += sin(theta) * f; - } - if(!fx && !fy) - return false; - d->setInstantaneousSX(d->curSX()+fx * dt); - d->setInstantaneousSY(d->curSY()+fy * dt); - return true; -} - -void SwarmAffector::reset(int systemIdx) -{ - if(!m_system) - return; - if(!m_lastPos[systemIdx].isNull()) - m_lastPos[systemIdx] = QPointF(); -} - -void SwarmAffector::updateGroupList() -{ - if(!m_system || !m_system->m_initialized) - return; - m_leadGroups.clear(); - foreach(const QString &s, m_leaders) - m_leadGroups << m_system->m_groupIds[s]; -} -QT_END_NAMESPACE diff --git a/src/imports/particles/swarmaffector.h b/src/imports/particles/swarmaffector.h deleted file mode 100644 index 63f77c9294..0000000000 --- a/src/imports/particles/swarmaffector.h +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SWARMAFFECTOR_H -#define SWARMAFFECTOR_H -#include "particleaffector.h" -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - - -class ParticleType; - -class SwarmAffector : public ParticleAffector -{ - Q_OBJECT - Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged) - Q_PROPERTY(QStringList leaders READ leaders WRITE setLeaders NOTIFY leadersChanged) -public: - explicit SwarmAffector(QSGItem *parent = 0); - virtual bool affectParticle(ParticleData *d, qreal dt); - virtual void reset(int systemIdx); - - qreal strength() const - { - return m_strength; - } - - QStringList leaders() const - { - return m_leaders; - } - -signals: - - void strengthChanged(qreal arg); - - void leadersChanged(QStringList arg); - -public slots: - -void setStrength(qreal arg) -{ - if (m_strength != arg) { - m_strength = arg; - emit strengthChanged(arg); - } -} - -void setLeaders(QStringList arg) -{ - if (m_leaders != arg) { - m_leaders = arg; - emit leadersChanged(arg); - } -} - -private: - void ensureInit(); - void mapUpdate(int idx, qreal strength); - QVector m_lastPos; - qreal m_strength; - bool m_inited; - QStringList m_leaders; - QSet m_leadGroups; -private slots: - void updateGroupList(); -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // SWARMAFFECTOR_H diff --git a/src/imports/particles/toggleaffector.cpp b/src/imports/particles/toggleaffector.cpp deleted file mode 100644 index 5e03b17684..0000000000 --- a/src/imports/particles/toggleaffector.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "toggleaffector.h" - -QT_BEGIN_NAMESPACE - -ToggleAffector::ToggleAffector(QObject *parent) : - ParticleAffector(parent) -{ -} - -bool ToggleAffector::affect(ParticleData *d, qreal dt) -{ - if(m_affecting) - return m_affector->affect(d, dt); - else - return false; -} - -QT_END_NAMESPACE diff --git a/src/imports/particles/toggleaffector.h b/src/imports/particles/toggleaffector.h deleted file mode 100644 index 08e7c0e2eb..0000000000 --- a/src/imports/particles/toggleaffector.h +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef TOGGLEAFFECTOR_H -#define TOGGLEAFFECTOR_H -#include "particleaffector.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class ToggleAffector : public ParticleAffector -{ - Q_OBJECT - Q_PROPERTY(bool affecting READ affecting WRITE setAffecting NOTIFY affectingChanged) - Q_PROPERTY(ParticleAffector* affector READ affector WRITE affector NOTIFY affectorChanged) - Q_CLASSINFO("DefaultProperty", "affector") - -public: - explicit ToggleAffector(QObject *parent = 0); - virtual bool affect(ParticleData *d, qreal dt); - bool affecting() const - { - return m_affecting; - } - - ParticleAffector* affector() const - { - return m_affector; - } - -signals: - - void affectingChanged(bool arg); - - void affectorChanged(ParticleAffector* arg); - -public slots: -void setAffecting(bool arg) -{ - if (m_affecting != arg) { - m_affecting = arg; - emit affectingChanged(arg); - } -} - -void affector(ParticleAffector* arg) -{ - if (m_affector != arg) { - m_affector = arg; - emit affectorChanged(arg); - } -} - -private: -bool m_affecting; -ParticleAffector* m_affector; -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // TOGGLEAFFECTOR_H diff --git a/src/imports/particles/zoneaffector.cpp b/src/imports/particles/zoneaffector.cpp deleted file mode 100644 index cb7adca795..0000000000 --- a/src/imports/particles/zoneaffector.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "zoneaffector.h" -#include - -QT_BEGIN_NAMESPACE - -ZoneAffector::ZoneAffector(QObject *parent) : - ParticleAffector(parent), m_x(0), m_y(0), m_width(0), m_height(0), m_affector(0) -{ -} - -bool ZoneAffector::affect(ParticleData *d, qreal dt) -{ - if(!m_affector) - return false; - qreal x = d->curX(); - qreal y = d->curY(); - if(x >= m_x && x <= m_x+m_width && y >= m_y && y <= m_y+m_height) - return m_affector->affect(d, dt); - return false; -} - -void ZoneAffector::reset(int systemIdx) -{ - if(m_affector) - m_affector->reset(systemIdx); -} -QT_END_NAMESPACE diff --git a/src/imports/particles/zoneaffector.h b/src/imports/particles/zoneaffector.h deleted file mode 100644 index 8c17cedba2..0000000000 --- a/src/imports/particles/zoneaffector.h +++ /dev/null @@ -1,159 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** 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 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef ZONEAFFECTOR_H -#define ZONEAFFECTOR_H -#include "particleaffector.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class ZoneAffector : public ParticleAffector -{ - Q_OBJECT - //TODO: Can we get anchors in here? consider becoming an un-parented QSGItem? - Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged); - Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged); - Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged); - Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY heightChanged); - Q_PROPERTY(ParticleAffector* affector READ affector WRITE affector NOTIFY affectorChanged) - Q_CLASSINFO("DefaultProperty", "affector") -public: - explicit ZoneAffector(QObject *parent = 0); - - virtual bool affect(ParticleData *d, qreal dt); - virtual void reset(int systemIdx); - - ParticleAffector* affector() const - { - return m_affector; - } - - qreal x() const - { - return m_x; - } - - qreal y() const - { - return m_y; - } - - qreal width() const - { - return m_width; - } - - qreal height() const - { - return m_height; - } - -signals: - - - void affectorChanged(ParticleAffector* arg); - - void xChanged(qreal arg); - - void yChanged(qreal arg); - - void widthChanged(qreal arg); - - void heightChanged(qreal arg); - -public slots: - - -void affector(ParticleAffector* arg) -{ - if (m_affector != arg) { - m_affector = arg; - emit affectorChanged(arg); - } -} - -void setX(qreal arg) -{ - if (m_x != arg) { - m_x = arg; - emit xChanged(arg); - } -} - -void setY(qreal arg) -{ - if (m_y != arg) { - m_y = arg; - emit yChanged(arg); - } -} - -void setWidth(qreal arg) -{ - if (m_width != arg) { - m_width = arg; - emit widthChanged(arg); - } -} - -void setHeight(qreal arg) -{ - if (m_height != arg) { - m_height = arg; - emit heightChanged(arg); - } -} - -private: -qreal m_x; -qreal m_y; -qreal m_width; -qreal m_height; -ParticleAffector* m_affector; -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // ZONEAFFECTOR_H From 984f21f18d1a3981e7363df467ff2e24e69aa847 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Tue, 7 Jun 2011 19:39:38 +1000 Subject: [PATCH 08/48] Immense Particles Refactor Part B Examples work again. Also augmented Wander and PointAttractor with physics modes. --- .../declarative/particles/allsmiles/plain.qml | 26 ++++ .../declarative/particles/allsmiles/smile.qml | 94 +++++++++++-- .../particles/allsmiles/smilefactory.qml | 20 +-- .../particles/allsmiles/spriteparticles.qml | 16 +-- .../allsmiles/spritestateparticles.qml | 10 +- .../allsmiles/spritevariedparticles.qml | 10 +- .../particles/allsmiles/ultraparticles.qml | 8 +- .../particles/asteroid/asteroid.qml | 26 ++-- .../particles/asteroid/blackhole.qml | 26 ++-- .../particles/custom/blurparticles.qml | 125 ++++++++++++++++++ .../particles/custom/content/smile.png | Bin 0 -> 15408 bytes .../particles/modelparticles/bubbles.qml | 13 +- .../particles/modelparticles/gridsplosion.qml | 20 ++- .../particles/modelparticles/package.qml | 8 +- .../particles/modelparticles/stream.qml | 32 ++--- .../snow/{snow2.qml => content/Button.qml} | 55 ++++---- examples/declarative/particles/snow/snow.qml | 34 +++-- examples/declarative/particles/snow/snow3.qml | 80 ----------- .../particles/spaceexplorer/spaceexplorer.qml | 45 ++++--- .../particles/trails/dynamicemitters.qml | 10 +- .../particles/trails/fireballs.qml | 28 ++-- .../declarative/particles/trails/layered.qml | 24 ++-- .../declarative/particles/trails/list.qml | 6 +- .../particles/trails/overburst.qml | 8 +- .../declarative/particles/trails/portal.qml | 18 +-- .../declarative/particles/trails/rainbow.qml | 10 +- .../declarative/particles/trails/shimmer.qml | 6 +- .../declarative/particles/trails/swarm.qml | 78 ----------- .../declarative/particles/trails/trails.qml | 12 +- .../particles/trails/turbulence.qml | 20 +-- .../particles/trails/velocityfrommotion.qml | 34 ++--- src/declarative/particles/qsgmaskextruder.cpp | 11 +- .../particles/qsgparticleaffector_p.h | 1 - .../particles/qsgpointattractor.cpp | 32 ++++- .../particles/qsgpointattractor_p.h | 48 +++++++ src/declarative/particles/qsgwander.cpp | 36 ++++- src/declarative/particles/qsgwander_p.h | 29 +++- 37 files changed, 633 insertions(+), 426 deletions(-) create mode 100644 examples/declarative/particles/allsmiles/plain.qml create mode 100644 examples/declarative/particles/custom/blurparticles.qml create mode 100644 examples/declarative/particles/custom/content/smile.png rename examples/declarative/particles/snow/{snow2.qml => content/Button.qml} (72%) delete mode 100644 examples/declarative/particles/snow/snow3.qml delete mode 100644 examples/declarative/particles/trails/swarm.qml diff --git a/examples/declarative/particles/allsmiles/plain.qml b/examples/declarative/particles/allsmiles/plain.qml new file mode 100644 index 0000000000..1b456b0c5b --- /dev/null +++ b/examples/declarative/particles/allsmiles/plain.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 +import QtQuick.Particles 2.0 + +Rectangle{ + color: "goldenrod" + width: 2000 + height: 2000 + ParticleSystem{id: sys} + ImageParticle{ + id: up + system: sys + image: "content/singlesmile.png" + } + Emitter{ + anchors.centerIn: parent + system: sys + particlesPerSecond: 1000 + particleSize: 20 + particleDuration: 10000 + speed: AngledDirection{angleVariation: 360; magnitudeVariation: 100;} + } + MouseArea{ + anchors.fill: parent + onClicked: up.autoRotation = !up.autoRotation + } +} diff --git a/examples/declarative/particles/allsmiles/smile.qml b/examples/declarative/particles/allsmiles/smile.qml index e37e8fa98e..9ce0e3a5e6 100644 --- a/examples/declarative/particles/allsmiles/smile.qml +++ b/examples/declarative/particles/allsmiles/smile.qml @@ -39,27 +39,93 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ + id: root color: "white" width: 310 height: 300 ParticleSystem{ id: sys } - Picture{ + CustomParticle{ system: sys - anchors.fill: parent - image: "content/singlesmile.png" - onceOff: true + property real maxWidth: root.width + property real maxHeight: root.height + ShaderEffectSource{ + id: pictureSource + sourceItem: picture + hideSource: true + } + Image{ + id: picture + source: "content/singlesmile.png" + } + ShaderEffectSource{ + id: particleSource + sourceItem: particle + hideSource: true + } + Image{ + id: particle + source: "content/particle.png" + } + vertexShader:" + attribute highp vec2 vPos; + attribute highp vec2 vTex; + attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize + attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration + attribute highp float r; + + uniform highp float maxWidth; + uniform highp float maxHeight; + + uniform highp mat4 qt_ModelViewProjectionMatrix; + uniform highp float timestamp; + uniform lowp float qt_Opacity; + + varying highp vec2 fTex; + varying highp vec2 fTex2; + varying lowp float fFade; + + void main() { + fTex = vTex; + fTex2 = vec2(vPos.x / maxWidth, vPos.y / maxHeight); + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fFade = fadeIn * fadeOut * qt_Opacity; + } + " + property variant particleTexture: particleSource + property variant pictureTexture: pictureSource + fragmentShader: " + uniform sampler2D particleTexture; + uniform sampler2D pictureTexture; + varying highp vec2 fTex; + varying highp vec2 fTex2; + varying highp float fFade; + void main() { + gl_FragColor = texture2D(pictureTexture, fTex2) * texture2D(particleTexture, fTex).w * fFade; + }" } - ColoredParticle{ - system: sys - image: "content/particle.png" - color: "black" - alpha: 0.4 - sizeTable: "content/sizeInOut.png" - } - TrailEmitter{ + Emitter{ id: emitter system: sys emitting: false @@ -67,7 +133,7 @@ Rectangle{ maxParticles: 1200 anchors.fill: parent particleSize: 32 - speed: PointVector{ xVariation: 12; yVariation: 12 } + speed: PointDirection{ xVariation: 12; yVariation: 12 } } MouseArea{ anchors.fill: parent diff --git a/examples/declarative/particles/allsmiles/smilefactory.qml b/examples/declarative/particles/allsmiles/smilefactory.qml index 47becb50fe..262644ec56 100644 --- a/examples/declarative/particles/allsmiles/smilefactory.qml +++ b/examples/declarative/particles/allsmiles/smilefactory.qml @@ -39,14 +39,14 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ color: "goldenrod" width: 400 height: 400 ParticleSystem{id:sys} - DeformableParticle{ + ImageParticle{ system: sys particles: ["goingLeft", "goingRight"] image: "content/singlesmile.png" @@ -54,12 +54,12 @@ Rectangle{ rotationSpeed: 90 autoRotation: true } - DeformableParticle{ + ImageParticle{ system: sys particles: ["goingDown"] image: "content/squarefacespriteXX.png" rotation: 180 - yVector: PointVector{ y: 0.5; yVariation: 0.25; xVariation: 0.25; } + yVector: PointDirection{ y: 0.5; yVariation: 0.25; xVariation: 0.25; } } Timer{ running: true @@ -79,38 +79,38 @@ Rectangle{ interval: 8400 onTriggered: emitC.emitting = true; } - TrailEmitter{ + Emitter{ id: emitA x: 0 y: 120 system: sys emitting: false particle: "goingRight" - speed: PointVector{ x: 100 } + speed: PointDirection{ x: 100 } particleDuration: 4000 particlesPerSecond: 2 particleSize: 32 } - TrailEmitter{ + Emitter{ id: emitB x: 400 y: 240 system: sys emitting: false particle: "goingLeft" - speed: PointVector{ x: -100 } + speed: PointDirection{ x: -100 } particleDuration: 4000 particlesPerSecond: 2 particleSize: 32 } - TrailEmitter{ + Emitter{ id: emitC x: 0 y: 360 system: sys emitting: false particle: "goingDown" - speed: PointVector{ x: 100 } + speed: PointDirection{ x: 100 } particleDuration: 4000 particlesPerSecond: 2 particleSize: 32 diff --git a/examples/declarative/particles/allsmiles/spriteparticles.qml b/examples/declarative/particles/allsmiles/spriteparticles.qml index 4bcb7081b8..1f385f2c20 100644 --- a/examples/declarative/particles/allsmiles/spriteparticles.qml +++ b/examples/declarative/particles/allsmiles/spriteparticles.qml @@ -39,13 +39,13 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ color: "goldenrod" width: 400 height: 400 - ColoredParticle{ + ImageParticle{ id: test particles: ["Test"] image: "content/particle.png" @@ -55,23 +55,23 @@ Rectangle{ color: "#336666CC" colorVariation: 0.0 } - SpriteParticle{ + ImageParticle{ id: single particles: ["Face"] system: sys z: 2 anchors.fill: parent - Sprite{ + sprites: Sprite{ source: "content/squarefacesprite.png" frames: 6 duration: 120 } } - Mask{ + MaskShape{ id: mask source: "content/smileMask.png" } - TrailEmitter{ + Emitter{ system: sys particle: "Test" anchors.fill: parent @@ -82,7 +82,7 @@ Rectangle{ particleSize: 10 shape: mask } - TrailEmitter{ + Emitter{ system: sys particle: "Face" anchors.fill: parent @@ -90,7 +90,7 @@ Rectangle{ particlesPerSecond: 60 particleDuration: 1440 emitting: true - speed: PointVector{xVariation: 10; yVariation: 10;} + speed: PointDirection{xVariation: 10; yVariation: 10;} particleSize: 30 particleSizeVariation: 10 shape: mask diff --git a/examples/declarative/particles/allsmiles/spritestateparticles.qml b/examples/declarative/particles/allsmiles/spritestateparticles.qml index 6a61487a89..0818686e15 100644 --- a/examples/declarative/particles/allsmiles/spritestateparticles.qml +++ b/examples/declarative/particles/allsmiles/spritestateparticles.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ color: "goldenrod" @@ -109,7 +109,7 @@ Rectangle{ z:4 } ParticleSystem{ id: sys } - SpriteParticle{ + ImageParticle{ anchors.fill: parent id: particles system: sys @@ -168,13 +168,13 @@ Rectangle{ duration: 10000 }] } - TrailEmitter{ + Emitter{ system: sys particlesPerSecond: 16 particleDuration: 10000 emitting: true - speed: AngleVector{angle: 90; magnitude: 60; angleVariation: 5} - acceleration: PointVector{ y: 10 } + speed: AngledDirection{angle: 90; magnitude: 60; angleVariation: 5} + acceleration: PointDirection{ y: 10 } particleSize: 30 particleSizeVariation: 10 width: parent.width diff --git a/examples/declarative/particles/allsmiles/spritevariedparticles.qml b/examples/declarative/particles/allsmiles/spritevariedparticles.qml index c1b773093f..e6aeaccea1 100644 --- a/examples/declarative/particles/allsmiles/spritevariedparticles.qml +++ b/examples/declarative/particles/allsmiles/spritevariedparticles.qml @@ -39,14 +39,14 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ color: "goldenrod" width: 800 height: 800 ParticleSystem{ id: sys } - SpriteParticle{ + ImageParticle{ system: sys anchors.fill: parent sprites: [Sprite{ @@ -92,15 +92,15 @@ Rectangle{ duration: 120 }] } - TrailEmitter{ + Emitter{ id: particleEmitter system: sys width: parent.width particlesPerSecond: 16 particleDuration: 8000 emitting: true - speed: AngleVector{angle: 90; magnitude: 300; magnitudeVariation: 100; angleVariation: 5} - acceleration: PointVector{ y: 10 } + speed: AngledDirection{angle: 90; magnitude: 300; magnitudeVariation: 100; angleVariation: 5} + acceleration: PointDirection{ y: 10 } particleSize: 30 particleSizeVariation: 10 } diff --git a/examples/declarative/particles/allsmiles/ultraparticles.qml b/examples/declarative/particles/allsmiles/ultraparticles.qml index 85bbdbacd8..f2b6f8d163 100644 --- a/examples/declarative/particles/allsmiles/ultraparticles.qml +++ b/examples/declarative/particles/allsmiles/ultraparticles.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ color: "white" @@ -48,7 +48,7 @@ Rectangle{ ParticleSystem{ id: sys } - UltraParticle{ + ImageParticle{ sprites: [ Sprite{ name: "licking" @@ -80,14 +80,14 @@ Rectangle{ factor: 0.1 system: sys } - TrailEmitter{ + Emitter{ system: sys anchors.centerIn: parent id: particles particlesPerSecond: 200 particleDuration: 6000 emitting: true - speed: AngleVector{angleVariation: 360; magnitude: 80; magnitudeVariation: 40} + speed: AngledDirection{angleVariation: 360; magnitude: 80; magnitudeVariation: 40} particleSize: 40 particleEndSize: 80 } diff --git a/examples/declarative/particles/asteroid/asteroid.qml b/examples/declarative/particles/asteroid/asteroid.qml index b5b4f672c2..223ea81205 100644 --- a/examples/declarative/particles/asteroid/asteroid.qml +++ b/examples/declarative/particles/asteroid/asteroid.qml @@ -38,8 +38,8 @@ ** ****************************************************************************/ -import Qt.labs.particles 2.0 -import Qt.labs.particles 2.0 as Qlp +import QtQuick.Particles 2.0 +import QtQuick.Particles 2.0 as Qlp import QtQuick 2.0 Item { @@ -65,14 +65,14 @@ Item { } } - ColoredParticle { + ImageParticle { system: sys particles: ["starfield"] image: "content/star.png" colorVariation: 0.3 color: "white" } - TrailEmitter { + Emitter { id: starField system: sys particle: "starfield" @@ -82,25 +82,25 @@ Item { anchors.centerIn: parent - //acceleration: AngleVector{angleVariation: 360; magnitude: 200}//Is this a better effect, more consistent speed? - acceleration: PointVector{ xVariation: 200; yVariation: 200; } + //acceleration: AngledDirection{angleVariation: 360; magnitude: 200}//Is this a better effect, more consistent speed? + acceleration: PointDirection{ xVariation: 200; yVariation: 200; } particleSize: 0 particleEndSize: 80 particleSizeVariation: 10 } - TrailEmitter{ + Emitter{ system: sys particle: "meteor" particlesPerSecond: 12 particleDuration: 5000 emitting: true - acceleration: PointVector{ xVariation: 80; yVariation: 80; } + acceleration: PointDirection{ xVariation: 80; yVariation: 80; } particleSize: 15 particleEndSize: 300 anchors.centerIn: parent } - SpriteParticle{ + ImageParticle{ system: sys particles: ["meteor"] sprites:[Sprite{ @@ -168,7 +168,7 @@ Item { } } - ColoredParticle{ + ImageParticle{ z:0 system: sys particles: ["exhaust"] @@ -191,7 +191,7 @@ Item { colorVariation: 0.2 } - TrailEmitter{ + Emitter{ id: trailsNormal2 system: sys particle: "exhaust" @@ -202,10 +202,10 @@ Item { y: holder.y x: holder.x - speed: PointVector{ xVariation: 40; yVariation: 40; } + speed: PointDirection{ xVariation: 40; yVariation: 40; } speedFromMovement: 16 - acceleration: PointVector{ xVariation: 10; yVariation: 10; } + acceleration: PointDirection{ xVariation: 10; yVariation: 10; } particleSize: 4 particleSizeVariation: 4 diff --git a/examples/declarative/particles/asteroid/blackhole.qml b/examples/declarative/particles/asteroid/blackhole.qml index 68d5835880..f2c9d0a98d 100644 --- a/examples/declarative/particles/asteroid/blackhole.qml +++ b/examples/declarative/particles/asteroid/blackhole.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ id: root @@ -65,7 +65,7 @@ Rectangle{ } } - TrailEmitter{ + Emitter{ particle: "stars" system: particles particlesPerSecond: 40 @@ -73,10 +73,10 @@ Rectangle{ emitting: true particleSize: 30 particleSizeVariation: 10 - speed: PointVector{ x: 220; xVariation: 40 } + speed: PointDirection{ x: 220; xVariation: 40 } height: parent.height } - TrailEmitter{ + Emitter{ particle: "roids" system: particles particlesPerSecond: 10 @@ -84,14 +84,14 @@ Rectangle{ emitting: true particleSize: 30 particleSizeVariation: 10 - speed: PointVector{ x: 220; xVariation: 40 } + speed: PointDirection{ x: 220; xVariation: 40 } height: parent.height } ParticleSystem{ id: particles anchors.fill: parent } - ColoredParticle{ + ImageParticle{ id: stars particles: ["stars"] system: particles @@ -99,7 +99,7 @@ Rectangle{ color: "white" colorVariation: 0.1 } - SpriteParticle{ + ImageParticle{ id: roids particles: ["roids"] system: particles @@ -112,7 +112,7 @@ Rectangle{ speedModifiesDuration: -0.1 } } - ColoredParticle{ + ImageParticle{ id: shot particles: ["shot"] system: particles @@ -121,7 +121,7 @@ Rectangle{ color: "#0FF06600" colorVariation: 0.3 } - ColoredParticle{ + ImageParticle{ id: engine particles: ["engine"] system: particles @@ -166,7 +166,7 @@ Rectangle{ drag.axis: Drag.XandYAxis drag.target: ship } - TrailEmitter{ + Emitter{ particle: "engine" system: particles particlesPerSecond: 200 @@ -175,18 +175,18 @@ Rectangle{ particleSize: 10 particleEndSize: 4 particleSizeVariation: 4 - speed: PointVector{ x: -128; xVariation: 32 } + speed: PointDirection{ x: -128; xVariation: 32 } height: parent.height width: 20 } - TrailEmitter{ + Emitter{ particle: "shot" system: particles particlesPerSecond: 32 particleDuration: 2000 emitting: spacePressed particleSize: 40 - speed: PointVector{ x: 256; } + speed: PointDirection{ x: 256; } x: parent.width y: parent.height/2 } diff --git a/examples/declarative/particles/custom/blurparticles.qml b/examples/declarative/particles/custom/blurparticles.qml new file mode 100644 index 0000000000..7d0f9cc6ac --- /dev/null +++ b/examples/declarative/particles/custom/blurparticles.qml @@ -0,0 +1,125 @@ +import QtQuick 2.0 +import QtQuick.Particles 2.0 + +Rectangle{ + color: "white" + width: 240 + height: 360 + ParticleSystem{ + id: sys + } + Emitter{ + system:sys + height: parent.height + particlesPerSecond: 1 + particleDuration: 12000 + speed: PointDirection{x:20;} + particleSize: 64 + } + ShaderEffectSource{ + id: theSource + sourceItem: theItem + hideSource: true + } + Image{ + id: theItem + source: "content/smile.png" + } + + CustomParticle{ + system: sys + //TODO: Someway that you don't have to rewrite the basics for a simple addition + vertexShader:" + attribute highp vec2 vPos; + attribute highp vec2 vTex; + attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize + attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration + attribute highp float r; + + uniform highp mat4 qt_ModelViewProjectionMatrix; + uniform highp float timestamp; + uniform lowp float qt_Opacity; + + varying highp vec2 fTex; + varying lowp float fFade; + varying lowp float fBlur; + + void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fFade = fadeIn * fadeOut * qt_Opacity; + fBlur = max(0.2 * t, t * r); + } + " + property variant source: theSource + property variant blurred: ShaderEffectSource { + smooth: true + sourceItem: ShaderEffectItem { + width: theItem.width + height: theItem.height + property variant delta: Qt.size(0.0, 1.0 / height) + property variant source: ShaderEffectSource { + smooth: true + sourceItem: ShaderEffectItem { + width: theItem.width + height: theItem.height + property variant delta: Qt.size(1.0 / width, 0.0) + property variant source: theSource + fragmentShader: " + uniform sampler2D source; + uniform highp vec2 delta; + varying highp vec2 qt_TexCoord0; + void main() { + gl_FragColor = 0.0538 * texture2D(source, qt_TexCoord0 - 3.182 * delta) + + 0.3229 * texture2D(source, qt_TexCoord0 - 1.364 * delta) + + 0.2466 * texture2D(source, qt_TexCoord0) + + 0.3229 * texture2D(source, qt_TexCoord0 + 1.364 * delta) + + 0.0538 * texture2D(source, qt_TexCoord0 + 3.182 * delta); + }" + } + } + fragmentShader: " + uniform sampler2D source; + uniform highp vec2 delta; + varying highp vec2 qt_TexCoord0; + void main() { + gl_FragColor = 0.0538 * texture2D(source, qt_TexCoord0 - 3.182 * delta) + + 0.3229 * texture2D(source, qt_TexCoord0 - 1.364 * delta) + + 0.2466 * texture2D(source, qt_TexCoord0) + + 0.3229 * texture2D(source, qt_TexCoord0 + 1.364 * delta) + + 0.0538 * texture2D(source, qt_TexCoord0 + 3.182 * delta); + }" + } + } + fragmentShader: " + uniform sampler2D source; + uniform sampler2D blurred; + varying highp vec2 fTex; + varying highp float fBlur; + varying highp float fFade; + void main() { + gl_FragColor = mix(texture2D(source, fTex), texture2D(blurred, fTex), min(1.0,fBlur*3.0)) * fFade; + }" + + } +} + diff --git a/examples/declarative/particles/custom/content/smile.png b/examples/declarative/particles/custom/content/smile.png new file mode 100644 index 0000000000000000000000000000000000000000..3d66d725781730c7a9376a25113164f8882d9795 GIT binary patch literal 15408 zcmYMbb95%Y6E|Ahwrv|vZF6g5>!-GD+qP|Q+pV|l*0$~T?)P`^eczljnVe+)NHU*H zCNn3I%8F8maCmSaARve`(&DQBIN(1*f%;d!z1HUbW3Z0WKb-%KFaJlN2@ak)|8=@K zidg<9`9C_le=+*kfHIMj694{Rfz^KA00H5Tl@S+F_gK5|aaY$^Y5Niibv_~Fmk==p z9gMDuJY++PoKeDz83)Z#h@j-W!-*=Sk#x*rK=eNb?Z96DrGgE5W)$&Af_=f4 z^2-$--`@Yc@q6X*(Gx-zs{f`L5RN%f4PK0be_JbeY1vOqq>eHa#1dp?AX0wVnUB-?vc{F*MRr)e&iAX}*`2Q) zka~xcYr0qDO6uz~WMgC&KKLkXNaFO&3lZ|X9o%*76}d_(Sbm3#o)wg>|Hj#(pV(Bj zkY#D{aU9FC_?cNy{HB4p@qp6 z0;gZ6fT@dy96XI8;lY3pasU;8#lqgB=LqHA`sqO9phs|R>p9--xJ6FfUxX+H$4*oS zTh;7Pabnwv-Sjeym)^P6obAhJQg!@0Y&pU%_uV@m!Mj{6e}{Cd&JMjO-P_Z1Ix6PG zY1F##xlqx%_x#Ld%Z9)9dK1A>zC`YMHV4a#vLzP>6K?j$@S5u!H1cf%er_zzW@rBt z1Mzs-l$3mG*^y9)?$AVV6&cj8{4C9m|b~Qj?dIluTkV| z=SRSob`=omsy5IO*dgS2=G7dRMM=(I(`yuZ*v_+-9zhQ*z$ui`Qn$-sEn z>uk6_@0(x3HqVT&O@dYPTafSqU}ydqXB)=V@$To=`hD&O`*};o+aUaZHzM2UrGgmo z;@S~1cEEf2I0(6T(Td%EMbYw1CWK|tx)$`(vUpAk4rS0{0k*@o#f zMF#N%;BfBSUYXx5wU#IERDDQ&Onr)d_Ds-B>EF$81vL2OAOff%V>?8y-hdLdK{}#9 zT?C8}sL~;xL5T?4-iy*BYrE$c@k*L^m2RlcQ+`CuFs|4n$>TR^2YCm|*OptH^((7< zIg&179Y*|B&=JoTr&=cwzy|iCZp<4&asBp42M|!3Zuz`j@chFb815T@mtKu(jgy*N z#@P?|61!i;eZ-a{zEaNBEjybF6Au~VP9Ht;Y~T&ISzXY7*f+Z`V0?srwnQihGNy~G z$7v5v^DC{vm7H%#Hm&8g@|jiS=I6Zp%H(W$>@9z}A^s(P?mL^XAxuVgU$e{f{UDKI zB$^?$A$K}>I(+(Ke-7A(E4KfcoVG2!2+TX|$zAC86|4y!w9K-0Lj;OiG@QHXK4^44 z^d|dC=FO?Ion@gRAR4ya*Pf#fBk>~d5^wFzCx72)Ubx+Zw+KAym;bFYi9G-KyUc81 zhq3b}Dk>7WuSpC-iGUvQ-eOqVQ??BaNJT&~`YvNQW5_m7PkEbbHY~mC&v*3j$e-(u ziH<$RuFVCFCA!+rmL?;ixv45WHVRHm`+z?fLXOe`W9(vHowV=;`ai{Nh&MwCfyMH>W~0 zR4?nu5063oBv{*mLn`KlBplZdcj?E5ou4eP$CHbc_h(~uRr{=kPjzF?LC+nzur?^E z&}1*2qbs-h0Y=>`+eFz&=ynG}ZNr-K#WBx#apQ!Y_RW@DsIFTqF<}N}pJCIaaU^1H z)B^PRW!0EKKI15kG!{J}v6DdQ6?9k!+4Ixh@jJvJav+GV9^9ux(@`Z~ofFfHSpWDv z!_3qzaE>9Pyxm(}O1rYI>gwZfDMnD+*!ZxJu~UZb3U1I&W0hh*irP)yS#PP>nW47E z@zO;Q*)wml{}FH(xCh(^9sobnH3GiND-As>My7TWV)F<6#cJ+tY6{fOQI;P^=Qq-~ z5~KAFAmlXl>t)<~i-Y1?aV#z_IVk_Mmc!m37DRy>IdyRh+PgIjtUKV5e;T6h_IJim z3|^zLX_$<<(gi+&heTag$<|zg8i(|DdTmQy!|pzzhXlG|r)n?vSu>NySgZ^;3ZJ?2 zpVzY+C)DP>^-X7P`b&F=2c;G{%&G4Pv>NhU1J*gY9+3f~j~(+)m|<~H{LnFD8zrMp zdGr9(HEzC@i>k(UwzaJagK!!5=!aKGp~z^xj+~B-4uVd-&YW*Z!GX$Nmcg(fhza0!9!5r-JwMLvXok>-2mj zOX02JVvz7aMvcS%TSVHL!i+L(o4iB{`ZTeMx9xO#NF*m@2rlP4Of$nh46^Q1HH1z! zdR%iJ$?q0dGK7}zBD4vXC81cy;8@zYR-#?pbMqnmqk5|kgyzgO8BXWbm&6dc>1U7t z-E+Sz_YfesGlG7@l!&%gk6LrWdc#a?AAK?U0K0(0Mltr>`IDTq&lI?5!mWNduj`bT z0$o@3d#B;|VeS7}uQ>)l#f=EM15yRq`TROj^cS!<)jg$LH2mljj`^VNIZ_8WXT|EH zy^dq;biOu6FF@FQkKVv1(=kDA(|$Mcv0hDN_M;V98^>bvMs2xG5d!)>?H;3#p%`Q&FEcy%fSKGJDHd4 z|c~0(#pcLP0F?nAiD&Vw+=GwtJ%L zkgHx()jy#Q@Qsy>6kn9<6hq^9-p-GU?V$vN-yeR z8@vVP6D^jtn`be=dD|3jZ>5XJAy<9h<1t66er}T5&1;f?!>cP&`Z+Y0WW8x;BG-K<5o;ZS*QQKvzy2N31<^Ufy6hew=27d9C(n(Eq48;b% z!AF=+T}4G2>gOgDbbEJv0bAH^)Rj;_JNy(Fk+%4%#oin=fvV*+LY6Q|dcCV0=$R>D z1yCz|QO=7M7CS<5W5E*pE6W2NU?K|cLYs;EF7GVXB6jB=SBKN)Z5(SH9e44?R~CT# zHE+fTjMH0N9k@~9L>3G#Y#oYvG(Xw#AA<1n{ezJzu7kV7pTcO$NuD^yQLVwU6_2KA zo9U zIuC+pigsW`vxjBx7~8mGzg9pgnFY6V`ojL9dcbp6;1gY7U@=>p0u^Erh0d5s74YGz znflx3DLQ+6{qiCEk$ppcP$*8#Hy>Iryz=9Sn?f7f!IowD$qS)Fu3MK_bV9k*o=Z_h ztx_Upkpdo89g~>44pJZ*Yew%veMp-kBqC1{XC~s-=n{~(Ydy z)6LM=pplRL;#b{@xy+8}^0s;z&iaq+$8oU;UySbU_-Q#p>bPlYEX-(N_>s5^uTED& zX-IT&WD$(H;KZQZJL3*dtmHH3uhX2TH1M!V`C<6_TfiK2pF^naP*m|Ykw5H(Z%SY0 zyX9cl!V@I=#{IVaxaUrfW8RnXKi860zL@9@tQJrSY6a{zj&1a`-2&c4nI5Ybk3721KXL$B# zmk`JS-)Po2FN-jGB0m`XBZC^f$HZo4F4O#VBS?w`>>VwwGGvlhlfL!7Mk|Bt1F*8l z5O84J#)du9{7J0+E~J)V5wcqI4P5pwa9t-p7Lm47HFMp9-8s$LUo0T`1c`^qL<<2) zLYSmhysvHKo;V~NCkw!#VfjQ@!54{pK%JboF1hyBetkF%w2W1p7GUe)h6`d`PC4uH zmxehGydU!1>-hv10JLM_WbV#;vPgRucDo>(%x-C=1-0u1@0rHz$28|ycExkz2qgWq z3xu6@(>)Hy`w({^+e^0|KY!#k{q~NIp1C{%F!OL*e-4nF;l+2`IuYwKVRdya zMaA#_Q$CNwHwdKT3m7@t@&O52Zc9IkJG=vQ$||smn5X`dee|}&nY!JE<rGIQ~@?&k%71^_U5og`}-q;8cgYf!UN1GC)$0wvTtU zW5KZEfVR6zP+a`z4wYTnDynEJT#yf79;@Ino+flo`OR?H%Xia|)!R55U@y~}3 zr6_~>#46eEphL&10uAYNdi{>#Yx_3A=F!o*! zHrvGw{2d;*vW6R6l82)(!&;!mZ3QDqH0yV4Bw5OAQYu z#ML=xPXIAlEM4Nt6eQPWn84e6w(^Fq^E7Aak?%P2e$4X&o@gg_)dL9C){CPji*DV4 zkm_!jeE4M3yMK~l7S3;UI=pkGHvT$8Dc$pBN12nT5Bt>d`cXx!4vQ(N_42P>^d;nqUW7uCP#Ww8cpId z9ED7ien1M8rkfbX=$^@%SPi2iAh@BPvE((PuoMxbiL(6;ZUCb4@Vq>Lwtxglx^!5U zlGtwDsZY(&$f9&t454({pbS;f)g*~mDTjMz-%uIE8H%WMNVIR1-Nr`DyOO2j3T#y( zhK>_k72wf^{pDdW&^qUP)Z5j4+-_Hxhj%l~_c}D*yJ5!l>zU1YXZ0%Jsmss{hCP zVH}lSLO1$&BQu@4zW>{~rP$ahe0P}?*DGrdobz8FR(zRO1~*(@((e^X&=f0_+BAy{ ziyJM=kOC$ZrO*<8`W-4*cY6FA4LM%IY7YgRws_+GML=_=PNH*N?|fda6U*$)XT~xM znYx%cDn&Yg72WtKiEq>Sq-MzWZc=~fDQ5a#`Y7~UIOd4$1lB1>m|T|xtw zYoE)&hL}EYe>6qpmad>2rY%9r2MlMHR)OilrA-Qgqe@`mmq5rG7rgq~tzRk|(sbQ5 zN#OojxaR2eejSAA5YiB+C?Makch~ehmm+W6wZbHG}=~o$n(XY9rBZ>r9 z4ULdSvqTDM$rt!A3#tLR3k;Mt86osujxI}WHrFXvaBO`wISNiTU!!t^>H87N2+%7~ zP(No@DV3zmDWxjhsyHoEl9;+m6+tCLq35`yC^%Fc!VHabB$@@^iTtY?cToz0O9{i*3{x$mzth5== zeF31scL?;~xXLLH4{Np$#Kb1EIrkMcU<=O=cTUF|`(pc`Cv*nBpXrONj1*c2S-~kF zcOXF=ZB@{_dz0!9AbS)4Mkdf!()PwEWO62cengg$R-6LF8PQ;{mn_xJ_r>f^dla$YT@_*FU$sxJ`m>zP^x@qX>)QiHRd2MrcoCP&%`g zE~=lK;5Jo6J1x)z+cC!RyUcHPq%-3C{Rb2;%5x3z)!j)itD&8!4sm z1yH$aQqTh0yV_Ko4bbD=jg!;K4T`~}vQ`mJND1qB!|Z-=ksI$W{QW6xD<`LLa<_26 zUKW{Rwh0nVp6*~T3&#QjDVnw!P1uZwbE{A>jY$!OiW5KtSd@*c3%T4{sDp1u1P}7{ zyE8<~g>3W$OzR;_b8Yo^{53~B@pr`kR$JbUIeGpeoZpl%EZ1_mbiPEd5mXgExdqjYgrrD%+}a{mYw|gQHf?iAKcfK+^DY7hFmfl{zg~#FyW$}_9kkDE5tMh z=W$%?>8+yh&b2vnE|=;-+rY%B#LfuBGc3CSz^f|BN9FVixpqw&O~hc%@ZwO&cMZ zLw!N6qkPL9mh%D1t6hB`b=>1b#9Qw#3aZmT&T+`1d3~`dF-Np76%?OTLCb*`sx%!=pl!puYOL4@M@4oD7(X`LfYSzK4t!N6LNst#i}Ml6Z`e9JApwZ?u$HL=(mQ} zrQxEst|4Tr=Vd@Q>^b*-t@_SFws{=olE{*??25j;lyTq$#gKD`WQ2mjSA%5>E>tji zpzU2L+rkkbDk(&tN{j`a1X`OO;6djUJNmvuUqs6>i?%@M_1|@zv(n#QHwm#)nB$j zGpGnL1#1F+2O+b-ndHEWhb7@n@luvhMsq-VmkQuuNbrv1L-IUec;Qa-Q&Fa+38Fr} zWKIdP@hT(~8^;?EFi!yUqLPV%Y3z^wYp7DNl@8~Aqb{7l!KxKW8taC=!&sutu9({Z z0#j~?D+=Kmp-w3$h%_21u&%yCLKHMK**T>!&#sN79req!gvNAf-*6H$f(MJAmX-o^ z);x_rvVkCpi42A@8|FRb#}0}lgnl}YiZ};tP$WIXFr8aJ>-n+1W51z&owh6xmO=~-V4vv%mnHl3 zhI9An!NHMm;F08>rW{B^s9IqJK}15(pcko}RQYi)Zw)EH`ahit7lLpv@44(cu)wKw zE5T2Mv}W!x{7t1_MwjJ6NR{WjZ-f&{rDUWWTBP7kwIcDsWu!?}Rvxn!DkMVZ*q$?1 z%z=Uf2rG1SNrR=gP2R1V$>ZfSd2eaj%X|%s@yau?{Ia(sc3~jXn-%($H4A5mT!su$ zsew43!6^H!7mcd=3*!VaOft)Vq z1p|CeF^^?jC<$XbScpU>K!6CN@1%O~@`Z2!I-46?yI`ALMR*%ktZ^s!j&#U0ME?YK zVAh^wN^zb9t^lQyhL~3$Jsg;lo+N6UK*=FMDc4C#0n&$JPlH`U&Et?GHFKV?9ALo} zLw{C2zTo=u3q}>AS*4G$hq1k;CPgiU0gpz3PK~hdpv*cn3q}g__)onjXQtV!OaM&) z1t@kJXkehwS;+a#M)nk+4q?^pxXNQ&aj{N*N~SHXXx5>?boR*$05ypwBqTWCghl2j z0EgyDE0H!L+&3$V3)?N@PMPs`O|ilGgC4=(j3=m94d<1PQ@iG?uIMlq@WTX!^n;Wx~LwO+%lxC1F&loVY-!TjO+mH{D~BY!UC*je5=XPXV2GUUJ>lrhR+^fL-1 zKo~N6%ph{e)JF_)m`r$NCA{Io9y6tOFPR8dA7^SVC+qK@aq2V@V~N@F~< zQawH-5=-mtNK0mhS_mr<=h<^szV6Vrb)Af#+F!)d-;iyP7e8gM6ZHnpia}2~lpi5P zqX0nlq=vn63c2u>O_)#4=GzdFG*)pCX2wKp>%b%ZO8ZmLf6fo8=VvmJkt)R*sY@&3 z7ZRVD31;P_)Uu7_#1^FYAv?eqX#*nu{^l@3oqRh>Z4zmM<;)O|f0;6o51Z$MiEelguN5 z1k8z-;$;>Wbp;0$des#Zv*TN8&w_PU&FE6O4&WaQjeA9cfc9;nH&=PyCchA)-K71djkH?D16t0HRLk`Qrv)p6hnoo z$ZqCcBzO%=(->lUGm3ukBr7O^-JAwYToit2=+HyRUI7(M)#A$Dd9T zNwV2}T5{{dU_1-(OeGRgJ2OyQ!**T5KEI`Dnje6-6vJvCul*VrVc;F2w_llH1R&BH z0;Os=c6M5r8OO_&?_q^O4;?HP7Ke=5 z1gfEqfR+zO4irkYh~=^BZrUW%mY3)w-Jv(W6lWBVAjyG*ejmvRs$hy1r8(YJp)U6L zf3c5)o-e}n0X&D_9*7ei=`~DqJuWUSu|1!m9`b%9{+5hbN^-#m9P-Qutm3w1JZkPD zg4@gBbgX+iwTcP`ElMiEgXkWl#r4b_9;wPg?NgE!DP z=D7KDCkIl&1vjzvv@SB_r^q|Yh4E#R*l?#v&XXtX|AjkvklgU%EAy`Ezz_Z&Nn@`z zhCAL^yPuPl_WPP&(Vn)X(2M;9joP6qF9?+jf5@IIDg|ZXP<$!$Lt-?dWh@Yd{c}Yn z0VBSLi4hRSz#BmuM&=`Syi-m$0EXre!$&gM!tGU`MGXJR|LnYG!4SmIC3d?a{c@*( zzt<43Ai3R6^yOkZq}$9@w9{XeoEkK-nVdm=JTF>0UsU}kR6v<`^rTswqtGL1SImtN zR=7xr`dtp1_0{L;s9BKABc|rA*t@YPGo{yx(f)c(;)yu4PLR`6zYfw37jlXDXd>J= zZ#tI`Zx_MXc|E>-G)O7~m2ZfQi)6}I{Z(9p26zb@V1Tm^%E=>q1UnPtmGb1uIY zY4@b~0;%rG*`-&G&VnpmTU12m9V%iETsXYhmBALNpXz4tJ1hOlDop4z1?lQSy?t%D zBDdY-RzWhu$;*oUNhkpg6Anl+h=(H01A64ofQ{jJnYJ;9{tQ;RTEO`bmp_9OKmg;v zu0OS{bYJ~15#U7hZ4WtTMfI%FEIG`FV_#4m&rX419tb2J2TtAJQLg#xsBlgh2p= zZQmwr`XRQbKBSNO7;l~J#Y4- zKW+Cuca=W$t-8H7@l4teXV4S15I3wg0O)64>YwU37$(VIXCaF>!V5qlQ9}!0f}pc2 zREi{vBR$3|eiY06p&_b3(TjhSnjR@*3OM`-CF}-~FM|tuOsV9|@n~f+Qhr_|I1YgfNQzZf=>wL(( zy%@Wrt&)6W8s+-FrV@;&%VOwBs4P-7Kcba*xzUeDZnx@-bc5E{n;9U!1 zThg)3iL9P!?US<2cyKxV%zT1mip$I|(e!d@v@+P&6a{>KJq$gJJUro-gj%K-d;<3w zb~@G_ylvfrB~nl1^qB_@He=~^AN?c?Rf>rnQt&2O;g>tsGXh_44`9J)6cY|vg21rn zpU_XuHp7^pY5(AwJLfX^3;8>ilj=3b4FYgV1JUEoDya63f~BO(dBcha0}lE0WkAM` zDGb63sGoN`!*bDf^o(S(cOZX{fjfvZK*KQ!*E7mrBUVS)c$&_51<^ALd`~Hf83VcQ zPwsqK!hdu>IZhJcoO5T+UcG<{EC$Z6l9n66NN;MJb|S&?3ed895ZY=ou6EKPDzQF) zPVB#RI`h;f{pK~1#IBE~DZTu04>x(Vi4e%bgzE?o`HMs*4LZV`>||H^_OzuhfA+cj z4ln!NMcP+9oo(-w+?)9f;7wR$ZJ!!*3?&~o_L#9|K`{4)Gs}8HAKN(w_vvAW?HA{E zl3FXzEUzT*@a+J;18h$a3oD*r(^KTh!SX?J)B0oADVKCOX&U)p;N9cqV&8GA;SHzQ z7t{UF%t~bs$2G(0FW-2E^8UE2fRw(K^4c_57!csenQNK9*eO3Uppku#O~2LW`ro&r#?9AtXCa$N0-Xi^DEaOn)z!@lH2 zd{AeN-l?^8nzXs+oG+?ElHY8hY)$rf{*M#W?m6|edrUlQf{j2)e!(a1joZ6vjG64h zTD)u&#?TV6 zfU$*u3%`Nx0C+yZVUD*?UNoCqi4wtP2t@=`ZS%C<1Y_VF%$y^_F}?tZnCoyD5EYo!#^7j_X>$o}CNU55se*AD#16l`Wac@}yg@4M&lUcx1Fb zeo|bxR){oyfv#6zz#@Sqns!RK^+2Flw?#s=77Vz|?cmdt^excRvO3)HTyq0Ojygrx40uU^?G`Kt42f5?D}Ne> zBQ63(7)FxhwlCwXH0g{H$${6PsXXau&$vng^s3p9rO4vStqFA@I7eB__^}Dvt$q;9 z)yk9o3L~}=hO=Ex8t75drjrCqnn@XN(rie)qAO=zU7cmx3;V^RRq1k+H9_B_wgf+= z2OLRHbj~^^9PMCpqCP0H;#&1q#N5uS7&P@NrA4L1>fhdHsc$$-n;5kFxu=1rp{J+r zKEeaPjY`6^ry+0GDTP+N0sW|t%}VaS+1g5}Ruq>;s?v@bI2e|g=e>X!VeTBlvfM^q zt@de2(V6}h)VdpEuDf)b<%_3(tsO>_jbrXML?B)}LX*QCfg9o@3WbGWnr>~PI+8@# zze$2`4&TvtSA8hhP%%>F;AF_bcN2zcqI6)Sp-ee301qUqC%iD^jaxH7H+{qn>|q21 z0O;ew9cG>y0u0n(c32n`Fa}h}c@Xxid|wD}UD!EWa@OhTBcv;3 zU3VM3%Wb%D)`Y1+0{gn)e&m@FhpNgyp%yx<-(D91mLe*PHS1b^i5LD-$Ti2VMq0tV zhh31R_hW`R!YD7`EegRhQ{O=>e%I<9a^^NvVl!G`Gydb#PAq-{wY#dAqkF0gr|YL_ zYN*j*dx{$VY*0mbD-ojx#>5%ri5%I*^)jNfh6lAb(s)*9;ueQ>i?&yR2p});T@dgW z!)Z&rf4g-XQ5t%7Ld?xMd<4 z&7+>+Tdl9ayr~m!injG6izqA~iJlart^UFvt(cx{0)H5X1`n`>?1x1j4E-;Tfwa z&29DXR=;XegZokfjEa?ZzLm;?!FGFak-pOuSYIx4hY(P^UPGMTem2UR`?smPrgaF(5-#P#4Vbl+n-Q+=8Lnl z$SMtO*G2;(AKre|kuQuwLI=2-p6iRx`+`VG;;lqnCn_D^j)n>fzg+|0I*UE+XJ(dx z1i`0v5{w9yA8;xAt^$_uv>C@up^BGS-Svgr<)XbOd*06;Ffd}V?c6GJ>(n6!I+yJU zTQ~Fd_54TY>wgnAf+5J5!7uN(M;UyZf7CnJ3@6J)iQBgOh3gqaQs+=rTu+b`P-)Ue zYeO>3aamH5d?V56?Gi`vDxxvD;Uz1yN6x3 z4d1IX7p3A&T76e=mrW*;b9;nB7W7KEK(}#a%PJjZ9D-@ghPB=F?UNIO59Xe}! zB{cQ&jQ3iQG&;{EY+E?-Fs zMKHZYz&PB9^8^AjR{iOvG9U=z?u7b3CQDCTyI<)Y0owSQ^E*ya2 z(%X++G~H2fAE0Y%@${YINI;sp_{hA=C$0m@4v6w6n43ka6HU~u1h*6^Vx1xz$u zw7jb)Zr6ge5z0u?pt&cet{tfc=DYKW6Z3bh`wRu}%{tMn;`pq5%2^ST{ z^lxk9+4@F9TkiIZ&rPXEP>lEOzJF?15dI3q_ z>k~X8if&<)bw;SeE<4A$Q%$=|4F*$D9^bSpr)S8!fip<7t>x<*7+p8R6EjrJr79MR z)Qg)D?Idi*2+CBqDDS5qc-`>3+uO>%dtqzdXOKg0+Fru&HTZ0ydFhR%9yty|#)Gx3 z7L!%L`$NC9TQ5og=`2_$eF5QXEO!)e=RG#9ijU>b(LvhZH1M;VkLe=fQW3u}fUb&3%FEC3NnPWBcUi6@h{}&3b zPMjET-E;h|tK*t6^45xqkw;8t;1PM(qDR?0hZd@uKk|FYg99aBf*5t*Hw=PUy5X_k zx6rS0%v>)t#BKZ%?730;u2v}SDFx?zUm7BM8+wM{!b{pr*cVOiSiJ99W0x~V0xSkN zN-^AH`~IKbkdB@7NMJ8__jIO#WAQ!&xkiWuaKwTP>flpZeqv?NH#~d$y}Dl6g6mxl zFnz8~d~!`R*2m7pFCpp%R4KEm?haX2dR5V>c!t7|*j|P{T?HU8+Xnc_-k6S7j};fk zfF3(WW+~VxPu-SJ5%(N@ZYY7yh^cA|v0ILTei?Sz5r9*-i@i1eZj&l0Jv4*ERn*Gq z_X5hTI@05BEd42m;l=S*d)~T-oYYk;kFR!4YM_7U^~xwEq{RTc>N^}uNgC!|ox;{w z>8x8+4+ZjMNPAUv4`onEF5~g<6&Fl=NrW#F0Q@Dv$lA1rQ4dSrm((|FNLwbhZaX#g z!x$sxasEaqpxyE0hd)V?(^Chp7@4tA{6+n<^T7?87H8cG_Gw>4_pQH;!FIB}G{O|G zuCGWF@3*6n%WCL$i zRq|2*mBTxJcaUz4$H^P03ur53ngAfKiH4K~Gr;L_vYYzmt?S`~VxWG6XjZq-PpNW+ z9(H64>DTj~vmrDWlQmOv{4YYi$4E2juc`CZdT5z#1q2&}dFs=kJ^Kc?2De7HCO38> z9A^KhZ-loMQj-uu#xw$A#^H}#1E22Mz@ztKAT$7?zs@6w7wk`>1|}1X^f3m3Bm2rK z0c(Y+TpnHa8-01boD9E?TiL7aLL9<6ob%$Z8ac?mR_`o9y5Ol`ye^KsWXmXVh3jbS zcE+UIWQZl+>|gV zLMA2-EG~^MO)kwYEd)MJzd2*3He-~B5B)*{mo#_7#+_NNOWWO%g;20ro-Ut>b`PxX zlHCVLu^EW}r2vexQ}m5rKZ|%nx4#uSru{N+f9t4hD%f>AEk{+9SC!SLaysSxOZ1x_ zj@fA~=A|MfV_e8dC#LvpKhRhYWp3<6j5SV7fp(MUF}J4~^68wB%50`5?CM=Ir`vbG zJ*Y0Sm}}-(ZerD#RfFq4}?^cavS9`#I5ljLH~NrWVKtqsV_Kwm5+)FsQeq+c|A+$ zgLW65)<;eV!VmNAGog*f`$I+cdgkcvQ5bu3rMh}M?{9}9g z_g0AA7`4q_^}`$Pq!3Xh&bg&w#&0kowfX$7Zw-+qvp14AQ*!Xc<59lYwh~0rq9TLz(iF2SGziyUC~XwA SNxgs4ATkn);&q}%LH`e8$k9yz literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/modelparticles/bubbles.qml b/examples/declarative/particles/modelparticles/bubbles.qml index 80d03a9ea7..d0eb3ea044 100644 --- a/examples/declarative/particles/modelparticles/bubbles.qml +++ b/examples/declarative/particles/modelparticles/bubbles.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 import "../../modelviews/listview/content" as OtherDemo import "content/script.js" as Script import "content" @@ -56,21 +56,22 @@ Item{ ParticleSystem{ id: sys; } - TrailEmitter{ + Emitter{ system: sys particle: "A" width: parent.width/2 x: parent.width/4 y:parent.height - speed: PointVector{ y: -64; yVariation: 16 } + speed: PointDirection{ y: -64; yVariation: 16 } particlesPerSecond: 1 particleDuration: 8000 } - Drift{ + Wander{ system: sys - xDrift: 200 + xVariance: 400 + pace: 200 } - DataParticle{ + ModelParticle{ id: mp z: 0 system: sys diff --git a/examples/declarative/particles/modelparticles/gridsplosion.qml b/examples/declarative/particles/modelparticles/gridsplosion.qml index d45ef392e0..fe2dd261a5 100644 --- a/examples/declarative/particles/modelparticles/gridsplosion.qml +++ b/examples/declarative/particles/modelparticles/gridsplosion.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 import "content" Item{ @@ -48,13 +48,14 @@ Item{ height: 240 property bool inGrid: false ParticleSystem{ id: sys } - TrailEmitter{ + Emitter{ system: sys id: burster; emitting: false particlesPerSecond: 1000 - particleDuration: 500 - speed: PointVector{xVariation: 400; yVariation: 400} + particleDuration: 50000 + maxParticles: 100; + speed: PointDirection{xVariation: 400; yVariation: 400} anchors.centerIn: parent Timer{ interval: 1000 @@ -69,7 +70,7 @@ Item{ onTriggered: {inGrid = true;}// sys.running = false;} } } - ColoredParticle{ + ImageParticle{ system: sys image: "../trails/content/particle.png" color: "black" @@ -80,17 +81,14 @@ Item{ width: 120 height: 120 } - DataParticle{ + ModelParticle{ system: sys model: theModel.parts.particles + fade: false } Friction{ system: sys - factor: 1 - } - Stasis{ - system: sys - targetLife: 400 + factor: 5 } VisualDataModel{ id: theModel diff --git a/examples/declarative/particles/modelparticles/package.qml b/examples/declarative/particles/modelparticles/package.qml index d5c104b480..64873802a5 100644 --- a/examples/declarative/particles/modelparticles/package.qml +++ b/examples/declarative/particles/modelparticles/package.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 import "content" Rectangle { @@ -69,7 +69,7 @@ Rectangle { width: 200; height:200 model: visualModel.parts.list } - DataParticle{ + ModelParticle{ x: 200; width: 200; height:200 model: visualModel.parts.grid system: sys @@ -80,11 +80,11 @@ Rectangle { id: sys anchors.fill: parent } - TrailEmitter{ + Emitter{ system: sys width: 100 x: 50 - speed: PointVector{ y: 40 } + speed: PointDirection{ y: 40 } particleDuration: 5000 particlesPerSecond: 1.6 } diff --git a/examples/declarative/particles/modelparticles/stream.qml b/examples/declarative/particles/modelparticles/stream.qml index 0ad807bc7b..73107ad4f7 100644 --- a/examples/declarative/particles/modelparticles/stream.qml +++ b/examples/declarative/particles/modelparticles/stream.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 import "content/script.js" as Script import "content" @@ -68,13 +68,13 @@ Item{ overwrite: false startTime: 12000//Doesn't actually work with the loading time though... } - TrailEmitter{ + Emitter{ id: emitter system: sys height: parent.height - 132/2 x: -132/2 y: 132/2 - speed: PointVector{ x: 32; xVariation: 8 } + speed: PointDirection{ x: 32; xVariation: 8 } particlesPerSecond: 0.5 particleDuration: 120000 //TODO: A -1 or something which does 'infinite'? (but need disable fade first) particle: "photos" @@ -85,7 +85,7 @@ Item{ height: parent.height width: 1000 } - ColoredParticle{ + ImageParticle{ system: sys particles: ["fireworks"] image: "../trails/content/star.png" @@ -125,7 +125,7 @@ Item{ } property Item alertItem; function alert(){ - resetter.active = false + //resetter.active = false force.active = true; alertItem = alertDelegate.createObject(root); alertItem.x = root.width/2 - alertItem.width/2 @@ -142,31 +142,27 @@ Item{ interval: 800 onTriggered: { force.active = false - resetter.active = true; + //resetter.active = true; mp.take(alertItem, true); centerEmitter.burst(1); } } - Attractor{ + PointAttractor{ id: force system: sys x: root.width/2 y: root.height/2 - strength: -30000 + strength: -10000 active: false anchors.centerIn: parent width: parent.width/2 height: parent.height/2 particles:["photos"] + physics: PointAttractor.Position } - Reset{ - id: resetter - system: sys - particles:["photos"] - } - TrailEmitter{ + Emitter{ id: centerEmitter - speed: PointVector{ x: 32; xVariation: 8;} + speed: PointDirection{ x: 32; xVariation: 8;} particlesPerSecond: 0.5 particleDuration: 12000 //TODO: A -1 or something which does 'infinite'? (but need disable fade first) maxParticles: 20 @@ -177,7 +173,7 @@ Item{ //TODO: Zoom in effect } - TrailEmitter{ + Emitter{ id: spawnFireworks particle: "fireworks" system: sys @@ -191,8 +187,8 @@ Item{ emitting: false particleSize: 32 particleEndSize: 8 - speed: AngleVector{ magnitude: 160; magnitudeVariation: 120; angleVariation: 90; angle: 270 } - acceleration: PointVector{ y: 160 } + speed: AngledDirection{ magnitude: 160; magnitudeVariation: 120; angleVariation: 90; angle: 270 } + acceleration: PointDirection{ y: 160 } } Item{ x: -1000; y: -1000 //offscreen Repeater{//Load them here, add to system on completed diff --git a/examples/declarative/particles/snow/snow2.qml b/examples/declarative/particles/snow/content/Button.qml similarity index 72% rename from examples/declarative/particles/snow/snow2.qml rename to examples/declarative/particles/snow/content/Button.qml index c016ba2934..a937b3bede 100644 --- a/examples/declarative/particles/snow/snow2.qml +++ b/examples/declarative/particles/snow/content/Button.qml @@ -39,36 +39,35 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 -Rectangle{ - width: 360 - height: 540 - ParticleSystem{ id: particles } - SpriteParticle{ - system: particles - Sprite{ - name: "snow" - source: "content/flake-01.png" - frames: 51 - duration: 40 +Rectangle { + id: container + + property string text: "Button" + signal clicked + + width: buttonLabel.width + 20; height: buttonLabel.height + 20 + smooth: true + property color myCol: "#999999" + border { width: 1; color: Qt.darker(myCol) } + radius: 8 + + gradient: Gradient { + GradientStop { + position: 0.0 + color: { + if (mouseArea.pressed) + return Qt.darker(myCol) + else + return Qt.lighter(myCol) + } } + GradientStop { position: 1.0; color: myCol } } - Drift{ - system: particles - anchors.fill: parent - xDrift: 400; - } - TrailEmitter{ - system: particles - particlesPerSecond: 20 - particleDuration: 7000 - emitting: true - speed: PointVector{ y:80; yVariation: 40; } - acceleration: PointVector{ y: 4 } - particleSize: 20 - particleSizeVariation: 10 - width: parent.width - height: 100 + + MouseArea { id: mouseArea; anchors.fill: parent; onClicked: container.clicked() } + + Text { + id: buttonLabel; text: container.text; anchors.centerIn: container; color: "black"; font.pixelSize: 24 } } diff --git a/examples/declarative/particles/snow/snow.qml b/examples/declarative/particles/snow/snow.qml index 25d2e1468b..2be2438f1d 100644 --- a/examples/declarative/particles/snow/snow.qml +++ b/examples/declarative/particles/snow/snow.qml @@ -39,15 +39,16 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 +import "content" Rectangle{ width: 360 height: 540 ParticleSystem { id: particles } - SpriteParticle { + ImageParticle { system: particles - Sprite{ + sprites: Sprite{ name: "snow" source: "content/flake-01.png" frames: 51 @@ -55,21 +56,38 @@ Rectangle{ } } Wander { + id: wanderer system: particles anchors.fill: parent - xVariance: 40; - pace: 40; + xVariance: 360/(wanderer.physics+1); + pace: 100*(wanderer.physics+1); } - TrailEmitter { + Emitter { system: particles particlesPerSecond: 20 particleDuration: 7000 emitting: true - speed: PointVector{ y:80; yVariation: 40; } - acceleration: PointVector{ y: 4 } + speed: PointDirection{ y:80; yVariation: 40; } + acceleration: PointDirection{ y: 4 } particleSize: 20 particleSizeVariation: 10 width: parent.width height: 100 } + Row{ + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + Button{ + text:"dx/dt" + onClicked: wanderer.physics = Wander.Position; + } + Button{ + text:"dv/dt" + onClicked: wanderer.physics = Wander.Velocity; + } + Button{ + text:"da/dt" + onClicked: wanderer.physics = Wander.Acceleration; + } + } } diff --git a/examples/declarative/particles/snow/snow3.qml b/examples/declarative/particles/snow/snow3.qml deleted file mode 100644 index 080bc4d1af..0000000000 --- a/examples/declarative/particles/snow/snow3.qml +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import Qt.labs.particles 2.0 - -Rectangle{ - width: 360 - height: 540 - id: root - ParticleSystem{ id: particles } - SpriteParticle{ - system: particles - sprites: Sprite{ - name: "snow" - source: "content/flake-01.png" - frames: 51 - duration: 40 - } - } - Drift{ - system: particles - anchors.fill: parent - xDrift: 200 - } - SpeedLimit{ - system: particles - anchors.fill: parent - speedLimit: 100 - } - TrailEmitter{ - system: particles - particlesPerSecond: 20 - particleDuration: 7000 - emitting: true - speed: PointVector{ y:80; yVariation: 40; } - acceleration: PointVector{ y: 4 } - particleSize: 20 - particleSizeVariation: 10 - width: parent.width - height: 40 - } -} diff --git a/examples/declarative/particles/spaceexplorer/spaceexplorer.qml b/examples/declarative/particles/spaceexplorer/spaceexplorer.qml index 091ca0a8b5..f303bb4d9a 100644 --- a/examples/declarative/particles/spaceexplorer/spaceexplorer.qml +++ b/examples/declarative/particles/spaceexplorer/spaceexplorer.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 import "content/helpers.js" as Helpers Rectangle{ @@ -75,7 +75,7 @@ Rectangle{ property bool fakeMoving: false property real fakeMovementDir: 0 - TrailEmitter{ + Emitter{ particle: "stars2" system: background particlesPerSecond: 60 @@ -86,7 +86,7 @@ Rectangle{ anchors.fill: parent } ParticleSystem{ id: background } - ColoredParticle{ + ImageParticle{ particles: ["stars2"] system: background anchors.fill: parent @@ -197,7 +197,7 @@ Rectangle{ ParticleSystem{ id: foreground } - ColoredParticle{ + ImageParticle{ particles: ["stars"] anchors.fill: parent system: foreground @@ -205,7 +205,7 @@ Rectangle{ color: "white" colorVariation: 0.1 } - ColoredParticle{ + ImageParticle{ particles: ["shot"] anchors.fill: parent system: foreground @@ -214,7 +214,7 @@ Rectangle{ color: "orange" colorVariation: 0.3 } - ColoredParticle{ + ImageParticle{ id: engine particles: ["engine"] anchors.fill: parent @@ -238,30 +238,31 @@ Rectangle{ colorVariation: 0.2 } - SpriteParticle{ + ImageParticle{ particles: ["powerups"] anchors.fill: parent system: foreground - Sprite{ + sprites:[Sprite{ name: "norm" source: "content/powerupScore.png" frames: 35 duration: 40 to: {"norm":1, "got":0} - } + }, Sprite{ name: "got" source: "content/powerupScore_got.png" frames: 22 duration: 40 to: {"null":1} - } + }, Sprite{ name: "null" source: "content/powerupScore_gone.png" frames: 1 duration: 1000 } + ] } SpriteGoal{ x: rocket.x - 30 @@ -273,8 +274,9 @@ Rectangle{ onAffected: if(!gameOver) score += 1000 system: foreground } - GravitationalSingularity{ + PointAttractor{ id: gs1; x: vorteX; y: vorteY; strength: 800000; + proportionalToDistance: PointAttractor.Quadratic; system: foreground } Kill{ @@ -285,8 +287,9 @@ Rectangle{ system: foreground } - GravitationalSingularity{ + PointAttractor{ id: gs2; x: vorteX2; y: vorteY2; strength: 800000; + proportionalToDistance: PointAttractor.Quadratic; system: foreground } Kill{ @@ -297,8 +300,9 @@ Rectangle{ system: foreground } - GravitationalSingularity{ + PointAttractor{ id: gs3; x: vorteX3; y: vorteY3; strength: 800000; + proportionalToDistance: PointAttractor.Quadratic; system: foreground } Kill{ @@ -308,8 +312,9 @@ Rectangle{ height: holeSize * 2 system: foreground } - GravitationalSingularity{ + PointAttractor{ id: gs4; x: vorteX4; y: vorteY4; strength: 800000; + proportionalToDistance: PointAttractor.Quadratic; system: foreground } Kill{ @@ -319,7 +324,7 @@ Rectangle{ height: holeSize * 2 system: foreground } - TrailEmitter{ + Emitter{ particle: "powerups" system: foreground particlesPerSecond: 1 @@ -329,7 +334,7 @@ Rectangle{ particleSizeVariation: 10 anchors.fill: parent } - TrailEmitter{ + Emitter{ particle: "stars" system: foreground particlesPerSecond: 40 @@ -374,7 +379,7 @@ Rectangle{ drag.axis: Drag.XandYAxis drag.target: rocket }, - TrailEmitter{ + Emitter{ system: foreground particle: "engine" particlesPerSecond: 100 @@ -383,7 +388,7 @@ Rectangle{ particleSize: 10 particleEndSize: 4 particleSizeVariation: 4 - speed: PointVector{ + speed: PointDirection{ x: -128 * Math.cos(rocket.rotation * (Math.PI / 180)) y: -128 * Math.sin(rocket.rotation * (Math.PI / 180)) } @@ -392,14 +397,14 @@ Rectangle{ width: 4 }, - TrailEmitter{ + Emitter{ system: foreground particle: "shot" particlesPerSecond: 16 particleDuration: 1600 emitting: !gameOver && shoot particleSize: 40 - speed: PointVector{ + speed: PointDirection{ x: 256 * Math.cos(rocket.rotation * (Math.PI / 180)) y: 256 * Math.sin(rocket.rotation * (Math.PI / 180)) } diff --git a/examples/declarative/particles/trails/dynamicemitters.qml b/examples/declarative/particles/trails/dynamicemitters.qml index 8ea0272d94..8e555033a0 100644 --- a/examples/declarative/particles/trails/dynamicemitters.qml +++ b/examples/declarative/particles/trails/dynamicemitters.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ id: root @@ -49,7 +49,7 @@ Rectangle{ ParticleSystem{ id: sys } - ColoredParticle{ + ImageParticle{ system: sys image: "content/particle.png" color: "white" @@ -58,9 +58,9 @@ Rectangle{ } Component{ id: emitterComp - TrailEmitter{ + Emitter{ id: container - TrailEmitter{ + Emitter{ id: emitMore system: sys emitting: true @@ -68,7 +68,7 @@ Rectangle{ particleDuration: 600 particleSize: 16 particleEndSize: 8 - speed: AngleVector{angleVariation:360; magnitude: 60} + speed: AngledDirection{angleVariation:360; magnitude: 60} } property int life: 2600 diff --git a/examples/declarative/particles/trails/fireballs.qml b/examples/declarative/particles/trails/fireballs.qml index 116a2334dc..cd81168d81 100644 --- a/examples/declarative/particles/trails/fireballs.qml +++ b/examples/declarative/particles/trails/fireballs.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle { id: root @@ -52,7 +52,7 @@ Rectangle { } /* - ColoredParticle{ + ImageParticle{ id: fireball anchors.fill: parent particles: ["E"] @@ -62,7 +62,7 @@ Rectangle { color: "#00ff400f" } */ - ColoredParticle{ + ImageParticle{ id: smoke system: particles anchors.fill: parent @@ -71,7 +71,7 @@ Rectangle { colorVariation: 0 color: "#00111111" } - ColoredParticle{ + ImageParticle{ id: flame anchors.fill: parent system: particles @@ -80,7 +80,7 @@ Rectangle { colorVariation: 0.1 color: "#00ff400f" } - TrailEmitter{ + Emitter{ id: fire system: particles particle: "C" @@ -91,8 +91,8 @@ Rectangle { particlesPerSecond: 350 particleDuration: 3500 - acceleration: PointVector{ y: -17; xVariation: 3 } - speed: PointVector{xVariation: 3} + acceleration: PointDirection{ y: -17; xVariation: 3 } + speed: PointDirection{xVariation: 3} particleSize: 24 particleSizeVariation: 8 @@ -109,8 +109,8 @@ Rectangle { particlesPerParticlePerSecond: 1 particleDuration: 2000 - speed: PointVector{y:-17*6; yVariation: -17; xVariation: 3} - acceleration: PointVector{xVariation: 3} + speed: PointDirection{y:-17*6; yVariation: -17; xVariation: 3} + acceleration: PointDirection{xVariation: 3} particleSize: 36 particleSizeVariation: 8 @@ -145,14 +145,14 @@ Rectangle { emissionWidth: 16 emissionHeight: 16 - speed: PointVector{yVariation: 16; xVariation: 16} - acceleration: PointVector{y: -16} + speed: PointDirection{yVariation: 16; xVariation: 16} + acceleration: PointDirection{y: -16} particleSize: 24 particleSizeVariation: 8 particleEndSize: 8 } - TrailEmitter{ + Emitter{ id: balls system: particles particle: "E" @@ -163,8 +163,8 @@ Rectangle { particlesPerSecond: 2 particleDuration: 7000 - speed: PointVector{y:-17*4*2; xVariation: 6*6} - acceleration: PointVector{y: 17*2; xVariation: 6*6} + speed: PointDirection{y:-17*4*2; xVariation: 6*6} + acceleration: PointDirection{y: 17*2; xVariation: 6*6} particleSize: 12 particleSizeVariation: 4 diff --git a/examples/declarative/particles/trails/layered.qml b/examples/declarative/particles/trails/layered.qml index 38eb8e67dc..b2895dd617 100644 --- a/examples/declarative/particles/trails/layered.qml +++ b/examples/declarative/particles/trails/layered.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ id: root @@ -55,30 +55,26 @@ Rectangle{ id: sys startTime: 4000 } - TrailEmitter{ + Emitter{ system: sys y:root.height + 20 width: root.width particlesPerSecond: 200 particleDuration: 4000 - speed: PointVector{ y: -120; } + speed: PointDirection{ y: -120; } } - SpriteParticle{ + ImageParticle{ system: sys visible: !cloneMode - Sprite{ - source: "content/particle2.png" - } + image: "content/particle2.png" } - SpriteParticle{ + ImageParticle{ system: sys visible: cloneMode z: 0 - Sprite{ - source: "content/particle3.png" - } + image: "content/particle3.png" } - SpriteParticle{ + ImageParticle{ system: sys clip: true visible: cloneMode @@ -86,8 +82,6 @@ Rectangle{ height: 240 width: root.width z: 1 - Sprite{ - source: "content/particle.png" - } + image: "content/particle.png" } } diff --git a/examples/declarative/particles/trails/list.qml b/examples/declarative/particles/trails/list.qml index 2ab579f126..7874590e28 100644 --- a/examples/declarative/particles/trails/list.qml +++ b/examples/declarative/particles/trails/list.qml @@ -43,14 +43,14 @@ // highlight bar is moved between items. + Particles. import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 import "content" Rectangle { width: 200; height: 300 color: "black" ParticleSystem{ id: particles } - ColoredParticle{ + ImageParticle{ anchors.fill: parent system: particles z: 10 @@ -92,7 +92,7 @@ Rectangle { y: listView.currentItem.y; //Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } } Behavior on y { NumberAnimation {id: anim} } - TrailEmitter{ + Emitter{ anchors.fill: parent system: particles; emitting: anim.running diff --git a/examples/declarative/particles/trails/overburst.qml b/examples/declarative/particles/trails/overburst.qml index 6ca15972a4..ed9313d471 100644 --- a/examples/declarative/particles/trails/overburst.qml +++ b/examples/declarative/particles/trails/overburst.qml @@ -39,21 +39,21 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ color: "black" width: 360 height: 540 ParticleSystem{ id: sys } - ColoredParticle{ + ImageParticle{ system: sys id: cp image: "content/particle.png" colorVariation: 0.4 color: "#000000FF" } - TrailEmitter{ + Emitter{ //burst on click id: bursty system: sys @@ -63,7 +63,7 @@ Rectangle{ particlesPerSecond: 16000 particleDuration: 1000 maxParticles: 4000 - acceleration: AngleVector{angleVariation: 360; magnitude: 360; } + acceleration: AngledDirection{angleVariation: 360; magnitude: 360; } particleSize: 8 particleEndSize: 16 particleSizeVariation: 4 diff --git a/examples/declarative/particles/trails/portal.qml b/examples/declarative/particles/trails/portal.qml index dba2e59513..ae9b447fb9 100644 --- a/examples/declarative/particles/trails/portal.qml +++ b/examples/declarative/particles/trails/portal.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ id: root @@ -54,7 +54,7 @@ Rectangle{ id: particles startTime: 2000 } - ColoredParticle{ + ImageParticle{ particles: ["center","edge"] anchors.fill: parent system: particles @@ -62,7 +62,7 @@ Rectangle{ colorVariation: 0.1 color: "#009999FF" } - TrailEmitter{ + Emitter{ anchors.fill: parent particle: "center" system: particles @@ -72,15 +72,15 @@ Rectangle{ particleSize: 20 particleSizeVariation: 2 particleEndSize: 0 - shape: Ellipse{fill: false} - speed: DirectedVector{ + shape: EllipseShape{fill: false} + speed: TargetedDirection{ targetX: root.width/2 targetY: root.height/2 proportionalMagnitude: true magnitude: 0.5 } } - TrailEmitter{ + Emitter{ anchors.fill: parent particle: "edge" system: particles @@ -90,15 +90,15 @@ Rectangle{ particleSize: 20 particleSizeVariation: 2 particleEndSize: 0 - shape: Ellipse{fill: false} - speed: DirectedVector{ + shape: EllipseShape{fill: false} + speed: TargetedDirection{ targetX: root.width/2 targetY: root.height/2 proportionalMagnitude: true magnitude: 0.1 magnitudeVariation: 0.1 } - acceleration: DirectedVector{ + acceleration: TargetedDirection{ targetX: root.width/2 targetY: root.height/2 targetVariation: 200 diff --git a/examples/declarative/particles/trails/rainbow.qml b/examples/declarative/particles/trails/rainbow.qml index 6c64929668..543a9b6182 100644 --- a/examples/declarative/particles/trails/rainbow.qml +++ b/examples/declarative/particles/trails/rainbow.qml @@ -38,7 +38,7 @@ ** ****************************************************************************/ -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 import QtQuick 2.0 Rectangle { @@ -48,7 +48,7 @@ Rectangle { color: "black" ParticleSystem{ id: particles } - ColoredParticle{ + ImageParticle{ system: particles colorVariation: 0.5 alpha: 0 @@ -57,7 +57,7 @@ Rectangle { colorTable: "content/colortable.png" sizeTable: "content/colortable.png" } - TrailEmitter{ + Emitter{ system: particles particlesPerSecond: 500 particleDuration: 2000 @@ -72,8 +72,8 @@ Rectangle { speedFromMovement: 20 - speed: PointVector{ xVariation: 5; yVariation: 5;} - acceleration: PointVector{ xVariation: 5; yVariation: 5;} + speed: PointDirection{ xVariation: 5; yVariation: 5;} + acceleration: PointDirection{ xVariation: 5; yVariation: 5;} particleSize: 16 //particleEndSize: 8 diff --git a/examples/declarative/particles/trails/shimmer.qml b/examples/declarative/particles/trails/shimmer.qml index 06f599d97a..b3157f68cd 100644 --- a/examples/declarative/particles/trails/shimmer.qml +++ b/examples/declarative/particles/trails/shimmer.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ width: 360 @@ -53,7 +53,7 @@ Rectangle{ id: particles running: false } - ColoredParticle{ + ImageParticle{ anchors.fill: parent system: particles image: "content/star.png" @@ -61,7 +61,7 @@ Rectangle{ alpha: 0 colorVariation: 0.6 } - TrailEmitter{ + Emitter{ anchors.fill: parent system: particles particlesPerSecond: 2000 diff --git a/examples/declarative/particles/trails/swarm.qml b/examples/declarative/particles/trails/swarm.qml deleted file mode 100644 index 083f9e816d..0000000000 --- a/examples/declarative/particles/trails/swarm.qml +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import Qt.labs.particles 2.0 as QLP - -Rectangle{ - width: 200 - height: 200 - color: "black" - QLP.ParticleSystem{ id: ps } - QLP.ColoredParticle{ - system: ps - particles: ["star1","star2"] - anchors.fill: parent - clip: true - image: "content/star.png" - } - QLP.Swarm{ - system: ps - leaders: ["star2"]; - anchors.fill: parent - strength: 128 - } - QLP.TrailEmitter{ - anchors.fill: parent - system: ps - particle: "star1" - particlesPerSecond: 100 - particleDuration: 2000 - } - QLP.TrailEmitter{ - anchors.fill: parent - system: ps - particle: "star2" - particlesPerSecond: 0.4 - particleDuration: 10000 - particleSize: 64 - particleEndSize: 32 - } -} diff --git a/examples/declarative/particles/trails/trails.qml b/examples/declarative/particles/trails/trails.qml index 58d369c8d5..6ee8a6ec37 100644 --- a/examples/declarative/particles/trails/trails.qml +++ b/examples/declarative/particles/trails/trails.qml @@ -39,33 +39,33 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ color: "black" width: 360 height: 540 ParticleSystem{ id: sys } - ColoredParticle{ + ImageParticle{ system: sys id: cp image: "content/particle.png" color: "#00FFFFFF" colorVariation: 0.4 } - TrailEmitter{ + Emitter{ //burst on click id: bursty system: sys emitting: false particlesPerSecond: 2000 particleDuration: 500 - acceleration: AngleVector{ angle: 90; angleVariation: 360; magnitude: 640; } + acceleration: AngledDirection{ angle: 90; angleVariation: 360; magnitude: 640; } particleSize: 8 particleEndSize: 16 particleSizeVariation: 4 } - TrailEmitter{ + Emitter{ system: sys speedFromMovement: 4.0 emitting: ma.pressed @@ -73,7 +73,7 @@ Rectangle{ y: ma.mouseY particlesPerSecond: 400 particleDuration: 2000 - acceleration: AngleVector{ angle: 90; angleVariation: 22; magnitude: 32; } + acceleration: AngledDirection{ angle: 90; angleVariation: 22; magnitude: 32; } particleSize: 8 particleEndSize: 16 particleSizeVariation: 8 diff --git a/examples/declarative/particles/trails/turbulence.qml b/examples/declarative/particles/trails/turbulence.qml index 7da50464c2..bde25bc472 100644 --- a/examples/declarative/particles/trails/turbulence.qml +++ b/examples/declarative/particles/trails/turbulence.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle{ width: 360 @@ -66,21 +66,21 @@ Rectangle{ frequency: 64 gridSize: 16 } - ColoredParticle{ + ImageParticle{ particles: ["smoke"] system: ps image: "content/particle.png" color: "#11111111" colorVariation: 0 } - ColoredParticle{ + ImageParticle{ particles: ["flame"] system: ps image: "content/particle.png" color: "#11ff400f" colorVariation: 0.1 } - TrailEmitter{ + Emitter{ anchors.centerIn: parent system: ps particle: "flame" @@ -90,8 +90,8 @@ Rectangle{ particleSize: 20 particleEndSize: 10 particleSizeVariation: 10 - acceleration: PointVector{ y: -40 } - speed: AngleVector{ angle: 270; magnitude: 20; angleVariation: 22; magnitudeVariation: 5 } + acceleration: PointDirection{ y: -40 } + speed: AngledDirection{ angle: 270; magnitude: 20; angleVariation: 22; magnitudeVariation: 5 } } FollowEmitter{ id: smoke1 @@ -107,8 +107,8 @@ Rectangle{ particleSize: 16 particleEndSize: 8 particleSizeVariation: 8 - acceleration: PointVector{ y: -40 } - speed: AngleVector{ angle: 270; magnitude: 40; angleVariation: 22; magnitudeVariation: 5 } + acceleration: PointDirection{ y: -40 } + speed: AngledDirection{ angle: 270; magnitude: 40; angleVariation: 22; magnitudeVariation: 5 } } FollowEmitter{ id: smoke2 @@ -123,7 +123,7 @@ Rectangle{ particleSize: 36 particleEndSize: 24 particleSizeVariation: 8 - acceleration: PointVector{ y: -40 } - speed: AngleVector{ angle: 270; magnitude: 40; angleVariation: 22; magnitudeVariation: 5 } + acceleration: PointDirection{ y: -40 } + speed: AngledDirection{ angle: 270; magnitude: 40; angleVariation: 22; magnitudeVariation: 5 } } } diff --git a/examples/declarative/particles/trails/velocityfrommotion.qml b/examples/declarative/particles/trails/velocityfrommotion.qml index 3692410853..d2e4ed2917 100644 --- a/examples/declarative/particles/trails/velocityfrommotion.qml +++ b/examples/declarative/particles/trails/velocityfrommotion.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Rectangle { @@ -75,7 +75,7 @@ Rectangle { } ParticleSystem{ id: sys1 } - ColoredParticle{ + ImageParticle{ system: sys1 image: "content/particle.png" color: "cyan" @@ -105,7 +105,7 @@ Rectangle { } colorVariation: 0.3 } - TrailEmitter{ + Emitter{ id: trailsNormal system: sys1 @@ -116,15 +116,15 @@ Rectangle { y: mouseArea.pressed ? mouseArea.mouseY : circle.cy x: mouseArea.pressed ? mouseArea.mouseX : circle.cx - speed: PointVector{xVariation: 4; yVariation: 4;} - acceleration: PointVector{xVariation: 10; yVariation: 10;} + speed: PointDirection{xVariation: 4; yVariation: 4;} + acceleration: PointDirection{xVariation: 10; yVariation: 10;} speedFromMovement: 8 particleSize: 8 particleSizeVariation: 4 } ParticleSystem { id: sys2 } - ColoredParticle{ + ImageParticle{ color: "cyan" system: sys2 alpha: 0 @@ -144,7 +144,7 @@ Rectangle { colorVariation: 0.5 image: "content/star.png" } - TrailEmitter{ + Emitter{ id: trailsStars system: sys2 @@ -155,15 +155,15 @@ Rectangle { y: mouseArea.pressed ? mouseArea.mouseY : circle.cy x: mouseArea.pressed ? mouseArea.mouseX : circle.cx - speed: PointVector{xVariation: 4; yVariation: 4;} - acceleration: PointVector{xVariation: 10; yVariation: 10;} + speed: PointDirection{xVariation: 4; yVariation: 4;} + acceleration: PointDirection{xVariation: 10; yVariation: 10;} speedFromMovement: 8 particleSize: 22 particleSizeVariation: 4 } ParticleSystem { id: sys3; } - ColoredParticle{ + ImageParticle{ image: "content/particle.png" system: sys3 color: "orange" @@ -185,7 +185,7 @@ Rectangle { colorVariation: 0.2 } - TrailEmitter{ + Emitter{ id: trailsNormal2 system: sys3 @@ -197,14 +197,14 @@ Rectangle { speedFromMovement: 16 - speed: PointVector{xVariation: 4; yVariation: 4;} - acceleration: PointVector{xVariation: 10; yVariation: 10;} + speed: PointDirection{xVariation: 4; yVariation: 4;} + acceleration: PointDirection{xVariation: 10; yVariation: 10;} particleSize: 12 particleSizeVariation: 4 } ParticleSystem { id: sys4; } - ColoredParticle{ + ImageParticle{ system: sys4 image: "content/star.png" color: "green" @@ -225,7 +225,7 @@ Rectangle { colorVariation: 0.5 } - TrailEmitter{ + Emitter{ id: trailsStars2 system: sys4 @@ -237,8 +237,8 @@ Rectangle { x: mouseArea.pressed ? mouseArea.mouseX : circle2.cx speedFromMovement: 16 - speed: PointVector{xVariation: 2; yVariation: 2;} - acceleration: PointVector{xVariation: 10; yVariation: 10;} + speed: PointDirection{xVariation: 2; yVariation: 2;} + acceleration: PointDirection{xVariation: 10; yVariation: 10;} particleSize: 22 particleSizeVariation: 4 diff --git a/src/declarative/particles/qsgmaskextruder.cpp b/src/declarative/particles/qsgmaskextruder.cpp index 3fe28b30a3..d9a463910d 100644 --- a/src/declarative/particles/qsgmaskextruder.cpp +++ b/src/declarative/particles/qsgmaskextruder.cpp @@ -53,7 +53,7 @@ QSGMaskExtruder::QSGMaskExtruder(QObject *parent) : QPointF QSGMaskExtruder::extrude(const QRectF &r) { ensureInitialized(r); - if(!m_mask.count()) + if(!m_mask.count() || m_img.isNull()) return r.topLeft(); const QPointF p = m_mask[rand() % m_mask.count()]; //### Should random sub-pixel positioning be added? @@ -63,6 +63,8 @@ QPointF QSGMaskExtruder::extrude(const QRectF &r) bool QSGMaskExtruder::contains(const QRectF &bounds, const QPointF &point) { ensureInitialized(bounds);//###Current usage patterns WILL lead to different bounds/r calls. Separate list? + if(m_img.isNull()) + return false; QPoint p = point.toPoint() - bounds.topLeft().toPoint(); return m_img.rect().contains(p) && (bool)m_img.pixelIndex(p); } @@ -70,14 +72,19 @@ bool QSGMaskExtruder::contains(const QRectF &bounds, const QPointF &point) void QSGMaskExtruder::ensureInitialized(const QRectF &r) { if(m_lastWidth == r.width() && m_lastHeight == r.height()) - return; + return;//Same as before m_lastWidth = r.width(); m_lastHeight = r.height(); + m_img = QImage(); m_mask.clear(); if(m_source.isEmpty()) return; m_img = QImage(m_source.toLocalFile()); + if(m_img.isNull()){ + qWarning() << "MaskShape: Cannot load" << qPrintable(m_source.toLocalFile()); + return; + } m_img = m_img.createAlphaMask(); m_img = m_img.convertToFormat(QImage::Format_Mono);//Else LSB, but I think that's easier m_img = m_img.scaled(r.size().toSize());//TODO: Do they need aspect ratio stuff? Or tiling? diff --git a/src/declarative/particles/qsgparticleaffector_p.h b/src/declarative/particles/qsgparticleaffector_p.h index 1572ad8102..b299158069 100644 --- a/src/declarative/particles/qsgparticleaffector_p.h +++ b/src/declarative/particles/qsgparticleaffector_p.h @@ -52,7 +52,6 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) - class QSGParticleAffector : public QSGItem { Q_OBJECT diff --git a/src/declarative/particles/qsgpointattractor.cpp b/src/declarative/particles/qsgpointattractor.cpp index 3c3ca33086..4c675237ba 100644 --- a/src/declarative/particles/qsgpointattractor.cpp +++ b/src/declarative/particles/qsgpointattractor.cpp @@ -45,6 +45,7 @@ QT_BEGIN_NAMESPACE QSGPointAttractorAffector::QSGPointAttractorAffector(QSGItem *parent) : QSGParticleAffector(parent), m_strength(0.0), m_x(0), m_y(0) + , m_physics(Velocity), m_proportionalToDistance(Linear) { } @@ -52,15 +53,36 @@ bool QSGPointAttractorAffector::affectParticle(QSGParticleData *d, qreal dt) { if(m_strength == 0.0) return false; - qreal dx = m_x - d->curX(); - qreal dy = m_y - d->curY(); + qreal dx = m_y - d->curX(); + qreal dy = m_x - d->curY(); qreal r = sqrt((dx*dx) + (dy*dy)); qreal theta = atan2(dy,dx); - qreal ds = (m_strength / r) * dt; + qreal ds = 0; + switch(m_proportionalToDistance){ + case Quadratic: + ds = (m_strength / qMax(1.,r*r)) * dt; + break; + case Linear://also default + default: + ds = (m_strength / qMax(1.,r)) * dt; + } dx = ds * cos(theta); dy = ds * sin(theta); - d->setInstantaneousSX(d->pv.sx + dx); - d->setInstantaneousSY(d->pv.sy + dy); + switch(m_physics){ + case Position: + d->pv.x = (d->pv.x + dx); + d->pv.y = (d->pv.y + dy); + break; + case Acceleration: + d->setInstantaneousAX(d->pv.ax + dx); + d->setInstantaneousAY(d->pv.ay + dy); + break; + case Velocity: //also default + default: + d->setInstantaneousSX(d->pv.sx + dx); + d->setInstantaneousSY(d->pv.sy + dy); + } + return true; } QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgpointattractor_p.h b/src/declarative/particles/qsgpointattractor_p.h index d4f715928a..3ca29dfa96 100644 --- a/src/declarative/particles/qsgpointattractor_p.h +++ b/src/declarative/particles/qsgpointattractor_p.h @@ -57,7 +57,23 @@ class QSGPointAttractorAffector : public QSGParticleAffector Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged) Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + Q_PROPERTY(PhysicsAffects physics READ physics WRITE setPhysics NOTIFY physicsChanged) + Q_PROPERTY(Proportion proportionalToDistance READ proportionalToDistance WRITE setProportionalToDistance NOTIFY proportionalToDistanceChanged) + Q_ENUMS(PhysicsAffects) + Q_ENUMS(Proportion) + public: + enum Proportion{ + Linear, + Quadratic + }; + + enum PhysicsAffects { + Position, + Velocity, + Acceleration + }; + explicit QSGPointAttractorAffector(QSGItem *parent = 0); qreal strength() const @@ -75,6 +91,16 @@ public: return m_y; } + PhysicsAffects physics() const + { + return m_physics; + } + + Proportion proportionalToDistance() const + { + return m_proportionalToDistance; + } + signals: void strengthChanged(qreal arg); @@ -83,6 +109,10 @@ signals: void yChanged(qreal arg); + void physicsChanged(PhysicsAffects arg); + + void proportionalToDistanceChanged(Proportion arg); + public slots: void setStrength(qreal arg) { @@ -107,12 +137,30 @@ void setY(qreal arg) emit yChanged(arg); } } +void setPhysics(PhysicsAffects arg) +{ + if (m_physics != arg) { + m_physics = arg; + emit physicsChanged(arg); + } +} + +void setProportionalToDistance(Proportion arg) +{ + if (m_proportionalToDistance != arg) { + m_proportionalToDistance = arg; + emit proportionalToDistanceChanged(arg); + } +} + protected: virtual bool affectParticle(QSGParticleData *d, qreal dt); private: qreal m_strength; qreal m_x; qreal m_y; +PhysicsAffects m_physics; +Proportion m_proportionalToDistance; }; QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgwander.cpp b/src/declarative/particles/qsgwander.cpp index e6787f2570..6e56d6a05b 100644 --- a/src/declarative/particles/qsgwander.cpp +++ b/src/declarative/particles/qsgwander.cpp @@ -44,7 +44,8 @@ QT_BEGIN_NAMESPACE QSGWanderAffector::QSGWanderAffector(QSGItem *parent) : - QSGParticleAffector(parent) + QSGParticleAffector(parent), m_xVariance(0), m_yVariance(0), m_pace(0) + , m_physics(Velocity) { m_needsReset = true; } @@ -81,6 +82,7 @@ void QSGWanderAffector::reset(int systemIdx) bool QSGWanderAffector::affectParticle(QSGParticleData* data, qreal dt) { + /*TODO: Add a mode which does basically this - picking a direction, going in it (random speed) and then going back WanderData* d = getData(data->systemIndex); if (m_xVariance != 0.) { if ((d->x_vel > d->x_peak && d->x_var > 0.0) || (d->x_vel < -d->x_peak && d->x_var < 0.0)) { @@ -106,5 +108,37 @@ bool QSGWanderAffector::affectParticle(QSGParticleData* data, qreal dt) p->y += dy; return true; + */ + qreal dx = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1); + qreal dy = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1); + qreal newX, newY; + switch(m_physics){ + case Position: + newX = data->curX() + dx; + if(m_xVariance > qAbs(newX) ) + data->pv.x += dx; + newY = data->curY() + dy; + if(m_yVariance > qAbs(newY) ) + data->pv.y += dy; + break; + default: + case Velocity: + newX = data->curSX() + dx; + if(m_xVariance > qAbs(newX) ) + data->setInstantaneousSX(newX); + newY = data->curSY() + dy; + if(m_yVariance > qAbs(newY) ) + data->setInstantaneousSY(newY); + break; + case Acceleration: + newX = data->pv.ax + dx; + if(m_xVariance > qAbs(newX) ) + data->setInstantaneousAX(newX); + newY = data->pv.ay + dy; + if(m_yVariance > qAbs(newY) ) + data->setInstantaneousAY(newY); + break; + } + return true; } QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgwander_p.h b/src/declarative/particles/qsgwander_p.h index 45c8ca8b20..783efc8636 100644 --- a/src/declarative/particles/qsgwander_p.h +++ b/src/declarative/particles/qsgwander_p.h @@ -63,11 +63,19 @@ struct WanderData{ class QSGWanderAffector : public QSGParticleAffector { Q_OBJECT + Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged) Q_PROPERTY(qreal xVariance READ xVariance WRITE setXVariance NOTIFY xVarianceChanged) Q_PROPERTY(qreal yVariance READ yVariance WRITE setYVariance NOTIFY yVarianceChanged) - Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged) + Q_PROPERTY(PhysicsAffects physics READ physics WRITE setPhysics NOTIFY physicsChanged) + Q_ENUMS(PhysicsAffects) public: + enum PhysicsAffects { + Position, + Velocity, + Acceleration + }; + explicit QSGWanderAffector(QSGItem *parent = 0); ~QSGWanderAffector(); virtual void reset(int systemIdx); @@ -86,6 +94,12 @@ public: { return m_pace; } + + PhysicsAffects physics() const + { + return m_physics; + } + protected: virtual bool affectParticle(QSGParticleData *d, qreal dt); signals: @@ -96,6 +110,9 @@ signals: void paceChanged(qreal arg); + + void physicsChanged(PhysicsAffects arg); + public slots: void setXVariance(qreal arg) { @@ -121,12 +138,22 @@ void setPace(qreal arg) } } + +void setPhysics(PhysicsAffects arg) +{ + if (m_physics != arg) { + m_physics = arg; + emit physicsChanged(arg); + } +} + private: WanderData* getData(int idx); QHash m_wanderData; qreal m_xVariance; qreal m_yVariance; qreal m_pace; + PhysicsAffects m_physics; }; QT_END_NAMESPACE From 1f88f2ceedf00f96e2491ecdd1c655b0f12f3632 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 8 Jun 2011 13:19:56 +1000 Subject: [PATCH 09/48] Immense Particles Refactor Part C Demos work again (also, the examples I missed). Added an example launcher for particles. --- .../flickr/content/ImageDetails.qml | 182 +++++++++++++----- demos/declarative/flickr/content/Progress.qml | 8 +- .../declarative/flickr/content/StreamView.qml | 16 +- .../flickr/content/UnifiedDelegate.qml | 2 +- .../flickr/content/images/noise.png | Bin 0 -> 92003 bytes demos/declarative/flickr/flickr.qml | 10 +- .../minehunt/MinehuntCore/Explosion.qml | 2 +- .../PhotoViewerCore/AlbumDelegate.qml | 2 +- .../plasmapatrol/content/BlasterHardpoint.qml | 12 +- .../plasmapatrol/content/CannonHardpoint.qml | 8 +- .../plasmapatrol/content/ChoiceBox.qml | 2 +- .../plasmapatrol/content/Cruiser.qml | 12 +- .../plasmapatrol/content/Frigate.qml | 8 +- .../plasmapatrol/content/Hardpoint.qml | 2 +- .../plasmapatrol/content/HelpScreens.qml | 2 +- .../plasmapatrol/content/LaserHardpoint.qml | 14 +- .../content/PlasmaPatrolParticles.qml | 24 +-- .../declarative/plasmapatrol/content/Ship.qml | 2 +- .../plasmapatrol/content/Sloop.qml | 8 +- .../declarative/plasmapatrol/plasmapatrol.qml | 8 +- .../samegame/SamegameCore/BoomBlock.qml | 10 +- demos/declarative/samegame/samegame.qml | 8 +- demos/declarative/snake/content/Cookie.qml | 2 +- demos/declarative/snake/content/Link.qml | 2 +- .../particles/asteroid/blackhole.qml | 5 +- .../particles/exampleslauncher.qml | 182 ++++++++++++++++++ .../content => launcherContent}/Button.qml | 0 .../particles/launcherContent/Shell.qml | 78 ++++++++ .../launcherContent/icons/asteroid.png | Bin 0 -> 35579 bytes .../launcherContent/icons/blackhole.png | Bin 0 -> 31296 bytes .../launcherContent/icons/blurparticles.png | Bin 0 -> 7793 bytes .../particles/launcherContent/icons/close.png | Bin 0 -> 2144 bytes .../launcherContent/icons/dynamicemitters.png | Bin 0 -> 9347 bytes .../launcherContent/icons/fireballs.png | Bin 0 -> 5371 bytes .../launcherContent/icons/flickr.png | Bin 0 -> 10542 bytes .../launcherContent/icons/gridsplosion.png | Bin 0 -> 8154 bytes .../launcherContent/icons/layered.png | Bin 0 -> 8572 bytes .../particles/launcherContent/icons/list.png | Bin 0 -> 32912 bytes .../launcherContent/icons/overburst.png | Bin 0 -> 1019 bytes .../launcherContent/icons/package.png | Bin 0 -> 3163 bytes .../particles/launcherContent/icons/plain.png | Bin 0 -> 4705 bytes .../launcherContent/icons/plasmapatrol.png | Bin 0 -> 9839 bytes .../launcherContent/icons/portal.png | Bin 0 -> 11359 bytes .../launcherContent/icons/rainbow.png | Bin 0 -> 6538 bytes .../launcherContent/icons/remove.png | Bin 0 -> 2144 bytes .../launcherContent/icons/samegame.png | Bin 0 -> 8647 bytes .../launcherContent/icons/shimmer.png | Bin 0 -> 13670 bytes .../particles/launcherContent/icons/smile.png | Bin 0 -> 12784 bytes .../launcherContent/icons/smilefactory.png | Bin 0 -> 2863 bytes .../particles/launcherContent/icons/snow.png | Bin 0 -> 5858 bytes .../launcherContent/icons/spaceexplorer.png | Bin 0 -> 9152 bytes .../launcherContent/icons/spriteparticles.png | Bin 0 -> 6963 bytes .../icons/spritestateparticles.png | Bin 0 -> 2565 bytes .../icons/spritevariedparticles.png | Bin 0 -> 2569 bytes .../launcherContent/icons/stream.png | Bin 0 -> 26560 bytes .../launcherContent/icons/trails.png | Bin 0 -> 23168 bytes .../launcherContent/icons/turbulence.png | Bin 0 -> 9684 bytes .../launcherContent/icons/ultraparticles.png | Bin 0 -> 9590 bytes .../icons/velocityfrommotion.png | Bin 0 -> 36360 bytes .../particles/launcherContent/launcher.js | 8 + examples/declarative/particles/snow/snow.qml | 7 +- 61 files changed, 489 insertions(+), 137 deletions(-) create mode 100644 demos/declarative/flickr/content/images/noise.png create mode 100644 examples/declarative/particles/exampleslauncher.qml rename examples/declarative/particles/{snow/content => launcherContent}/Button.qml (100%) create mode 100644 examples/declarative/particles/launcherContent/Shell.qml create mode 100644 examples/declarative/particles/launcherContent/icons/asteroid.png create mode 100644 examples/declarative/particles/launcherContent/icons/blackhole.png create mode 100644 examples/declarative/particles/launcherContent/icons/blurparticles.png create mode 100644 examples/declarative/particles/launcherContent/icons/close.png create mode 100644 examples/declarative/particles/launcherContent/icons/dynamicemitters.png create mode 100644 examples/declarative/particles/launcherContent/icons/fireballs.png create mode 100644 examples/declarative/particles/launcherContent/icons/flickr.png create mode 100644 examples/declarative/particles/launcherContent/icons/gridsplosion.png create mode 100644 examples/declarative/particles/launcherContent/icons/layered.png create mode 100644 examples/declarative/particles/launcherContent/icons/list.png create mode 100644 examples/declarative/particles/launcherContent/icons/overburst.png create mode 100644 examples/declarative/particles/launcherContent/icons/package.png create mode 100644 examples/declarative/particles/launcherContent/icons/plain.png create mode 100644 examples/declarative/particles/launcherContent/icons/plasmapatrol.png create mode 100644 examples/declarative/particles/launcherContent/icons/portal.png create mode 100644 examples/declarative/particles/launcherContent/icons/rainbow.png create mode 100644 examples/declarative/particles/launcherContent/icons/remove.png create mode 100644 examples/declarative/particles/launcherContent/icons/samegame.png create mode 100644 examples/declarative/particles/launcherContent/icons/shimmer.png create mode 100644 examples/declarative/particles/launcherContent/icons/smile.png create mode 100644 examples/declarative/particles/launcherContent/icons/smilefactory.png create mode 100644 examples/declarative/particles/launcherContent/icons/snow.png create mode 100644 examples/declarative/particles/launcherContent/icons/spaceexplorer.png create mode 100644 examples/declarative/particles/launcherContent/icons/spriteparticles.png create mode 100644 examples/declarative/particles/launcherContent/icons/spritestateparticles.png create mode 100644 examples/declarative/particles/launcherContent/icons/spritevariedparticles.png create mode 100644 examples/declarative/particles/launcherContent/icons/stream.png create mode 100644 examples/declarative/particles/launcherContent/icons/trails.png create mode 100644 examples/declarative/particles/launcherContent/icons/turbulence.png create mode 100644 examples/declarative/particles/launcherContent/icons/ultraparticles.png create mode 100644 examples/declarative/particles/launcherContent/icons/velocityfrommotion.png create mode 100644 examples/declarative/particles/launcherContent/launcher.js diff --git a/demos/declarative/flickr/content/ImageDetails.qml b/demos/declarative/flickr/content/ImageDetails.qml index 62c3397f08..46827ae3bb 100644 --- a/demos/declarative/flickr/content/ImageDetails.qml +++ b/demos/declarative/flickr/content/ImageDetails.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Flipable { id: container @@ -57,6 +57,8 @@ Flipable { property int rating: 2 property variant prevScale: 1.0 + property int flipDuration: 1600 + signal closed transform: Rotation { @@ -137,94 +139,172 @@ Flipable { slider.value = prevScale; } if (inBackState && bigImage.status == Image.Ready) - particleBox.imageInAnim(); + effectBox.imageInAnim(); } property bool inBackState: false onInBackStateChanged:{ if(inBackState && bigImage.status == Image.Ready) - particleBox.imageInAnim(); + effectBox.imageInAnim(); else if (!inBackState && bigImage.status == Image.Ready) - particleBox.imageOutAnim(); + effectBox.imageOutAnim(); } } + ShaderEffectSource{ + id: pictureSource + sourceItem: bigImage + smooth: true + //Workaround: Doesn't work below lines + width: bigImage.width + height: bigImage.width + visible: false + } + Turbulence{//only fill visible rect + id: turbulence + system: imageSystem + anchors.fill: parent + frequency: 100 + strength: 250 + active: false + } Item{ - id: particleBox + id: effectBox width: bigImage.width * bigImage.scale height: bigImage.height * bigImage.scale anchors.centerIn: parent - function imageInAnim(){ - cp.visible = true; - pixAffect.onceOff = false; bigImage.visible = false; + noiseIn.visible = true; endEffectTimer.start(); - pixelEmitter.pulse(1); } function imageOutAnim(){ - cp.visible = true; - pixAffect.onceOff = true; bigImage.visible = false; + noiseIn.visible = false; turbulence.active = true; endEffectTimer.start(); pixelEmitter.burst(2048); } Timer{ id: endEffectTimer - interval: 1000 + interval: flipDuration repeat: false running: false onTriggered:{ - bigImage.visible = true; turbulence.active = false; - cp.visible = false; + noiseIn.visible = false; + bigImage.visible = true; } } + ShaderEffectItem{ + id: noiseIn + anchors.fill: parent + property real t: 0 + visible: false + onVisibleChanged: tAnim.start() + NumberAnimation{ + id: tAnim + target: noiseIn + property: "t" + from: 0.0 + to: 1.0 + duration: flipDuration + } + property variant source: pictureSource + property variant noise: ShaderEffectSource{ + sourceItem:Image{ + source: "images/noise.png" + } + hideSource: true + smooth: false + } + fragmentShader:" + uniform sampler2D noise; + uniform sampler2D source; + uniform highp float t; + uniform lowp float qt_Opacity; + varying highp vec2 qt_TexCoord0; + void main(){ + //Want to use noise2, but it always returns (0,0)? + if(texture2D(noise, qt_TexCoord0).w <= t) + gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; + else + gl_FragColor = vec4(0.,0.,0.,0.); + } + " + } ParticleSystem{ id: imageSystem } - ColoredParticle{ - id: cp - system: imageSystem - color: "gray" - alpha: 1 - image: "images/squareParticle.png" - colorVariation: 0 - } - Picture{ - id: pixAffect - system: imageSystem - anchors.fill: parent - image: container.photoUrl; - onceOff: true - } - Turbulence{ - id: turbulence - system: imageSystem - anchors.fill: parent - frequency: 100 - strength: 250 - active: false - } - TrailEmitter{ - id: pixelEmitter0 - system: imageSystem - height: parent.height - particleSize: 4 - particleDuration: 1000 - particlesPerSecond: 4096 - speed: PointVector{x: 360; xVariation: 8; yVariation: 4} - emitting: false - } - TrailEmitter{ + Emitter{ id: pixelEmitter system: imageSystem - anchors.fill: parent + //anchors.fill: parent + width: Math.min(bigImage.width * bigImage.scale, flickable.width); + height: Math.min(bigImage.height * bigImage.scale, flickable.height); + anchors.centerIn: parent particleSize: 4 - particleDuration: 1000 + particleDuration: flipDuration particlesPerSecond: 2048 emitting: false } + CustomParticle{ + id: blowOut + system: imageSystem + property real maxWidth: effectBox.width + property real maxHeight: effectBox.height + vertexShader:" + attribute highp vec2 vPos; + attribute highp vec2 vTex; + attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize + attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration + attribute highp float r; + + uniform highp float maxWidth; + uniform highp float maxHeight; + + uniform highp mat4 qt_ModelViewProjectionMatrix; + uniform highp float timestamp; + uniform lowp float qt_Opacity; + + varying highp vec2 fTex2; + varying lowp float fFade; + + void main() { + fTex2 = vec2(vPos.x / maxWidth, vPos.y / maxHeight); + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 10., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fFade = 1.0;//fadeIn * fadeOut * qt_Opacity; + } + " + property variant pictureTexture: pictureSource + fragmentShader: " + uniform sampler2D pictureTexture; + varying highp vec2 fTex2; + varying highp float fFade; + void main() { + gl_FragColor = texture2D(pictureTexture, fTex2) * fFade; + }" + } + + } } @@ -268,7 +348,7 @@ Flipable { transitions: Transition { SequentialAnimation { PropertyAction { target: bigImage; property: "smooth"; value: false } - NumberAnimation { easing.type: Easing.InOutQuad; properties: "angle"; duration: 1000 } + NumberAnimation { easing.type: Easing.InOutQuad; properties: "angle"; duration: flipDuration } PropertyAction { target: bigImage; property: "smooth"; value: !flickable.movingVertically } } } diff --git a/demos/declarative/flickr/content/Progress.qml b/demos/declarative/flickr/content/Progress.qml index d403feebd1..30142b4e29 100644 --- a/demos/declarative/flickr/content/Progress.qml +++ b/demos/declarative/flickr/content/Progress.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Item{ id: container @@ -59,17 +59,17 @@ Item{ running: container.visible id: barSys } - ColoredParticle{ + ImageParticle{ color: "lightsteelblue" alpha: 0.1 colorVariation: 0.05 image: "images/particle.png" system: barSys } - TrailEmitter{ + Emitter{ y: 2; height: parent.height-4; x: 2; width: Math.max(parent.width * progress - 4, 0); - speed: AngleVector{ angleVariation: 180; magnitudeVariation: 12 } + speed: AngledDirection{ angleVariation: 180; magnitudeVariation: 12 } system: barSys particlesPerSecond: width; particleDuration: 1000 diff --git a/demos/declarative/flickr/content/StreamView.qml b/demos/declarative/flickr/content/StreamView.qml index 26a3f35e63..22e7d5dcd2 100644 --- a/demos/declarative/flickr/content/StreamView.qml +++ b/demos/declarative/flickr/content/StreamView.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Item{ id: container @@ -52,7 +52,7 @@ Item{ anchors.fill:parent overwrite: false } - DataParticle{ + ModelParticle{ id: mp fade: false system: sys @@ -66,35 +66,35 @@ Item{ } } property real emitterSpacing: parent.width/3 - TrailEmitter{ + Emitter{ system: sys width: emitterSpacing - 64 x: emitterSpacing*0 + 32 y: -128 height: 32 - speed: PointVector{ y: (container.height + 128)/12 } + speed: PointDirection{ y: (container.height + 128)/12 } particlesPerSecond: 0.4 particleDuration: 1000000//eventually -1 should mean a million seconds for neatness maxParticles: 15 } - TrailEmitter{ + Emitter{ system: sys width: emitterSpacing - 64 x: emitterSpacing*1 + 32 y: -128 height: 32 - speed: PointVector{ y: (container.height + 128)/12 } + speed: PointDirection{ y: (container.height + 128)/12 } particlesPerSecond: 0.4 particleDuration: 1000000//eventually -1 should mean a million seconds for neatness maxParticles: 15 } - TrailEmitter{ + Emitter{ system: sys width: emitterSpacing - 64 x: emitterSpacing*2 + 32 y: -128 height: 32 - speed: PointVector{ y: (container.height + 128)/12 } + speed: PointDirection{ y: (container.height + 128)/12 } particlesPerSecond: 0.4 particleDuration: 1000000//eventually -1 should mean a million seconds for neatness maxParticles: 15 diff --git a/demos/declarative/flickr/content/UnifiedDelegate.qml b/demos/declarative/flickr/content/UnifiedDelegate.qml index aaf4ccbd7a..f4c926763f 100644 --- a/demos/declarative/flickr/content/UnifiedDelegate.qml +++ b/demos/declarative/flickr/content/UnifiedDelegate.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Package { function photoClicked() { diff --git a/demos/declarative/flickr/content/images/noise.png b/demos/declarative/flickr/content/images/noise.png new file mode 100644 index 0000000000000000000000000000000000000000..abc3c18d5f099a1a989368669111129c171fc879 GIT binary patch literal 92003 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 0 ? hp * 1 + 20 : 0 particleDuration: 2400 @@ -72,13 +72,13 @@ Item { particleSizeVariation: 16 particleEndSize: 16 - speed: AngleVector{angleVariation:360; magnitudeVariation: 32} + speed: AngledDirection{angleVariation:360; magnitudeVariation: 32} } - TrailEmitter{ + Emitter{ system: container.system particle: "cruiserArmor" anchors.fill: parent - shape: Ellipse{ fill: false } + shape: EllipseShape{ fill: false } emitting: hp>0 particlesPerSecond: 16 diff --git a/demos/declarative/plasmapatrol/content/Frigate.qml b/demos/declarative/plasmapatrol/content/Frigate.qml index 54f629268f..f18de0154a 100644 --- a/demos/declarative/plasmapatrol/content/Frigate.qml +++ b/demos/declarative/plasmapatrol/content/Frigate.qml @@ -39,7 +39,7 @@ ** ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Item { id: container @@ -54,7 +54,7 @@ Item { property int gunType: 0 width: 128 height: 128 - TrailEmitter{ + Emitter{ system: container.system particle: "frigateShield" anchors.centerIn: parent @@ -63,13 +63,13 @@ Item { particleDuration: 4800 emitting: hp > 0 } - TrailEmitter{ + Emitter{ system: container.system particle: container.shipParticle anchors.centerIn: parent width: 64 height: 16 - shape: Ellipse{} + shape: EllipseShape{} particleSize: 16 particleSizeVariation: 8 diff --git a/demos/declarative/plasmapatrol/content/Hardpoint.qml b/demos/declarative/plasmapatrol/content/Hardpoint.qml index 184c750a79..3d4edb38f8 100644 --- a/demos/declarative/plasmapatrol/content/Hardpoint.qml +++ b/demos/declarative/plasmapatrol/content/Hardpoint.qml @@ -39,7 +39,7 @@ ** ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Item { id: container diff --git a/demos/declarative/plasmapatrol/content/HelpScreens.qml b/demos/declarative/plasmapatrol/content/HelpScreens.qml index 8896aeee4a..7e4fb9f1c9 100644 --- a/demos/declarative/plasmapatrol/content/HelpScreens.qml +++ b/demos/declarative/plasmapatrol/content/HelpScreens.qml @@ -39,7 +39,7 @@ ** ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 SequentialLoader { id: hLdr diff --git a/demos/declarative/plasmapatrol/content/LaserHardpoint.qml b/demos/declarative/plasmapatrol/content/LaserHardpoint.qml index d6d470a15b..3705d29287 100644 --- a/demos/declarative/plasmapatrol/content/LaserHardpoint.qml +++ b/demos/declarative/plasmapatrol/content/LaserHardpoint.qml @@ -39,7 +39,7 @@ ** ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Item { id: container @@ -49,14 +49,14 @@ Item { width: 24 height: 24 - TrailEmitter{ + Emitter{ id: visualization particle: "laser" system: container.system anchors.fill: parent emitting: container.show - shape: Ellipse{} - speed: DirectedVector{ targetX: width/2; targetY: width/2; magnitude: -1; proportionalMagnitude: true } + shape: EllipseShape{} + speed: TargetedDirection{ targetX: width/2; targetY: width/2; magnitude: -1; proportionalMagnitude: true } particleDuration: 1000 particlesPerSecond: 64 @@ -84,7 +84,7 @@ Item { emitter.pulse(0.10); // console.log("Fire box: " + Math.min(container.width/2, target.x) + "," + Math.min(container.height/2, target.y) + " " + (Math.max(container.width/2, target.x) - Math.min(container.width/2, target.x)) + "," + (Math.max(container.height/2, target.y) - Math.min(container.height/2, target.y))); } - TrailEmitter{ + Emitter{ id: emitter particle: "laser" emitting: false @@ -93,7 +93,7 @@ Item { width: Math.max(container.width/2, target.x) - x; y: Math.min(container.height/2, target.y); height: Math.max(container.height/2, target.y) - y; - shape: Line{ + shape: LineShape{ mirrored: (emitter.y < 0 || emitter.x < 0) && !(emitter.y < 0 && emitter.x < 0 )//I just want XOR } @@ -103,6 +103,6 @@ Item { particleSize: 16 particleEndSize: 0 - speed: PointVector{xVariation: 4; yVariation: 4} + speed: PointDirection{xVariation: 4; yVariation: 4} } } diff --git a/demos/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml b/demos/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml index 7a6fcb96d0..26d5f21359 100644 --- a/demos/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml +++ b/demos/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml @@ -39,11 +39,11 @@ ** ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Item{ property ParticleSystem sys - ColoredParticle{ + ImageParticle{ system: sys particles: ["default"] image: "pics/blur-circle3.png" @@ -51,7 +51,7 @@ Item{ colorVariation: 0.1 z: 0 } - ColoredParticle{ + ImageParticle{ system: sys particles: ["redTeam"] image: "pics/blur-circle3.png" @@ -59,7 +59,7 @@ Item{ colorVariation: 0.1 z: 0 } - ColoredParticle{ + ImageParticle{ system: sys particles: ["greenTeam"] image: "pics/blur-circle3.png" @@ -67,7 +67,7 @@ Item{ colorVariation: 0.1 z: 0 } - ColoredParticle{ + ImageParticle{ system: sys particles: ["blaster"] image: "pics/star2.png" @@ -76,7 +76,7 @@ Item{ colorVariation: 0.2 z: 2 } - ColoredParticle{ + ImageParticle{ system: sys particles: ["laser"] image: "pics/star3.png" @@ -85,7 +85,7 @@ Item{ colorVariation: 0.2 z: 2 } - ColoredParticle{ + ImageParticle{ system: sys particles: ["cannon"] image: "pics/particle.png" @@ -93,7 +93,7 @@ Item{ colorVariation: 0.1 z: 2 } - ColoredParticle{ + ImageParticle{ system: sys particles: ["cannonCore"] image: "pics/particle.png" @@ -101,7 +101,7 @@ Item{ colorVariation: 0.8 z: 1 } - ColoredParticle{ + ImageParticle{ system: sys particles: ["cannonWake"] image: "pics/star.png" @@ -109,7 +109,7 @@ Item{ colorVariation: 0.2 z: 1 } - ColoredParticle{ + ImageParticle{ system: sys particles: ["frigateShield"] image: "pics/blur-circle2.png" @@ -119,7 +119,7 @@ Item{ greenVariation: 0.1 z: 3 } - SpriteParticle{ + ImageParticle{ system: sys particles: ["cruiserArmor"] z: 1 @@ -150,7 +150,7 @@ Item{ follow: "cannon" particlesPerParticlePerSecond: 64 particleDuration: 600 - speed: AngleVector{ angleVariation: 360; magnitude: 48} + speed: AngledDirection{ angleVariation: 360; magnitude: 48} particleSize: 16 particleEndSize: 8 particleSizeVariation: 2 diff --git a/demos/declarative/plasmapatrol/content/Ship.qml b/demos/declarative/plasmapatrol/content/Ship.qml index 0ccea94221..ce8fb6073f 100644 --- a/demos/declarative/plasmapatrol/content/Ship.qml +++ b/demos/declarative/plasmapatrol/content/Ship.qml @@ -39,7 +39,7 @@ ** ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Item { id: me diff --git a/demos/declarative/plasmapatrol/content/Sloop.qml b/demos/declarative/plasmapatrol/content/Sloop.qml index 1b9bce7333..20b60c5621 100644 --- a/demos/declarative/plasmapatrol/content/Sloop.qml +++ b/demos/declarative/plasmapatrol/content/Sloop.qml @@ -39,7 +39,7 @@ ** ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Item { id: container @@ -55,19 +55,19 @@ Item { property int gunType: 0 width: 128 height: 128 - TrailEmitter{ + Emitter{ id: emitter //TODO: Cooler would be an 'orbiting' affector //TODO: On the subject, opacity and size should be grouped type 'overLife' if we can cram that in the particles system: container.system particle: container.shipParticle - shape: Ellipse{} + shape: EllipseShape{} particlesPerSecond: hp > 0 ? hp + 20 : 0 particleDuration: blinkInterval maxParticles: (maxHP + 20) - acceleration: AngleVector{angleVariation: 360; magnitude: 8} + acceleration: AngledDirection{angleVariation: 360; magnitude: 8} particleSize: 24 particleEndSize: 4 diff --git a/demos/declarative/plasmapatrol/plasmapatrol.qml b/demos/declarative/plasmapatrol/plasmapatrol.qml index 3a7b217618..c490dfd322 100644 --- a/demos/declarative/plasmapatrol/plasmapatrol.qml +++ b/demos/declarative/plasmapatrol/plasmapatrol.qml @@ -39,7 +39,7 @@ ** ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 import "content" Rectangle { @@ -93,18 +93,18 @@ Rectangle { id: title width: root.width height: 240 - TrailEmitter{ + Emitter{ anchors.fill: parent system: particles emitting: true particle: "default" particlesPerSecond: 1200 particleDuration: 1200 - shape: Mask{source:"content/pics/TitleText.png"} + shape: MaskShape{source:"content/pics/TitleText.png"} particleSize: 16 particleEndSize: 0 particleSizeVariation: 8 - speed: AngleVector{angleVariation:360; magnitudeVariation: 6} + speed: AngledDirection{angleVariation:360; magnitudeVariation: 6} } } Button{ diff --git a/demos/declarative/samegame/SamegameCore/BoomBlock.qml b/demos/declarative/samegame/SamegameCore/BoomBlock.qml index 3d11fb9df2..b234688f8e 100644 --- a/demos/declarative/samegame/SamegameCore/BoomBlock.qml +++ b/demos/declarative/samegame/SamegameCore/BoomBlock.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 Item { id: block @@ -71,7 +71,7 @@ Item { Behavior on opacity { NumberAnimation { duration: 200 } } anchors.fill: parent } - TrailEmitter { + Emitter { id: particles system: particleSystem particle: { @@ -85,12 +85,12 @@ Item { } anchors.fill: parent - speed: DirectedVector{targetX: block.width/2; targetY: block.height/2; magnitude: -60; magnitudeVariation: 60} - shape: Ellipse{fill:true} + speed: TargetedDirection{targetX: block.width/2; targetY: block.height/2; magnitude: -60; magnitudeVariation: 60} + shape: EllipseShape{fill:true} emitting: false; particleDuration: 700; particleDurationVariation: 100 particlesPerSecond: 1000 - maxParticles: 100 //only fires 0.1s bursts (still 2x old number, ColoredParticle wants less than 16000 max though) + maxParticles: 100 //only fires 0.1s bursts (still 2x old number, ImageParticle wants less than 16000 max though) particleSize: 28 particleEndSize: 14 } diff --git a/demos/declarative/samegame/samegame.qml b/demos/declarative/samegame/samegame.qml index 0defdeec3c..c547e1afa7 100644 --- a/demos/declarative/samegame/samegame.qml +++ b/demos/declarative/samegame/samegame.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 -import Qt.labs.particles 2.0 +import QtQuick.Particles 2.0 import "SamegameCore" import "SamegameCore/samegame.js" as Logic @@ -77,7 +77,7 @@ Rectangle { } Item{ ParticleSystem{ id: particleSystem; } - ColoredParticle { + ImageParticle { system: particleSystem particles: ["red"] color: Qt.darker("red");//Actually want desaturated... @@ -85,7 +85,7 @@ Rectangle { colorVariation: 0.4 alpha: 0.1 } - ColoredParticle { + ImageParticle { system: particleSystem particles: ["green"] color: Qt.darker("green");//Actually want desaturated... @@ -93,7 +93,7 @@ Rectangle { colorVariation: 0.4 alpha: 0.1 } - ColoredParticle { + ImageParticle { system: particleSystem particles: ["blue"] color: Qt.darker("blue");//Actually want desaturated... diff --git a/demos/declarative/snake/content/Cookie.qml b/demos/declarative/snake/content/Cookie.qml index a076978999..6efed6a154 100644 --- a/demos/declarative/snake/content/Cookie.qml +++ b/demos/declarative/snake/content/Cookie.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 1.0 -import Qt.labs.particles 1.0 +import QtQuick.Particles 1.0 Item { id: root diff --git a/demos/declarative/snake/content/Link.qml b/demos/declarative/snake/content/Link.qml index 8c1f4866bb..4c58c4dd5c 100644 --- a/demos/declarative/snake/content/Link.qml +++ b/demos/declarative/snake/content/Link.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 1.0 -import Qt.labs.particles 1.0 +import QtQuick.Particles 1.0 Item { id:link property bool dying: false diff --git a/examples/declarative/particles/asteroid/blackhole.qml b/examples/declarative/particles/asteroid/blackhole.qml index f2c9d0a98d..1bc406b00c 100644 --- a/examples/declarative/particles/asteroid/blackhole.qml +++ b/examples/declarative/particles/asteroid/blackhole.qml @@ -98,6 +98,7 @@ Rectangle{ image: "content/star.png" color: "white" colorVariation: 0.1 + alpha: 0 } ImageParticle{ id: roids @@ -144,9 +145,11 @@ Rectangle{ colorVariation: 0.2 } - GravitationalSingularity{ + PointAttractor{ id: gs; x: root.width/2; y: root.height/2; strength: 4000000; system: particles + physics: PointAttractor.Acceleration + proportionalToDistance: PointAttractor.Quadratic } Kill{ system: particles diff --git a/examples/declarative/particles/exampleslauncher.qml b/examples/declarative/particles/exampleslauncher.qml new file mode 100644 index 0000000000..c08123cf44 --- /dev/null +++ b/examples/declarative/particles/exampleslauncher.qml @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Particles 2.0 +import "launcherContent/launcher.js" as Util +import "launcherContent" + +Rectangle{ + color: "black" + width: 360 + height: 600 + Shell{ + z: 1 + id: shell + anchors.fill: parent + } + property string emissionMode: "Falling" + onEmissionModeChanged: workaround.active = true + VisualDataModel{//TODO: Transitions between modes + id: vdm + model: [ + "../spaceexplorer/spaceexplorer.qml", + "../snow/snow.qml", + "../asteroid/asteroid.qml", + "../asteroid/blackhole.qml", + "../custom/blurparticles.qml", + "../modelparticles/bubbles.qml", + "../modelparticles/gridsplosion.qml", + "../modelparticles/package.qml", + "../modelparticles/stream.qml", + "../allsmiles/plain.qml", + "../allsmiles/smile.qml", + "../allsmiles/smilefactory.qml", + "../allsmiles/ultraparticles.qml", + "../allsmiles/spriteparticles.qml", + "../allsmiles/spritestateparticles.qml", + "../allsmiles/spritevariedparticles.qml", + "../trails/velocityfrommotion.qml", + "../trails/fireballs.qml", + "../trails/list.qml", + "../trails/portal.qml", + "../trails/rainbow.qml", + "../trails/dynamicemitters.qml", + "../trails/overburst.qml", + "../trails/layered.qml", + "../trails/shimmer.qml", + "../trails/turbulence.qml", + "../../../../demos/declarative/samegame/samegame.qml", + "../../../../demos/declarative/plasmapatrol/plasmapatrol.qml", + "../../../../demos/declarative/flickr/flickr.qml" + ] + delegate: Rectangle{ + color: "white" + width: 96 + height: 96 + Image{ + width: 72 + height: 72 + anchors.centerIn: parent + source: Util.iconFromPath(modelData) + } + Text{ + text: Util.nameFromPath(modelData) + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 8 + } + MouseArea{ + anchors.fill: parent + onClicked: shell.setDemo(modelData) + } + } + } + GridView{ + anchors.fill: parent + anchors.bottomMargin: 128 + model: vdm + visible: emissionMode == "Grid" + opacity: visible?1:0 + Behavior on opacity{NumberAnimation{}} + } + ParticleSystem{ id: sys } + ModelParticle{ + system: sys + model: vdm + } + Kill{ + //TODO: File bug? + id: workaround + system: sys + active: false + onActiveChanged: timer.start() + Timer{ + id: timer + interval: 32 + running: false + repeat: false + onTriggered: workaround.active = false + } + } + Emitter{ + system: sys + emitting: emissionMode == "Falling" + width: parent.width + particlesPerSecond: 2 + particleDuration: 6000 + speed: PointDirection{y:100;} + } + Emitter{ + system: sys + emitting: emissionMode == "Bursting" + anchors.centerIn: parent + particlesPerSecond: 2 + particleDuration: 6000 + speed: AngledDirection{magnitude: 60; angleVariation: 360} + } + Emitter{ + system: sys + emitting: emissionMode == "Shimmering" + anchors.fill: parent + particlesPerSecond: 4 + particleDuration: 4000 + } + Row{ + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + Button{ + text:"Grid" + onClicked: emissionMode = "Grid"; + } + Button{ + text:"Fall" + onClicked: emissionMode = "Falling"; + } + Button{ + text:"Burst" + onClicked: emissionMode = "Bursting"; + } + Button{ + text:"Shimmer" + onClicked: emissionMode = "Shimmering"; + } + } +} diff --git a/examples/declarative/particles/snow/content/Button.qml b/examples/declarative/particles/launcherContent/Button.qml similarity index 100% rename from examples/declarative/particles/snow/content/Button.qml rename to examples/declarative/particles/launcherContent/Button.qml diff --git a/examples/declarative/particles/launcherContent/Shell.qml b/examples/declarative/particles/launcherContent/Shell.qml new file mode 100644 index 0000000000..32b0cd624c --- /dev/null +++ b/examples/declarative/particles/launcherContent/Shell.qml @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Loader{ + id: ldr + visible: false + focus: visible + onVisibleChanged: source = "" + opacity: visible?1:0 + Behavior on opacity{NumberAnimation{}} + + function setDemo(str){ + visible = true; + source = str; + } + Image{//TODO: Augment with PARTICLES + z: 1 + source: "icons/close.png" + MouseArea{ + anchors.fill: parent + onClicked: ldr.visible = false; + } + } + Rectangle{ + z: -1 + anchors.fill: parent + color:"black" + Text{ + color: "white" + anchors.centerIn: parent + text: ldr.Status == Loader.Error ? "Error :(" : "Loading..." + } + MouseArea{ + id: graball + anchors.fill: parent + onClicked:; + } + } +} diff --git a/examples/declarative/particles/launcherContent/icons/asteroid.png b/examples/declarative/particles/launcherContent/icons/asteroid.png new file mode 100644 index 0000000000000000000000000000000000000000..77480c6f0918da228b57eabe3b5c0807429cf945 GIT binary patch literal 35579 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 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/blackhole.png b/examples/declarative/particles/launcherContent/icons/blackhole.png new file mode 100644 index 0000000000000000000000000000000000000000..4bd8040c3bf430f361dcc5bddf19cfd3eb9161f2 GIT binary patch literal 31296 zcmV)fK&8KlP)%_VS$m&z&%M9#%|pMQrEW>pJ`5CH%| z$qx}hHJ%d)_o?`Essbq12vs$~Ii;=}I;S88Tb68IzQXRctB9#6&%OhhOzHM-(jMGo zSXutXWWP_`0I{6{;E?#t;8p?S+lMMR2Pk3$aN+#84qvwKv!ueLm@v8UB(AHe-}1STQ9h6p_e-c|wS-$yYkT7;)QgI@9 znrP^As?Q-B?zIMt_4jt2Z-1a8fNVYc14`^Hu4f=sL`JJHr%967y(s95j0Y1 zP((Js^o{T>8B=u3K;!E}213c&Ycd9kx5s|D?D=lQKsuN(I=B`&MhB-v@|*JhjSm_8 zA<*h%|Nj3lkjd_m?78sW~jbP`ltHb;YG zP1;o`tYK0Xw4I~wI-JIS8!vU8MGO=w9U@V8Ms+0pASgiuj@1z$k}%Lf1tqnz3de+N zX@p1OHC+$CPiVIBpvMaJBrOxqg)bSyBo+H0HL~KjnEdy6gtKd+0h5ub58ogiFzU&} zcpTtiz=I~;$|LLWDxQAO+!)m1(gbv>bWWL+1&FY0TZDj>)%ZRNUWEGkwH*Q zsB1LHQJskcm$f_s%$(K|@6&!@b9FfAY>s2JzaZm5??K_pBj>kJo-HSIP3;MRJ9*=Z zhs6kGX;A0YoEs0WL~t(Pf+RPY#D<_YgUSd}eo%xcg)Kmpw(CMSS1^R;e!RUuI!s?j@dgH}EvbWEL#DC>UtVrlO=isA^&KY(l_Z&*6l(dQV9N=nEHFjc-zaAE z(5Mh`7*Wcipeze)VTS@Ybtw=wv|B{Nr|+|1{5>iGfcuXaeEEgN76udBWLC7NC+35X z72T1%oH#BfoD~_6Q30oprfF&0j;bt~O$rKQ{4k4u##Wa<@JY`=;o&$d;$3))>hxpp z?j*G6=dUd`s^ zpJ<0~o4O7$2G=wsbv>Zyiu$v@l%>T6wdB-`aWR2WFo|hQIv0n{g@NijnFOpcl%-`- zmN=($t)p`hO01d z#uk?LbpzdUd9pZbK|98bB*r93+O{2nP)-Ua)dVpuP1B~fB#bbifjilxfRR)cV}znC zs3rw%<7nG1sjqTO2H6X#Pv%`i4rgEy(O-)f4 zX446_Xjv>6suh&q?ZL z4IR&<2lP5h?^ISU*|N5$>sh&@Dsn6(i4Q+@FWR0OL3)Z){_{0x?Ki~ZLMP4@(u6*>v%y;%#?CtfmP#N@L zFt-OJ8q?sUT9)j;oK7;Jm0%L~wMiYLN{3QS3f5**tThx~Y-Rod2J znwD}pVQp5itPOS31q)L^ERuyeRKci4RS{#T3QIXDc;xqd6RN^L`_z}Y{#&2p?n_^d zY?4(j-V%)@iOeD+_es|LoX7gpa?(@sK>9@kz&|4@h=s0m93Iz9%92x?>&WJZ{`0O)vxY?bm`jeE1aOfM`7h$3R>v}5n!m?FkwH2)`|aRCj38ZDiy@Z=;LbS6jf83RbTG2LrMJ|a63$Ci^J ziBb7^dvXu;2~x%W_~Xn9!;PVK5>XYv`P#5R9i~s-yFf_Bi-LkOaf3(%1ab3RlX?W860B&EMXzjPlrq zuhFZ$BCwh)lNAk!#2!w@VWTCMyE?$y-yK{90ed7SQl)Mh7K@srx89*QG#N*3JtT9=(y-rnHV4Dnb!^FvSw>*okyN}oIBU9 zj(PLs7$z}3gSws3&yh9nt% zlM`3v0F*7}x*|nEsQn zOk|#Tzv-U^Z%K_zo#O0cOyB*TB%f5wmWxSGV~mF&WrdX0V7!Z};?lFva?msH>s-r+DgRdHIs|( z{r;XuKKhMpKXL`vG~9mi#gSUnkwlb{bcvXN6~~zAg&5P*;2`v%)elC~c641wbRtL6 zpm;MpNp0!twQ3rQYV|88JDIb#cRe4wCc!B?nq$m zuZ!h5tDFF3{MylFcxD4YyIzPQgHsv z7Eiz9Ax>?tQ2u}Hnr8;5}EsfSVkx?`qloz9!DrZe z^-bp2-ejtd#yO6UYnra5m`p1+*QU&uHOsnJ2V#u(VTIW@NN??1M$f9Dqn3bOv=<^t zLS%z7)H)}R(QNR?*EY4|vHSY9EUfai?jB266IAywypzOwNKFwHG<{rF?F`}>rxWpjOvwpBXU1kXV>^Nr0wb)a`_gZ7-icK$rackc#u6cz@= zNoznxt225a+0UY}Qi;B*{4iC;L*Mujs?8bQ?g1AspW}^R{Y@54L)SSb8*3C-E}Ulf zaL&ALJyEPTm%TQ~L`WtY(=3TIGODpub2iYCvDA?qgJ1WtN(zmcXiSOl)n?f3ZR=dk z$!q7&b9DPwxFui!AUPQ&2nqp=rK%=O*Cw>V6(JYQn1pV@o6`^<0eP)!Q9H)l+z z6^(PWb%#TfQ1N*-CKzC0(j|Nn{u*Vuzuyb|@MVOQvcAP5N-5V1gOPw8JfcE5o3hy7 zW%tWpVsY~px91BMO`8@TI5pU_PygOe?j9WDBsIs2)(F(XCvj&Cc&3E2_&pPogXpz@ zEkNTE8X2g8{?L_QPo`?DAk--OFV^z(cYPP_eCdh3vf!Lr!>{0Ij*o)c^6l-j3bXH4 zFs7iIPS{wV(sjzg@tosDo#JMyP#+%QoYJ(8MblDM1tJBVDqZI=Yo~B^-S-2MSuJq^ zK&AoIb=lDLeTp8c@tI)<^S?uhqcVKLEted=b`5uUM7yY2HZ5J}Jg=zI)(vLwaE>WL zroED+L`set&^uIWGP8Ve)5yghFLmwk{K{nU%&!seGjJki2EKk1|4M&*$=Lb3VtoXvu7 zSXmZ=(A*`ESIp>~|K8OtUEO}2bq4D#x$;d4(+NT`n^8$K7nEDw-0s}5OVaAnpW`0CrEoEWox{gKN(RB_@bG@HaDtY~i2F6&{ic<->(1L3XP1|_}3erXdv~#E# zCGRAQ&1->N33xT+d5j9hy3gucvrXI3-MiDSiuXaS~JTGs4zPqZM# zr>2Va)>Tm=7Mg=&x;iN;cQm*xf($4UV8qKy!d>O!Bto-p*luZe*q~@R9MNaL{;9)B`I(Y|aBJBb9Zjyvsrm)U{p$ zI0+V-K<5Wh5kF`ym?|oC>bz4?9o1w)QCN_cw$om4Y8GJ487WLE|22-TA{cxiHZW5H zKVCM9?R`e^kEzy?Y8|sU!p#qY;O++`x}pS9(t?NsHJIx=`$%D#&1Q%xaAHugewBg^ zF&AUJ!^>JIB%rSl^GeHC=ri7Oo$ToKwPvdT1`GbP-ni_!m0XqGenPm#`$iHA=S6fb z?Q-ibZ;MmU2u|r1HQl22w}%Ay$z*~9mTg1bw3M?Mg6)&uy8u|7!*v}FpNXhA)EPf1 zRXs)moVGNLW7)LWP)8BmIGQG8A8V8mNluc$?^KdvcNw%x3m7pQ(y`*~x6kqFF%1brjm9C+PurSqzaCx? z9n8a|IGhA2=YJL|hQ_tHR+&x;DzVHLzAy=6KMXGBvpf(k>R45VOP5^+Vk0q!U47u8 zC%&xc{M($1^?XenpN-B4aK-}K`4SQFYru@5>(omU0#5|2C!(o;5);3$qx}!`GYT09m2`DT74J0kI@wvcxF(vR$YvL=#jr z`GkvTJ9M3I^sK6=#IpVHqa5GgW&f?We6g+rib19Z(aOmI+DeSF;`@q$di%l7gRCp+ z$oWyDn9f1CdoV8+!TRVj?3OU5O+dAcljH$_tlwxNd{UNUkA8b$5mHu-$+W%VS~nicfXL$8vG>MH7(tED0>^cOF~Z*K zS2?(KJD~O$$kS};*r=qKvy(3Ms_P`juQ+nGG>AZd9nXxK)1SzD{NTW>om3cO6Wr); zkFE;|0cL`iexO57epf-&1nC_=7FF9<5Sq7Y^~WGFAnWEcfAo7f`}9*pqo%ixr1zK` z57>3kWrN>k%y{fNphvE*W0}!WoHa7gAw3fWN#R`7#$MF6okQym6GfKGL}@VL`=!qo z=z0!Ky$@U#MISIoTb~|N7@}og2;tV|w8lW0)AkIG^=srN^V> zz&tgKYHNe#!C@pvFU5oa`ujTjJ(henK%oQguP>nOTpv-FK%kWOWBZ189h#!VnF>mO zHJo;G=q|$+DKE!Zc-LQJI(7>Ew@+V`Zf%-iV42fk=ddTT)IN9Hbnf)x56eZk2J@e zZ|4;GUOqtdw~T>hsv;~rRJ^(G2Xe^BX@kzl2$C_QTSW#P(lEi?-0u}8b&|Fbige&y zZ`cJhJ@q@MST#+UFWw;0_ZJ~dNaHE`RTqJ42kIbF!}T#QwHRx0O&fADGlIl_B9rB~ zGFfRMhBH!geF5>vT1uROO>bx7czuRvi=V zczV9W`S!S1+2m>}6VEFddGuTR6Q-E(uJ8RrT>9YqnQm_n1wy1%NTANimhQ{>-wg^U zE*DRI9fXv!1|>~taBV|(u!}oB>}i;eC~PD_nSw4>*l}{}+rENSX@vYvNgk@?K+GPC zTs_{4IdwSq%j7^_`nIl|a-$}Wlkf);bkr8{5)=tDF~>g!Vra%{$C_&g?Jfj1yCpYm z1;}b=Ts+d?j3VFp>WkR=w&r*L$-m01>s%kk=9{cbCaWVeJaW1Mano^Q`eBv0&-V(5 zkhjV$k26N@2`Q0A;Q zVo3iEw1N)W9*Iy87v_njU!-pWc+X~aSW)Ra?mXw~iSmbTkJP4IbmvUudLrZiF#R0^ z=?I4NDIUs^n5OU`sW~n`Hqe(Fqt`L|ux~4VVe2s}*U+j^OWb86D`^0r1Ksj=4kTg( zw&UjXxT1I3`afsSypE{jch^7Q&!iY*1}(l0r@bot{N^^#9Ubv zGLfQU{VI45eS;xnpzx98z-?V%XVZ;lMYIG)1DgCt9fQ1@yagBp7q$x?%X2fjcg0~t z7$HfWK(%pECm3+_dlfKG1DpkVTtE0NLMszCE}mz3`xZq1P39Gi)>KqWz30PVw8M&{ z#hj>qFlV5rjS|LF_&3!Pe6YDoiijD(*n;iH9_Ie5ucJ;M=$S}UNkGJKWDP%h<>650 z(DIbL;*2A`N5&@A?&m>AOjrX&HlO=pCB#aG(<|?xfVCkRDfG2#!PWHP4m|;7((D|o zrFaZFpL0>T*IMkx1*DkJ9p6RkxHI8oG9!*Rc&|f4YLCkUtZItYhL@NiFK3zks8D?4vDep@m zzAM9#R%f0Vm|%jrnCTX0sOyI+H8P6nOf5Z#c|x^s6XWzpVWF%7=nZA9D$YQkC+r@4 zZ){Io*EVj@Aj2~AJ!EUz3U$2W@PO~Rb(41=>|;=_uC4Ra7q9Tb<`xqJfAP*$9&45? z#PEZAw|S+S@qD$OsVvLvy&uD_pP}2k#r&mTCsp7e1u%0zkXY)&Bd&e=v+TWoy;o^T zBCgT^@yAI0jii3iU>PYgFTW9QWQj=`jXrZh;}&=H`70~;oiQX;mpjkm40RD`2P~4T zldN?qzhw+E$*|JsaGl?5!4znHv_b{0I0mnC8l4-+`%zhObIh3hwaObRf;i=o<2irk z>Z^QY?><#XCVTqufJf%X{P>ekvoo!DqF!QAO6{nv;hoDlU#iyAoGLFoN^$z4-=w^J z7+#75GeZ(ZfvA3^>`OoM3v67v0M1L7Z|DyAoZX;{&)U9UGRR3kiMI3JCCC=})fjJ` zrd{Z~KS`{?bpZgY=E~W_cA0EaUV7h&j); z&YbQ~2xzlFmxm{rCt6SOvq}ZN?ePG0oz{k}KrO0FRQaa8d%S;tms!_hR57Yl&hhTU z1HS$4Ef&^t6xBVj6yb~0_1@|t^vn5G?5=wNO-@*q{&V=2Uorbkcf@6F_@|1!GXMY} z07*naRDEsO_&qS&=H*LN8|y=flR(>uL?OK4jnI&JYVce8Ms(zQc%q%6VG#i~d)-sY zpIh6;Zd{;=k}1{|)lK^0dg(KU9RXma`=hTS`G@itSx=?q!4b>-z5d!N6?Y=II4&ou zA?<(noy>4WelqXr+aP*@!g=*ITj5G5o#XN2IpW~YU3oWu`rb7Z;b%6_@#>@+%z<|2 zCiNT7W46!I-g~o$XR#>{#D{&mziiex`-?C1QZ=_b zBp(J7c7P4Uwn<;&cdGOBixGMMc<*>kJCsE;qP_nH?fo~1z(&W>!XUa*@H2tue!$da z;%5-8USY*$M|~7Fnp9=7u}*z>kU-twcsr{ti{GupK*@m|m3=)}uM(WP(T-x1+N zIkpAv#H{hZ7FOs4Zj=)~dGYZuaI4&>MX*Dqz(iYH&!uA2b9BJld{K48K-x z54v3+AM(5Z_@~e=PKGFXq6q27X7kELX6H_`^U`ayi+Spe3IFP7+`ZLjdJwH+|ND4p zZ&X<+4N|lF$Oba|ou_zYnT&%&bR`qSl+v~HXSwjqGn{|-yN42MyiPvWc|v>jeGn;` zz3bbUJoHR<(`fe!lNz5D>Ib00@kG1N2b+6bc1JiP{Pw9c+^edvpFm&jURm;)(`Pxh z7R7nSiEr-dUvc9lKqg2-V4E{t%e(6(&(y~}(k>}9E8Ry;S9E>_9TiTldI=LG)NP7_ z^Y42Pc3QD^{tRH!>KacsztKjtyoauHk9@RqL4|4dfqExKDJpCtuqCSxHg(BwllooA;8S?s6~}W}~UR z4)5}}(c~gO*3U^?lvL8mH|^wy2^&W`z?ld#9L3p((XOT2zdLwdMEF>JoB!$Pv#e>4 zcdnUr{`~3ZIMgZcJUrlC`@7iA@zVMRU)kJ3-uWbmusGPmR#Ogd+)jPylh0h=z3}%^ z*Knm>4*GOtEH6%`p;fWd($g-SPJSAboe%vJ!Ww(^V?4mn1~x97=jiqw|6Jm2pGYUR z0Ox||Iba{+(E6K;#7!q?uOv6C2+7A1tsiGV#&10i&*iWuXP&-a}IL(d83@3)-;^WXYbcZ{BwyO7<8X356x^^t?-lttQxOT7aNbEag(HKt*1r{O5 zDv_&zHWDCt1IKO%VbUq5)lpkvPYFxUeV(A1Owg`|E&yN^uUsSwW##bp-M|NwcR+QD0`QBdkaF=FZA6Lr2Uy2ot zD_da+hRUXN{K=hbeB0p;lVEH9p5y!c*yX2qb7O<$S3Zr{g2KR7TeHM)(MzDlmoD@7?O}wPB1+MWcY$Xh!gw?r{ghRkQ zY2SbdCH%gPKGoYt;Cm}BloN)u=;hYi;ELPA>) z#G5a3zFD$wOaA4#hxzoW3w*pe#WU?L7G;U$|EaFlPo~yUP#UyUwM0!JaMog;VGG`BN9!r=YdMtF~gjb6AukW2gslh^$|s+m9_fHoGoa zMek@DUMdTgBDkjUD>Bsek}U+Bqbd{WEvm?v8YF}XnVShjTaYfQ0}la2$X@%+5byPP zC%eGt``0}5^_8B-np#@A1v%h`-Za<|Q);Krs5CGV?U1#g5u0 zz;@R#>s<26s`~kGp{;$GSKwkj=g(Ywi7Sg^K-q3<{^YIK_-oVm@z*v!!nZEo;)*-s zi<8s*`s6aT$*s72GWoCd{=tBTMwRy;-sdmfc$o|Jg0SNxk1vmDMfjQ1muW=ku=%g`hyOOIL$VX&Hmd`L{NYNHH9N zY-_i~`srd6d8<6Bh6Jp~G*Rf|MUYc%tG2L}Me6+{8dNH8DR@d^r!(INx+Qa;d>Ej;%i=f8# z-APQFS*GfSi$j13${*dk#f5r-4d%T7+pgi`dw2PzQ|H;0^6Nl#v2VVDHw(kFUB{!^ zva1E3D=V4@Bmrs{mOLj@-6YMT!O{{NywHUqyc`S{Ui>13gjF8Foe+kg7}k=|pOpif4bczQV({BT?5p3nYOk33tWfa-96iP& zpyFehQFSGi>zsEE+Mv!%@B49G=f}t@f+fCeM%9JaDA>S2CjjWEsd1)7AgrT%@>PQa zo|9f=jvd+$(mC1TfHz!0N}W|@^U?*@XHy=(^$H)p@fFrx%U37c{L1tq_lqht!~1@( zG)q3Vf1he36*gPtblY&F@@nyw={mo&af*)~+^0~#iR4SO4L-MZ8jXwl!n-hl?8!HS@#@ z>f(aVo_ZHszi-hpSj(5S;dU+|O<)=IkO83BzJQzWqs@|V!Am9M({&Vwb|G+{Y#YU8 z>M$QCTRr@NPY$p22tAr}N#dAXgi3!u(Zmk-=J}p6y6st?A*W_7$xDQm4Z-V!sE*YAK%{z0J?9p0>8bn#kH!!0F4xU zVRM^9vbq%tr$Q zZ(83gE1p~&alWk)g-#5w&er)qFFwwjlj+Hc7e4Wo;vT|<@8LZoODs=zwYOtL_`F$X zAvOd?<63x4w3o@0wP(Hs?1b*%4rx{kUJ405P}&CHPuim~(m+P-gyJN0HtDzKswx-X z;_&%7!9A{)wX>%=zI!Jt+67TB^n3JV%roGX6?$8SGcNux`#XGB{T652MJl3F$A=ep z`Q^3CJYSq8#-8p^3ifT`EtRa4BV%br_`#i9JiR#LOY58b^7dK&uk(*!okwmi^H4{I z5a;@BuYnzzP-B?lqtlnI@+0+|e8z6_S-VYap#s;-iobQ`X})FuK2I(Wy+3N}46jz} zX^Z6{VKn;}z#?(|S7^h&82;U4lZ)!OEhPtGQK(n!Jr*GXLw_>naY@3!l>GNsy$$D;|#O)H6GIi zAMOsBi{%wF!-ZL{A{^R^7iXJ%X7dccv$oBh$%OHGi=G&f4t_~_&x-MiZ(EIJM+#~i z?iG`fR>J+_EvS@^qd~HnK1LUqBE_KdTfsN4vIl6HF?d2;8R8bd zevy3uwJru=b4JmgaWsqRieNX+Lf7<8!3QlJN|}XW_6IXrjPjo4E)TUwVXqrMpa*8c zFKj%_PEiqcdn>}L)s!2PDYZ4cUd{N~Gv~N9o$$x*-DDd4AqC2%cFAw9ZF94#!k|Th zM?%H{j4#0Tr+3OVo}3!aO}q*a^;Xw8ZTZ#WG)pl(1EM&AR-_Nao|rt5-2K1`ic!Bu zr5J-TWl*R7K8c%N1SX297HQ9KlWmT!@fp8!hlEJ3U$Y;p)G&tyQxxHTKPG7(P4E25 zL3H%3#8Ptib9|`t4UMrcr{J1QHDzYIfx;<|O&Us3t~Zv$aud6C5k=9%ofG;r)rEMG zJ2>x|d?CWmZam86_JD_)V~i-rQt}_xFY`vR-s6vO>=y;UxP69S**c9?$4?(U&$>Fw zkzg#Ha>{iWF%LE#Q_RWXm-vF<*id&u(9UR?eb2J(KOB-<8HU35H#?T!PZ z`kl(c_qZ2=K!HgzCc=oYL%Xa9{lU2v_i(~4WFC+## zNl{II``o>-|3jPs3NKgN{BLL8&-)iUY<5e&Qf~2;$u>0x>wK6_!|VhPFgp=u>P=q} zWw$CgzvwNC+vS9pr?bR7wwm}&N1GPc)g*g3s1przYcPDttn&_cOc^GU6XB{^V_yow z{;VrJWj(VD;7|0E2J6oteBZ5m7~q?B53nZglHx`~6|@HQF0qc_U=C~iT=4aTF^O}{ zGel(ejG+KrT=E__KKBic+uYgekq>^dAG{v1$5k3)!+NlMEqRnwNyNMUdHlc$aK31n zL~$r^r?Y8qgQTA`6)X{JMNykEqZpHJbYj>y6J9B{c%jiGWlm~ZX8H|97zwfAywo>(CVSqCPNXe20ZY zxMnKO(Xd5Jjp0SR!9OUkuq-Sesdjj{IOd+KXt8~#Oyr&(>!iODq=fO<>6P^+~e47 ze)FHuo+-MdS0Ol*GV?k~oH+g&C>tC>Vr+=x9Yuo?xZfGBHYcO#k)IT@O*^!ovSO0{ zP8)A$l1orN&^7#z^LsqxIz-?Q;U}vt{#Ch7V{89b7Htd^{-LjxILsy@% z))O~8+T)0vp#GStazTAQ&mB{7Bn6KZ2V64V7JIW>dgrH{fPN{@L`lg;4nIQcX< zq9k-i)%7c4GoPFfV}?DfI}bm^1}rHS5%rr^bsdTcHd63Tt9tLXw#0YH>UbG0nkAIw zZ4McT>Ov$oP2x<&0Vlz8?@dMxhn?a1w!(_iJV21^;NQshS?WkqW`W4T$36u@w;+vcp)fN;aDWs_yBx+Tqtv{E#uCuS(QQ+o&?xtGp; zk>Q1OE@VYUa2?`brYL-3SbUqIgvyO~>wBrBy)hAscjCR!2Gj{f4}kkP6!9T~D|`Gp z#7I9GJsU5${#=K_ogi2&AU^_l0ft$Ef$`G*x$(4D^j6@!>yoP}9$V^o;+gmG`1gOD z&wcSKcVGAl_g;D>UEgpWKX`wikMAFHrfo5lJUic|H4}bz`wZQQlu*K;MQjkxrKb9U zh~xw4)bQKQMG)oDcAvj-{c}vU<8NPjmfxP9TR{^~mF-6^vwqPhdhK7kIiQ8F-EZ`y zu6qFUgpvX?ihx?%!_>H8XxQt=d!k7&#l?DZD4}6oHDImI*J1k3=b|b9OQvgwI``yu zFk@2W-5!0y<-gKgA9OrLh(l-MS|tCH;%p{`rZ-M_Ns5s_MR-dri_@q1zyJPE@#0Va zEO)>1Rbp606rNrz`Of`AP6a2U-<_@EyZ4Sc<+_AP`w&QMdj@_7>NMmDqsgv_`$`xC zfAjip^RDF{kJX3#&)0v8$pd$yHB2_v*}s01y}&#o;&%^un@Ig&k(EX@*EcUhTf|FJ zRp+DiaqshxajJ2bxR?ht&;lgp^sj_a@~Iwfh(N9%q~Uc~DNqK?)!+L>eZUXheThG| z^D?Kq<=`5?-z=|$-$9HfpGDk#uP1cw$0J;-QT}PU#^>#XV=*i-T(t#1Iosk3zxJCe z+Uj3z_0DC(X6I#N^vQG>ww!Rf?Gg@25HJU~qe6#0?;Uhvp?v6R)}H-#HnU|wl`6%` z3ehd1tDW7c&j%Gq|o-#h;0U@;}`8JQn4rF1??5Q4Du1h5&B; zKpfu8!a{j)_5{?CzS`(q%xLSI<6rWW@Al&(>Jpmj4JPe6A9jkWEm)+4GJ1{E2Yg5A z9O1@3fAQoIO_ILH9-nB}6EC#$Z@CFS)b+lWQ{9q@YiMjWEHjz1zD!MyAFq)knBBCF z?<;O|-Yz+I1wYfA=Y~vC47;83fxA2W!TY;Zoujd^)?VlDoPQ?^G1THUw&HLCUz(O& zolbaaQO95dwGqBJt2kZO)FSK|AM0}B_g(fIiw^lm5Gto^lfY1x1*IvxNsR$qm!eWZThb>GI+%kS^7SR|2$wt+=%nP166ox23iex4D zPceL-L+@LtwzpX9?Q>F>jrb`3kzr#14vUh%fBs!OcW{f^2%q0L#hN&l2u*s9Nb=rP z4hd2D%27dhx}Nh#uD`|k`4SYKt0w%``(>~X&V_WyzyFW z=!qCmvZbd66v#gXD!QTc@VECrwv_ViYaSW08DVyNTmE6HD+jE{R6 z>+^b?O>aSrlfU<|0aAoDVO_cwRckK}6~thAVB}d(1*Y5EoPP2NKY-~2{bmLMU>W*~ zGe>m~ayxcR!56mAaBf@q3-%g+*}TU0m^)lDON{Z2&>)&)`;<^;Z|X4XT0U~~K9`Rc zOghKJIW8=g{ODV+a=LA~U6g!rHcPKzuZ{5fhF^zqwmD$K)qa~@1GX$EH`bsqyk$$I zP{jHWhlKdK0oLgFCoRrcG%vWr>*-Veg`N1oa$V zbJVmQb?cZ_6RM&Nb1{Z83?{r{$*4(nCLmo{CS^fU6f{jo=lp9btQc@g*EV5YS9php zWbb~n#{bSls9-U{Z)8jXrbwx-zUdi!Q5j*Nz_9bZJ?xDegLBCTVB(MLS}3s4jt}U9 z7fgk2onpDS%cC;qFUYHW%sfuGPvxh1o}Z!!S#{#>74u<2SJUC#a6 zVPe&)JW(HWuB+K8Oa7NfF7pTW4tTg;^8Cg+Pq-t#b^m3qEO&4s{N$-C80RQMp$hz5 zvCX%3`+TH3!WyN^oW%mHbxR)81-~I%Ax9{GA)D&~?$~b>g-whRq!50X`Pr6&3N&qt z5n-*Gurv*I+j(`U_yIKowBvN|OYdGy1o;<$Ga$~}=%&3-2&Y6b4K6{aKrr}e~1^#z^g1NQ)m5s;vZ}cYL z%?@Q=xGIf0b}?a@fAGvkPj%+RO?F4ZlKy#Z!;0DLwVDG>5N5Ip)NM;?h4r;5wr*Iu zwoh=#)E_~!Rb|Pvns8VzXr03tNxNu_z1`Y*$tgiXqCQGroPLPr)>YDbIkfW&7ozcF zqBlCU)r8LC!TQL(n;#=fEV;q++lK&qEA+M!?^avK@d4RqUE1s!4+hWB@*&x$j3Bw! zR;l^`irA{w_`ZW%7=hbGg~NMG4@|*NZC~b3wKw_c<1g|rj-O|Zj zo;FbEc_)b=)i4-Q!e&-Rbe7_yz#7ZOtYULE zW!~1bt@8@cht_*K+n$j<6GPe$+&Q$ZY3^Lj=q^k)*C}Qdly(sC^(Dl2zX}9!-ur?2 zQ9Hp$CvbFzqd=K#u2UZ#@k8Y!{Ahcde{4>t9kcS}N~e~2y@smA6pL(YWJEr?us|?ttGh>pa{Z(?_M0_q3i_ zb2hesf8`|n4jcS)&htIo;~WhR;dN&GGcNF^PJ&liP7ou#5?AuU3f~7?_hx-cZ~O~Z zRj}+Z3N6f=ma^zLy}8cua>=4@JO)}ng*)D3eNr+hOYR>YvuxX-3{1+Q>H`D@MAI83 zy>L<_EN73nK8j9L7>p@sU0kD|y`3lR69f|1#R+j;Snkovh#We+%3&QS1Q_arBLHqo z$zLumCuw(t<%?|b9QRo3ggXZhv8dPZIVUZrX3B+0&D<1pImQeTZ$DAJ3H_g5`Ecly z^2-|+5F<1yPzd)-$$8h{M0mk$FqeX3DVRn=A>1ft{HyvRe|!2oThj2E_B5~RIx#eI z>?}WDJ;d+Sn>?g7jac4f${n>dF;)~`LL8I0atS=z{-mFb(P{&~EnWB>Q)MMgvhZFn zANUrIo0h67xqSKz`|~-=$riJBd;nhf6s1`19UW1(4W}M`h^;G+aQnrVIlg_jXW-U-p#6Ax@W6%Jkr*P z2-hYRd)5y1YRUsLL);RrC-E+;C*_F0$jf|q-g2~mv2O@MLB}fsKelz2FBMZCON~-E ziqF_?Gpt&|nsF7FImb7;eO@j$xM`*c@WEz>KX!DLHEnsV*x(<|p5$&ZWg?D3ltoAp z#Tw#E;C#8kjUNzC%uEoB-%-_udY$^Lzf}@CPJ)aqgKz}BB=ady!RiqW!E!@kEv2=b z+uY#U#~$W*xkM0_O~WfUZ?kta$81b__YZsrYtsq0uI_UC#n-s|;!EL`WxbC|)u(Gj z{3b+!5`eu?)EO_aQ#-?Z#;gs1Uqe(MpAR3#!B&sNWj!u+_VK#rZ`{7cpWfN!69)%; zcz(?D(;4?;>?JW|&y{^7!Vq5xt^)g%yr5gWV=0_zqDb`56$E)kI zJCNjnCj1SrgN!kzgwyh9cx+elruT?BCC(+6wM$9uq9J!$1Y-niitzqoWotI&N51Fd zy!Y`(_`++~cxz{m7AR&@inT3ne)$b6 z^RRr#zcVddvxf9~2zq-W^}kZbpT2*e=jL-xxsFp^$2T9(`Lp-#1(guZQj(kIvM%_K zmp6H5cLd@UUMZg79l~9m@N&1-r(6W_KXolL?f7s#=i#;?y#yuR635fidU%{*s6jVS zV4($nPKcvGR|m9A*ZSOhukLYsnh zKxParMoy{-(@4P{U1NtoQ|Yl}n}LVGF0G z6~`0DZ(Ml~yZ1Ng7DxQd+QZ2W6FNZ;yvB@^@zvm3P*ED`W->e-r>_q)NZ5!=ASA&@ zsS<;mBqp#by+>XhMl9>oDeKjg-@ST``J%-Z;?GS>7IndX(}D*49BL+x0Q;}{$@=6O{t;HQK0%FbfZt~QMxP(49YJ%FdwqU)gSSw3*j*i$r zTA*rZRXJ|`4jI*?py?c)gGWF14V3F^H1h@Rd=W-6yf`K3-@eWueZ&ulMmQ2uJ8%-t zg=JfUcRm^kFI)*p_+4C6XZ)A{mIF}X$Jyi;H#XQSEWiK2JKBC}V}sAGuhAvzO_|~y zg;8w^!j7HtYO%qeI(-JjCr<5)-_Qh6_6<03Ow{tGt+-y4{QUYB|8Ba;Pi~$j8V_rt zJS=Sz!{8U|JyR?>=!8vcxn?e~&?yeV8u&H~x@It?XAl*o0v@r-x1#KH@MR2*WP-v4 zuxtc%P=DdyVvJ?6Sg?5Uvutl~u~z;TMPa#r=4s^OQ}kBn2$Y4L#u8?Z`P9M5h>Iv8 zV5g8O;a8GoL1R&-6H8fI?jIhr2q6x{PgFW(@3>>5EI6~V#{Gk1uKvid|z_3Y@3ojKCf~Iz4y7sWi*s{`3@As&o7o%lq8j+oI`a{n~v2 zI}uy~QN_eI6~F}p?J2i|702T}t`QL-$_wxC3kV?c%-IdNbF0Wtw`fB<>PYaj$c zUW4Qz4~CH!1Bn9&jx#{AOlyiLQ6wc%Y_gk8_Q>X)hcoT5s@7Ux9@eVbbxyZ7aPK|$ z?7eGP^{sFGfB%nvK0U%d`;a37v5LHN-SARbW2{5GWwP-6=TE=SpNzlEMjXFN&3|RL z)P-STEZ-N)UxXw6c6^F6k<;pc_mA$RF4VmV56e>~XHYbX%^jlJJ-o}&841OBWF(;v zduyFY)Vt8tSc9z&c;Uto*REZos!~3wX&WZv3AbV~LVWT8~Lk|~C8bFL?R90-87T+d6My$|v11ASL`6DqzPR>{C&!!wq#+;v2 zhWTvHwysfCu3f##-d@8uzwxh$cYbCkJYB=#CE-EI+&@|441zsX#pHTgX&u%Yju%S` z=NMH5+a?k-KrYQ&OIjmD1uP*3&Q@!#?$6kpj9F~f_!eAIP!*OCB5ep*YnY72EVfNQ zSsh7$G9#8~L*L5NEY>@uN}kLUeLy4FT#r+fpi0z^XdLA|arBat?ce<7O(s{4c=rAW zw42n$`C3@>->tsSk!d-fK%D?2 zrsa8B8Y8If0NpuE7kJ(Y|12DGecSTI>nqmdRo~L<9kgY@1i1F7K)fb%)TbGC!gH>| z`@yieP#+$ss+1^UM8kYOr}ZsQpFO203Y>G)^_FThBC4|9tg+UyH=A;LNk}_$IIyf2 zN@J>>#_Q|)s48Vq;EZFtZE1ZV6p`s@#AIyPwt)~rRin=c7NwnQDXW+@GWTmjlbxashPB^sqE9cP7Q zW2=NKMj30vEUuw!xif2c**s&@%&0!0jyn~&ZIq81&ySh)&gaQ5@07En$n=?_ts)zt z9HkD+&beO#Z?y2e#^BK$L4VxN`9~jc?b62f(%iqzpq@0h^gqAiWP+O~!Rok*xRg6nv+s2eg?0e6$&KD4I zgy=b5uK3KYE7Z-1v*nsLdd5}3mC1yAC#SSAAW<^wZ8}Z}n(K3t6OEn!1~$gbXRU;R zcda4$VuB!1ajD@dUdW#A6ZtQy1unw1LimeV@(11l0S81esLRc12qzOuFv7`^A(Y8+ zZLQ_$xMJZ7f|%rQplnRVx|*UUa65Raros9Eap;()OYsVAj$%SjFn;m1a*^M1!h5Y_ z=9N!xh38786j-a_Z4YmH;h9Eymf9Z0BE9DD?|p;uPyIM`Q*(H5fC8iOh=YRznx^6W ze8FrsV>}x3yI=b=bnn%V3r9@BhGbVq`cn-vYTaYX-5v^!Pvi|YJJS*s-_|Tm;9xSt zNy)Nl@F8NXW^|+^6QfD9S!-AMXHP8Yc(^L@@rG)7ZJCmD5m-0Eo6ic? ze%JfDQP}ong2B%3_5%@BlhXK|^Zoqry_8FB0r<)C-^_24| zOiM!C?|a*eEgN#A7GlF`yv<_o~7W zF!-qW9IQl1g%& z4=rrhkjbYBb%s%8IT%N#CQ{%tYC@5dhSLWH+Fo2> z6Ft}k%Y%Y-QS!hSd?eTS&b#L{ebeV(@qlFTUnN07Y-ao#eCV^sf5EM*N8Gx7o2yq- z@A~=K87HTwy!qYlusA=XX+7tk|Fs=tX#hi1&7pZW8o)%lUa#Js_3?hn6LqDw*%yBq zcWs8AZ&-ihw=pjHBqFKcIUQBJc>NlRa=cg)+Q_IXIh;;e`<9c%5(_+c^?=j$22tf` zf6PaZ&pBRfXhJ|W5<6O7lcFkGPYfQPBeMh_N`+Hv1#5)L7+jI~pqiMXFA$6v+;9`! zE&?xAk>f_V8C$+23tnc!gp@a!jtkrsOB|biEd)tUtFvs!V>a_Wn%Rh0xD5GB)n(bH z7Tb1G;70{*k$R-YF>;_wW^s*`K-@2Ir-r7DJCGl+5yyNr=1}&TU;PwKHRIv=npxpF z4V#>W&48>P(lE&Ib~0t)!ZRJb-Z}ab?*!!wZ@M`e~oW~JZ(8^Ky(WCiafnNoA9E5ne~=ApFw9B2HPte6pUH;*u$XB59EQ7c-V zAQ+6S(JHb%IG`DgiEiiYrW}ak+=#lK5@NEU#*P`u0meGc)f2-5_S<`iv`8UHSrx^y^gP`A!dY+!1@vFc1WBj$(UgB$SzQf%o&nS#AaV1aAS2S${nwk^aI#P$0 zc{w6@&zUyKyH_%dMUfCO21;u&5@@1Hn?X{jZ*9Uoca@032t}dz2vHl*eJaGJgF#2#+hZG0!`UuC-j_=L2|qCE|-REofH#xk!eP8JJ-_oT=?h3v77 zm|%#0&1_WRRam!)am|*NvaImyZ5nr_5trd9K2r%ANs(d98R=FO8)0lMQX1M2agWxp zXCsdr!#_KjvS{JU(lE;nc7u(YcdB6d%Z+tt<>p1*m-d-0s*`IhabAvCgcXzgxLUV2#B zj0{uP{V}`nzJq!~H(NE04RAdq8k5N=yYmbnq^Yo)TfRF-IwnEshqetMG+l{6&>op+ zB>ybZLtZOqxpuI}H@^2SU;Ey>%&U^gq~h-JF|F=^U#JlBP_P&SZB)*wnUs#GmTino z59Wl%;|c+Bgt`SADV(7chmY|h2u)Ii-U^Hj%nP9w!Tq-n_o)O{G15esNTlS#Jn*DQ zVnmw+SqNHQ5)vh1lUf`)Ro%OTAVdL)1D?MALxO6BxBGR?!-L`?}rkMKlFYBiKueWMVg&@4Q5;;~gtAa#^RHAE2tTYT?DTnd6j z2tf$G02TbYp*$G%Lp@S(8K4QA+JaIg^@|O@KQ7_4S<{AsV(`7SdhUlMqsno6e~*LP zx4HkpJ>LArH#vO%4##z%#oQGgEgA&BsA(4s-B7;%iS8~s|ErzUXEQUH7Yg)V za>r`XNm8x|^}b9Ow&m}3IiZeatm6XeO>jPcjEXG^4)-T)nwpc<8XuBIUuufl@4l1}2VX71OmzqQdK(0)@Cf zfM`M>ieUfy&%g2;cUZ^`2~P%+%1VGob7f*BPsb_8SC1jV_C*ePo>W?hV_O+n)d z!pLV*M|PMi5mXIrQ&Dfm#At}Bgt1RO<5Af9meY033BtKL)-gF}g&7JFoNqSVJ*}y# z3XEZEBJYPKkHd;r4zKgsqpMtm+4jo%AtP-l7+j->^Y|oLTNbWPM#vq|dL2F^Hzt9#OdZxtF>A zxmQ>{dXk6l9C&%{wltv*TNVK4awrW!}r%7;POv1BXPYG?x zX%|+QSg>J3+io~(l#Qgki~=pfR)vY-LNnm;y5@V&R!py7XE9Y4mB7R?vIQ^B_bD?a zv4}9AOt^mM1-^6dDPmgo>y{g>@`5dxwt;rJVlpYIW;3d?pd43}lL@wT6ypiSWJFPo zu%+#_=>-Lq3{ToHS$p*6vSTRgp4b3O)rsWjV4olVdw(5u2IES$=ZoZe#heXRrLdO$ z>4@MX=i7!RB$$RsYPF6b(gvkfPwPFUwG>qc{YVXksu>(Fsdi~ZAjX6p??yVPP*_9h zOjq&Bcr;KVjC-bJHmU1N`fs+N0w1E&_JV+y1W$bDl{srLUP!I z1Yy{Z@bmK#Tdg^_CBcFi%k{mzWY+YecQmz*>z{d^<6^^EQ?tKpIeGky!j*jK##J^= z&C)LkW5;-JpZ9C<#TB-zEun7kZ9rR#wT@=JrrkD)m@?}m=>hffal4&&c4z&1AfqZq z3}Lh3t^fDyoPKbR@zptQR1q|yb%J=5#xN}_d>dFdEm2cK(dUtvq6u0Wa}l(T9?`&T zG@>#>YYnzlqKV168iUJ19bzKcs+?hLA(~`wHAW%{o83iyLSR-JCMs+#D3*~m1Wlkz z`}baX{WnD5v5h=8t@+vcmLH$Ce0JiwwPzR?vfEfmD2Y~4M$~40Au5WZG?SVe4){OP@gQg z`Nh|%PZn&C&j?M6qLj`ut44sb-fRdyQ_cvf3=>l@VV=# zRn`_&O!M45^n@R&&rMm;vThp+fulOoj>kN^aX=_s(uM>iD#sx*Hx*-cz1&gZ>0-?) zDrSBF5^!N>&u_TTsk{Oa2*Rtxs#C55z&9Yz&m8Wpd*Vr8hKBKEU`Im(%ra2rO&m`n2Gc>m~am z%U?cdFdI)91@})kCBL}e@WrZS)R8FV{Ds94KdzfG3J-(l;j@RlG@c_xNjq_ry2RoM z9s89C&I*PSZ5)g`MYA&lQ-yi!IWKI2eQ3n1Cu*eDfQH1Veq1WmOi+8uSVvS$0dO&} zZX!R_EuSh!RMw&jCw0r)PtU2RBcd0gQC9E0&)K~PlzzoO{TIK-u>%R zSFTOC^U@Jt{@lwv{-EJEf9IP#)~mRKYovNl4&QS=Lp9~G6eaqW!dl9*q($*f+K6Lk zF(%UZRJ=@nV!(?c(czp>SlEP=9afhT3w7{}%M#}tmB7Xaq8hx{+}ZrAo4vqN`%yF`s;F&fc$O~^UOj(Exi zM>Y6$pm2&#JVpx&Dp2sXa^42cH)|%-F&N8v?OC^;dQ;Pe4ehhLG;jR?qVV+On1Av= z{5G$=dK>(f)5oU-aXfl@!vFGr{1%EbsS4CL#F(7NrVRw2fL%@FsU~AWH2mPbzvKts zeT%S3^};v4Cj9>Xclc{xevM!M_1F1V|K>Y<_@jrITdyKTbs;yD)*zW5u}~Eb^+Frk zlr6RyAFl?Nhr0xgJyD#*KnQ}fHf60Hpb0Z=VnhgFN@`MNey=KsML=*kE3`gRNASxf z=g%JT%9TC#C(6c5_;A(GhI9?%!m;+sn-4cw2L{8~8O}Bl_qRrlQ7+hBvQpSK><9?D zbO_*Lw;8jxV9@4id3ni8u7^&lnFDU+h%$+|_oP!xu}%FqN)?E~&mEQ&?7y_6YNOpLhJ z6QjvvhM)+M2P?xj&P#lRy^-P7gE_vKp^5!5MNOt4ilgvLLX7yvvti9yyTMj-nqtn{ zIif2uF5p5W=I($`f}D1JOWLNUe~Be$A##7M_`y6_^?|0YIel`<+5M-e6^cnk{i6?= z?jM2%LfZmuo}baCan=~Dv7DZqvs!IgoUaHWCE1J_OzG*;hgPtzV0*fN?Ka_OhPzA> zTiCPr9^qzVrblzk(>ICsCvelf^w|ii0dH-hECg(Je4|MbS3nV;m@z6NOk8U6^C7a) z%+4DVK6z#>2eX2zC|I^|ysc@1Cx(bO24{t%vY<4E^Cpk*j*1hd6y;>KWw}ko)YwKw z8GpX^0Urgc&{}1+$^!!dXVem7NvjQ|T$*a3%BEJ{-x~G{Wh(GtUGZCI6CMYH6XDvZ zDrY!anb2rW8*19%S$n9*Q|i7gxH13$8O}*WK~(XCV2hOWYLysDVrz&+ z4e|-X)4w83tu%M1Gze>2R+TWEtr0cy=&iRneeeiplyYP-KYGTMYuCAc`xdkF1<~I^ zV&w4JA^SH!!`X7f*|P=4K+`n%w&mbp#@;Le>AjCQXDOT|#c{n7lxW^yUP8E~q24LsU}?3W4S0!BtT z_d*?0kyWB&z&-YlIt< z5w3@L<}9m8-Ip2!Z%W#>VPl~!O6u_u!S2R$r#glZnlf?JXghooLupRVY#39<1S6Rv ztkyM~f}C87Y_~P%PoH3<#W{=mh>s0X4d>^p6iwNbnQfyGb2o|+SgkgB-zXsj7R%J) z5)`B+LtE2`B<)HVJ@yE8qkM+aw98g&CYXv%OA4bLf*?Ut(KX|M)ad|b+VLHxa(1Hk z1JnY6>9s4|`ia+B-@QwRa#uLrO|+Tjl-j6175Q9ECB6tI*hTUVGX+ z6}qxQh(e*Msd08RMKP>S7u0RQM{uG9HMpq4!aM$CJ>{@6JXd)tqiiGmuy%ZVU2(T5 zs1vstqr&pssN!f;;EZrEDt4q;&Qpw5*rHCi?j4(zAi)r=rJd{(%j!bXE#a>n&X%MO zhb2*JV#imLg-j8m7)nOS09;>1 zO-`Sk(40TSFPF40zre$HKVtN$mx&b2_oltcS}v0|Oj+iXw-V{%SAmX|WxUIrF4SZ`;5E@~=iWCxQR50Tn2MQW?X@Iy|uP9v>5Hh?9Wv%BoKH9Tc3k{bU0bLr}qBm_C1t zCMrMr)`vX${)Z_+8KET-)!_cD@qBgLavV|``&_2D>o4O^?h%^PzG_@L!z)KKe&*Hz zd!_5MzEi7g8jlnWw#dTMDR5Dw5X$RwWOfVPS|s|urX(1wsc^@e+D@no z4`!XRr}GiaN4P_Wf2@QCwlhP!r;OENiZqSS`UVYw)_aP=Aqqw1xcb@GIceV@`pC+k z!E}#eItDDpSpvf6UcJLRA3os9jYIZkBWz5?M(>p>SSFJ+j-YKlhgXhJERBE6x;aCe zlo?ecxYF?YE6?+#pLvnD-n-9tzqchEzKE%+0hlfIT^X0c^holRG!NSgQE>)J1Z*S( z!ROYboa=@4784tcEAV1yn{6&HC1wp{6oVy32%5H|$(is*m9|ZOniHXPf=BT=GVDe@ zee~uBeDK|Oa}6%F@`M=iK?p?LZ?9Wc8Z%H%DDHe2ckMaM)fZU5@jF=?3)8~!`q7+u z;VzU6j1kJh5N*SdO(P*Q4Qfla;{$v#McOpIFKMXUT5B8<7362DoYuXlV;* zrXx&MV4b1)v||%2oBS*^1WFqSQPGVOHv#VhQ3FL;LaiLV^g641ciEgAvv)Y-`Coa7 zV&nPYw?AaEHzk_X(*2MB(Z9odI%Ql{DW8=WE^17z!rs${RJ`0a9_N0JKl-yj;s5#F zKVdch)&+=!S&n$UQq7!??2iR-#YI--J)i4sOL2tIf6pt&tNNJJWi(ok57s1QObbvRN7 zMiV@?u#|gy_-D_sq^dz6RjJ$}J7-)6 zUepxUF`E^*vcRaLG!T8wan|0TZt^&j!|uS|LM^Dpwkr=R2b+t+!v zZ1|%$@AB;Um=E51lc3Nvfe@6ka7<@2_GdG`^o7@W?XxfQmv4WEdk-El8INci;f*&= z`Gc>1m1P_uvm@l7K(K@R%PVTrZjPkXMN~yR7?%^VDAovdOg-yn(Df=J7%Xa(O^B=) z>s%<++{IzhsBC?UN;+0)3^q*;p^b`Y+-Su zf_B>i5tWb%r&jRB;^K~wytPGYTF}5*jQG%EOqz4*tRd2km&^iZEv|y-ELB@lF~hWF zwx|tzdnG5Xq^wHLp9yb&|1tL;onwoF)<+HwkJxzlC;$4JJXv_2K3Pz>dCr2Thi|-K z&IPk<`J=DAMf{z&3Bj{H^N6_#wq$nu2Gh?Uvi@!!=QOA#3S`;=gNRMz$%CRMq;64@ zwzk*=DDxSnn#nYq~~ z8=L&39?@zn)}@Si8zB%$BTmw~+v~7QaH{1U$Y3}JkS#V=gQe2p6noj=q zNzFTFTlOnWmERu7Zcwo_{k?!D3v=`uv#lAN~MsYJyC7a8FrpD(2U36785J72kP$ zjyIO|x@FUNTD3%RsBuJ-v|+%|MrcEN5Fsd1RERARV`vv^){md1b&IBt+9Vghj3yxv z+ubi2>XDpfV-yopn@}V(G-k_YtVy6QafIyGM3az&R?^)jjC#oaR4S7glRiOJuIIpP=~-9PZu1H zET7(+ra~N02jz#0E#G>w!qo}9dR;jjMMmv{>GmnB>Po`hjvP{0h=HP6 zg7*X&(-uYErizs+#UIy>d@ z2XFKE2X7LamTEd-a&(4Qqi>!-|Mw( z4~V~5d{3^Q!qd%`-@p5eJNp}Mj7K2wWYh5e*_z{RzytTr;r>FI6oEP#e(F;&m6pBr zL#lSk#?4p<%foiW^vZ~R0#$rUjAH^LP=let>I#h;o}AX4#z<&|^(ru#web9mvPhi_ zR#Q^YM?+M>nUwt6ELQyR>tEy9y&o}}Pr31hpGFS$D6d>indJM&hz~^dG^-W(RO323 z+t4Yd~7*+ju~++L0Kf`$*=U zG`0yvf(I;KB=_@3?$|JxRB@#Z8EY+(!U#49sxgU%mE3X#Aq1MPe?R66*F-kn8cC+w zf~ym2eNdlfg>>rBc5%{eJAvu_oKC1j2nbKMfzxJ5A!}3;fkBKhhmQ%Ti^8&j zKfCXVs=Rs?rd1@aPdVP4@#c1le*S0J->Yf$8P`H&)Sgr5dD@{d5L7u{wY>Xq%e~`8 zN+QlH4({BdyfUMjnM`Al$5eMv35lFPxX<_h`M==$%dhb2ul`-;&%a2ko;oH1N>#9B z$=TDVY`3fQ3sHQVj;GyyM^e0;J5MF_`SY~kT?1g&KH4WgW!K>m6HMO7z-P+|c=GYO zS!5BDX?e*)o6Lt%pnw>2hF=rYi7~0ST{UILgXUy-_OD!#^hz>Y4r5Kq6Oqa@Rs=N$ zQ_hI7#f+zfvl9ZEkO$ZdVX{4Qo@z)wnJ8)uuAj=Q(EC6Aq`=GFcNw!BA!SH3V{T=j zxJ>`^`4;~4ZshH!Ez>eR=(AcGfA%w!r%Ud=^N^c+E1Ii@tE!CJNNfXbtDLR_Z$E6f zdlK*=Jy+#^~%`3Iwfj^L_68^1q9flJe*Ztw!3|3^2{9xLdb1%{HWB zK{lOTM`>bbix;xq{X%FyTTqaY$+vY-fstK%P-b#Xnx;#lCMdg!xy>l1hY@q;xy!z% zG)K~ast~Nr$4wojRx}A6k>Ha*lw7Vzqu`@tJd;5r5`5}LjX9ZG+`N4Qv43vCdp1uOY@RGACnYi-G!3E< z1I=o~@sA$SJZUhtq>-A*r;d_8Ac@znS#4Q9JEPq+nc%X!hc3_39rMBjnEXd`UczxRmF5~X-fa%66EW;**m3L>^2_n8H# zTc7OkT}tyvFGNFu8pu@MS-tAU>}rdbzL%h!q*v{`2d-R7Z;ovvfb~pZuue;=~e8+5L-IRgE8|PdrXcdsT5H+te!4dK3LE! zn|^F#B9b(SbJ#)>C@m)Xk{rBrH!%Qu<^K^rEGc}}p;Jog8B0tayJISuUEWHMDNkvV6d6kP4|hwNCo&B{f_+nD=DL=A z@5!p?sMFB@E~d~7keo>DE>hzKx98&OU2>|2uibp6ovP`p*2CA}!N~BtbUHlk#9jzZ zauh=UWB>?FpnkR@tOMtFPg6Teh{PtKKIR?0GbvFOnik*Ys(IdBz4h*Vl-Mg&c1CHy zxRFg!deq79NcW(U?^Jo<^edoI{g1GtLoU;Zi1-hGGVgD1J) zeCMBBl->NrLBh1thLTOzj(RTZfsQPpd+5S$)w43}iSsU$cXk^s`7CizCUkqTxaj!NVg7y>n91Om`<<8 zWX_DH22KpFKWb-OKPEuZpFc_U<@dV_?AL#{K(Ip#c4o4Bb?@uN6Q*&DDaqI$HUrB0 z&=`saz7Cp)Y4T&BIP9ceiTko!frOc2rUfm3<%`EP!xfpLVyR0|j6tYs{!SjXZo_ z0>hzgBDrnb&my$>BG8oEKg$wcphfiCpDbZ?SPY8Odr%nqg` zjo3@{;JSytLa&*+KNmHw4*iM2TdFZ*@Um?*gTr>$ra32-eI@Op#*}8hupjy%dR@kH zpt*a3qizCEcjf)}JF~t!cW*+wR@hFPrD$$9s({@wb`PzW{3LzF?awlL?dNyCL^jDg zZJ#}PlfTzFYy(ZcbJn`!cl_yzEYn?dre^C;kQGGwlL~$bI@KjQwRb(bQe*Fg zb~UXFaMT>}_4TYy9J5yJ6KS1|;DQe~yzt&f4BBU%)0#>Cy7rwB+7I#_(8WXIUK|h($5UiSGAQ zr`<1@j{HV;1KJ1g&^&x^_tp-dxwH45fEl{5qdGpqp7MXVv-QHK6Y-KkLtdm12Rwu% zTTXrel6|j1MR~ZA>0AScu1l7fUBa;UP0Yt6B9F|D{VgUQA44C)b&J`}6f=?qr>m_= zE>K1;sGv?;rcjMe3mx8hj*}1Xr}rceed~Ow90k$)M}tT!kJXu7y8;NCvope`%?>$q z8luzKWJ(!Cazw``n#<&mb$O{zAlba|`YY^?_J4Ih`#%*~n#=;_}sJPLu{VNrm< zMMqjMIHej4F*I}qqK{uz=i`{c{|7gn)lUjT^KR-cgJBz!CP9k%J6&{`or8WvKwopq zp|g@4*t{@;?H8J^yI0A=ogLTCOvjxWzG$+5sGNLoH$Q8$o8>v&Xq_cDI(Q`Me735X zTsva*B0%SPe#NR z*rV$J$(cPb#3b= z#0k^bK!a}h5Hvy76&`9ztX|SWFSxjf2~*j-J!bLK;C(h>pvZ6O}cD- zpN{V2rc27~G;VKAdd<{DgaeJ+We4(gB&BS6BXRY$mqB5)f57uU`9i|vTrR&dsnat~q&<&WmV2P3JSY`*HQ*|B2roSKEeMg1_c#%OaA>Z$% zs7>gFuwM(wCM;a40tl4T3C*@i`01)3)Uh|I=>&tw*9^xxWX+f}`r4&1hLf0MgggTk zv_}+3)Lv7{WI|Y-^=7@F^C~89{G12s!raTR{*m^M7rmL%dQ0=*J;d7FanN~Q;nD%} z1?G@gjO<(-84Awe96N81&d-u^k_g9#7ud^v7a0tXGtdym3>?ObpSj=@bmJ+3PI&rb z^ui;_WXANFp%CgY)VwI27cct4d$L=n$S!xJ(5yC@wO$CD#0DyMaHw4+b@kkb25pv2W8Pg~TT>(Gby>Bz3vDi^Lnb_>_t^o3u@V3D$2l>C6y z(nSF~ziE(}=JG=s3X1Xx7Xv^zzMr)~H)cB>GtKDgnz4Js(Tm4mn&;rz#QYtS1DgB9 zcTYB4Occss4LX6hCULxdf@K|=Tge5=q9ku00000NkvXX Hu0mjfy}fn1 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/blurparticles.png b/examples/declarative/particles/launcherContent/icons/blurparticles.png new file mode 100644 index 0000000000000000000000000000000000000000..7a247ae46ec565353ea05905014b8b45d918d86c GIT binary patch literal 7793 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 McQB)7*P)F606@lu2><{9 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/dynamicemitters.png b/examples/declarative/particles/launcherContent/icons/dynamicemitters.png new file mode 100644 index 0000000000000000000000000000000000000000..0cdef9d21ea61087dadf11d068f09016e6d87eac GIT binary patch literal 9347 zcmV-}Bz)V6P)#}#UF{-d%9l@;0t!Wf1CShcFGe+c(|@af z&Fp47tDad+_t=m$5(L2p383VYsWURdzQy zA)-_WClND^bDFv?s8q7+0RgvdAshIXXw5^a+hy2Q{VNZ)JO-&yqc{-n4&GUN=95k?}E03{Go zvF!urw;du#wr#)|Lkf{NW}Gp&(t!);DuR`WS?0+T!=p!<&PV>{7d_kUh$t0dGccw= zh@b+*K7P+j``zUG1In+DOh}0s6Jh5GXFYir(BAT!=V$n3BKpL|WrGj~Ee%RYoRio> zU|gO6)tOWz?msr%e;~Nt7~XwP@{hlSl)#i3HD+W0#*DC%JfuGqhp(0I4=81F%HT6e zXQI#eKG6R2Ewy!^0#P_#UN)$tQNm%BUjIl z-laY&*=`o(s}B1^ftnZiY}mb$m}^16W28aJkstzI^#7{1=z9UO&Y_J#35_-eq(mS{ z1LQ1_+Tw$v?-D|Qkp&_Ye{$BylcLb!h##oEBmSR?+D?5QfAf!Mk5ZaJ~Vhj>wP3R>_C5*BtT~ib#&K5{x z2tXG>WeKBZIP19SZiuNv$eMKN8Jmv&sznqEVH`R_vfBQuA<9}a3##*%1 zlm|7Ya7d#NN+6X&I*CjfNr@O0aZuz?B4j~K1zOcq^Cjzgj*^NjBmg)JanC!`3(ld!MlT9XT_6gD zt}Lc1IDUMeOyc_V9635fv?OmBZzXE1$`>SQ$);NUt~?e@NVAF6>h_@)D#VtD2}H={z9gNNW8b)M&pkASorH)aX(n zYlA8jvar1KCqLrxAN`0-;Ms>C^75lo#8_aWL&$>E8O%6mrVd!uC+sahpq#%&vIgCr z6SluWgpY})N0*VP1vCRnN5b%abGO{XLTREDj%WanXafh_*zAf?C!TX2x9ZqtqTn{T9XI z5NDw(S41f=<27BeL8zWFW(HYc?mO}>FzgIOfiD`&NslZe=Iwh}A&|eiBL9D{khwt3 zdPHLpy(jGcw@tTsLqmaGH3ySP?8+~FuUlD0cA2NLy8c5!U~B{nv~?^S+XFeNKT40B>d=cxkhG- zk(OFHYNPn+J{%oGt>EJUIv|P1=<$@fDjCF%;r0gg*~snb4e@25tSqB2beEQNmC0d) zoE6C98N~+=>6!*1reKu}2!Rk^C^rB(e?QCp<|>xJ_nr_EA$Yo7kGdI%9|u&M2&Wz6 zW?=VeB8!p!`3^VKh+wFY7_Cr7v6WyO7!>FL^&?PwU_br~`j0}@NJ&XyNGRyl%XsJ*_ zLCz!}@wY9i?+GC=3gCYMSvyv*EmaNNM60C@}hGpGfO0tUIy(yn+aZfWxp;~jcyND4aLpt7dEYKdc_ ze{sq1{EE2qXptyRj<8Bo%x4&7flO+8#%)V?xuLz@Ab@`8-&p0|98k`rK9F@JHl99g zh+Rg?C90kghL*6~l0t{fGn{Y`3%t4^r~)4s6e6K*h1O7OkQwejgS~g)q6Oc=kyX4c z6#r2CTmDb=2_}_@UJ{26pSSe4J@G&NHE2ci**U5AXg^|?YiwbeuNEjZITMuyVdhYU zK}tc6k+j)jQeqt6EWv$qBb_tZCsOZ`QjLIK@(afF|%v^!OkOv4c5xS0W`wZmkT2Lg7 z1EwmeRyD?1q|<0^A!qa$&`Of~k!m$#Y&$~m2qE5R7kvjniJTL$_Xz3G3KAJiB87mG zmTUzgf$v(B)*LJkd3SveZ!~RpN$pR_zQa+|WP@{n1m-}5H9u)9c%r_ZTM5JdnPP}-w%#P2%t>#j!5C>2n(13w}pNG)0YaF3t- zuO}RZH5Wgh^WfeI*H52et)VO&v5UkIi7^mTAf>N2+TRIKCX>hrks&8?7)dFDFi5Ge zDv*pv3PGP6KD+va`P!jPK~Ys49XO7TX6S`tK8N{og3|#GxUq0KgZ|T}4Do`XZ;*CG z6`&9}B`8XVA4mK!l4C-+MC@Bqj1VJ&$lQNM^I!jOcH=&0XO}#G{tTlO!Wu^9a5jA& zuE?ke(G%iVhXCKLU?#+5taF4Mkr`snC}$8=Ng6u>9wjwGCtk*<6dT1!ai0f=2dwTV z=Jz~i?l@RNW1v-#m^7`CkQ_AemW{uevV9wnPGXIro>c^yY<^~m8WLDi$`f>A-!fp$}>*YAJ}=zGt0 zyrN-8P?4f6F;$5U9%UjjcXz!wIpv2UliHrqY-xn##ow=}*OvAsphAyR5>r999dV1y zFoNqP!w5zgQi{a8<6pLeCQM%>hB6AJ1X5)}@?;{Uj8K|jB)uHybwjW_e)MR`qbCc_ zh7Cg=iFuA01o;|t0Uyjkybu5RqUPmwAP~q(BCSDI1++an^+X>jS4X4}@a;JX#rE=+ zlp-kvLbIcLQD91iu0W>5&_qJ?NDc45^#isy1ON2VuNi$tNre>hD?QtH1C;aUhk(h? zhK}4^k-Q@hJCf|lQsc9w%Yt3n(G0f?YQ=|}-|%4neU4ai!wqLIUg4vmY7cn4cb7Fe z__zOl&Zn*8>N7(z7|OY$x^i4AiO3Qo6`&9@5r+h3vX0yH8*<;{t)+K{Vy+OKAZkmo zay-}%y!^yalm*d)(Tc%`-@ESL8&J-ZaV7;s)(ADJhU#umg9Mpa9y(G=>~;-4I)*r~ z^IJN<1TpW#u@`wP#H_3C0R<^i|6E= zNHGyZM2G}LZpH?!N37F~y(G*PRq2?$t#Eb3y&O2bc}~0Wggn7XuP^++6p+XnNk-(1 z7Eo4ms>)&tg%p#Tm4zUsfC>?jGGg@XwmXKtCyUHkzQPMh<2P)EQ-~6uH~avB=HzZk zHFHt2R2r-$bc$Liu0=p)kC2%Z6XZyaQ?WirkJK3%1W}9(?Z_AvK1h5Y82&oa){fho zj2~VxZWB4C$vDT!)qhps_~yx~5J&`sgtdxUU16PLHm@mbhtLXQWNcg101I27Ol0hP z+UqUPpS@)MH^0CWsQ34XFLt0MB+WJ_KABg%w_gAS?XF{U3Mv>#8JiN7EGWH10mP9M zMpE$Plt?k${RKou3IPIAjHD0|L6Aqmt6f5)Nxh&SB7=_%ek7#uB?B$)_Hrh_86bIA zYmri*lwv-cF{?}J<&t_)ql`dlASDJRab`q|L9pLqW4oO*r8$%7Sj zy{3?sS=Rj1-+snb(c|_%AajV=7ud56^KOTa8kYqQg&s3vh-5zyz5o5Ip6^r!NZnNA z%Q54tj8*{|BEC!bKH@|EZLpXVlF3kuyE;m~EW9BCEC zYNVc2t27RkGde-L-SFdLiHMQEx;f?aqf_QxVmCk{aeR=;VvfiPL4kWvFcXfTEPVo1 zuW*+yF+oyhLtR*u5U6A*Ov!+rW*2fMB~MCU4)%nrh(gmxLm2MPBSQ>fQY$}@@}yb< zF_{VE=ldv&Z&9)7D{LZD;~`|uF(AaqeRZ2mBk3d zygXnrljLNH>?YfqGdV|6iiFUSQb3e7!W0PW5Y`ce1TrB6vh2tzk=3*lU!Us)M_{EP zk1qMOjcS=d6zosb&sqOa9GY{!hI9)1Tn`j=%r=pRql?A~h`- z%lYLsmw1FLXm2-c`{&fV78yqBLD233#e7AGnk=93;O&Zsc8!YzGFBR$cdLa=)XXVwA9| zkLbN*^e|%aWQ0^m2_X!O;}&fcV~%9!rgpSa2&pD}72{-Mijo{XB53j*pnO-qZesg6 zhpz|3RQOgXq%yd=V7_vCH>;5=iTlA%Lngr{QggP=ZD7}0|Ow@815sVEO>5zjq?NZ z*s-ETtPJI=lHuBbZz-)pIf>92?Z6m?5|Y*{&d(Mc;c#xBaz9}=GqN6V#RBIwv*jLR zV=4CE$D279XManGJvm9V5oDDSrXmjwSp@XM_t5v>A&))!_7eZeFPZ8s(H#^OFbI{R4_>4>Mb`I5}nk>@k(BTi_HpX2$yDPtkkx zsSDvUdq)-9JE-#DqXm{@0h#y;D1aMg~V@5 zcH07fS#f(>(qElZr~=_E*%WvMGYbUIS$fXJgD2=iO$}bpiGe)!pTo#8kcSR}hcJFEAX%Aja`SmAXK0Pm3RzoZ0m^D} zVL5tl#U4=pK3CM;>ou?joW9R6E2(FR+oeH#e9qgm`@H@5A@|IhVyxIbOSo-?J(#mO z-OzlzVcXndMaA_;p1t}$pY>M+p`dVN6X|v@xxTq#9CtL=XAIqz=H>-qw;}1N`kDMl z$`O$wIc0M2sh9tw39Ewb_nlt};vY&&RG3_YJ zF$?ync!#USD}M1Ozhb;;n3*F~E(wjpWlLo&$?mA@gl{$5^M>{~(f{;>tFwmYSI{<| zpfXg3)MliL#1uIF_~*pXF%COo=!xS<$cg9&VvMBdr!sNM5d8$_PTjt8{YoH|{Axh? z&SK>ZQa}=M3dBUh)sAZ6FiiI6o1_NzfeyiAECBmF!ON0Q+^-pKHP@{tew=VpKr~e4 znxabF%7R}vr|g|P;cOv!DM#YX0o~1q?E022HjKU@)kr;bo9Ef9wA6jCH zq?D$b_3M+0VcI$WlD1FEnJgfOKum!UJXU6e>~Te=V6rnK+@1PY%Zx7%d|BWLN9g(x zg`iu!qPx67jg~$_*LX~^CiGV%m3Wn&;dQ}-cMO-mX&8d19UJ1%({>wn-8JL5B@CXN z0x^30*bv5H+C5<;2G17<8Qug;j+q?N*8|EVa*l-HNyC7Q0^uV%4d@gIOs@Ug06D<_ z&TBPr%;X`q+~e&2J@(F_Y0ucD7C*wa%?x9Lr!QGN*ryq;*{epLsgbU0*tJ`{?-~0o z-R_#PZGbe@b<(6#F?dq&cR=2Jx4$+W||47I;|AuIle`GSk)mY3(ZxDfEWn!NumqfOL@JBHi; zG>r|f<_z02nu{=16?3|?pZPn_1Ia0opNef#qU#blFTTAV*6eND@kKaLQ`K?O6R!a?t8Qi>pRnB0qIP+V!zJ)T=_bu&tO;#NtNfvcMS`?_@ z$h(qI)(myQ*n$6ww#}Quky6|xz0+8;aRyh-z8z4Ek#Rc`mIMB#qrct|FI)0%Wcj;6 z&^L%-QmdXXdcO<^dIA6C`foV8>?l`;kD5!KwLMaJWY{3v2C=JoOW!AD&1La`ja8g4 zEOynBwmq`zx!PVc=I^Ff6C8<93MBl?1^aJCzrN3!OZJ>E^&h3j! zy2D2l-(fL{N!?!Cp4YvW_9cw%z~7!fXZM>+I^od}}~T|%>CXKU7l@-(2$Y>Qj<*9JoZaSYVxDtoF&T z&f(qxxFbHW5BOm32|v4h%Kt7;(dq)L3`Qz6hR8J1BJ$LH%)fJIqLfpYMoY{7`~kIuJbvlozGA3UHgj^7MWCNrd-w!P)z>=lc}ilSWb>cgje{15N*_McAi!u)F> z<==$^lOggO7;mxpmIV!!YZ0PjyW6t!r-)LsINYa<3xc%7bPqnPV7P|igwSoNOv#~H z^UEwz%773^C6Rgh#i<)SE#US4Q@kTF(sEcFaBp_N!TgxFj-POR^pK;&w|M{Hhdg-j z;LRI{AqP6YR zVMIhLs12BfroHf7`W^TBEnUB1<1hKe#V0(zZZLx<_?|}J@X_!o7wHg{J1^e|LZ>^5Fe|V2a>l6O?@I4+Lzr+5Ye8<-FmD)?9TB zeZS+n-7p&$oZY-Y4g3J0=GIQuYQgZ|Aq%P zb8vq$+5-ajRbaj-4p{HQ?iuT&l9St#lW~uu{)Xr6j@y(uZ?@>GD~!}gFHtJd?;4zk zbQ2hdFwFv}NVZ@`g~}3F%t$HHcRhvB7@-g@g9IZ4tHNOD-}N7;9A3ho_$ht#kF$~{;bRtGa?P0itc z&Hn9ztJJd9f%NRCv^iw zDbP-U$}Fs8Rn9p%T646l*xQ@&;NFaPPv+de_ak`x+vR=lyPk)k=knqb{p@2*{SfO8 zF>=J_4=5#weOSrq{J&KmKZ5=h>-LP+2KHiX1WG|P{ z+|q==XbsX?RJJVg5|KPv3g%J~QioerXcIttDhI35an==;d&^*M{3B;?2`fg}_j$q*w^B&@I$$}ul$qy%dv)+Fkg=AHj- z&XXr=_RXC2a>?Fmk3TxOPkFR}{d-fD?@qq|lNW$AblhA#Lz)_6euhykO0_t7NeV}( zu|hX99@H88VD;rd@%KbPSz*eGqF!S31zH+R77Sxzj2)NHPT9wys-Ce?T$hr<)g0vo zqbb>p9;pmETa1=iVQ@ktB~;e)?;pNL=XPx42H_LctYp2iEZ2}$d5TktGbTU$2LJ@% zn`?GAFEGYY8K@X2V@o++QjZQQgYioq)Jw$b9ljzWn&kG1UD6b8#X?6VRdNJ7wS zFhzk?H8;Ccx)fPHe2>NPJBTolqbJ;6fU$h+8&28{!8U}s!qyIvBgb!BibDxY4Y)h+ z(!3cUAp}xOw9{xOFj|1fydG8T`#>BsX-G7iGa9GZInPdxY+TRM98e8QHo@{zz^s^2 zJUnFTb$mgVE4Y4+6cUR@3r$(hut8G_hsts)#^*#c+;S}>V+=SS7@z-=7h2<+hSRab zRx`$KNALrK_e3R;Rx{+l-wqeJ&a*$O2-z`8$LTinR%Ou`?pn%Bl&0k9OMs?1AVp3I zX%xz7+{`jx6&Nc}Hj~11H(?wTrd14WWDt?md$zYbHVe(8+hg)|$98$j+)3tsNz-*) zTwe3Aeg*5_0YnAH(%yQST|z~RO%|CfwKA+-#ZA*AQe+rgT49h<;!~i#3~~%J5C8xK zDM>^@RM^#;kP}Vc5I?)1TV2r(4a4RfJ*#lipsm9Dgd8$-k_*|eD%PBKfxgIh<>0{7 zig;aZzpE%3V{pcxmAY#dN=)U@m8IHKlyglzpYGrr+X3GVgnncw6+1Jc1&m43Y1h24R$*QQib&g$U@J5qT zM2komw`>V0WpObuq=6VUF-CkGIeqafe2!#eSm?wcdqN5*pGg5WqXH{v#7H@l%}R7-DV7R57nG}MI!lO5=>(n1j5%VZKw-gWL?`e* zadUagOZO?*1=*c2FRxgZ4x>xjA5h9rxj75F#8)kG z+^~xyF$knch%uAoK85OIw7S{l|nm(F_PoQhZMDC zy{K8nEr0nR?z8K9{;$g&pZ-5RM1ht9trAj3LV}B%fOQFBCAN^%+Og70>{w!!3eh<} z^3QlTzYm9e9&WGo0ZJ>f&J@Ps$`V(W7*pU1N1ZM6LU7%9x-pVsCLW*02o9!6;n_V)Pc zAN`13YWcfQ{{c(l@{@DMe#7?Sns-h#?dgtY(-E(Fq?lUMuUkn{CNhzbB+oByriYIh zL#b;@J;PFx21!#$%8jN7zu>|6;|Wjz%FN+xkF#)vP>MrS;8jh}oX9OdJW*H%u9}fw zzUYt`1d2um2A{~%VhfAW8f#0enq!o~I7e0mA@^{$A)jyYbA_9g485SaYWU6Slppjf zn7{5B?@fRzJ3~p0mWK6g#mvx+A2~h01uRa@DMf)zmaug6rKFfCwoiYD zCmo%)4546MDRM6uygT3GKVz^BNW;>=H-f+apWW^bj6=P-owldzd1eS`t}NA xHOWI91-C=TvMYJfZ_vX_?x87p$m^K@zX95#?-Jx$t(yP<002ovPDHLkV1gRUvH<`9 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/fireballs.png b/examples/declarative/particles/launcherContent/icons/fireballs.png new file mode 100644 index 0000000000000000000000000000000000000000..39acf8b40d6071e04bd68e3f61022cb169fefc6e GIT binary patch literal 5371 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 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/flickr.png b/examples/declarative/particles/launcherContent/icons/flickr.png new file mode 100644 index 0000000000000000000000000000000000000000..4de2650280899276410f15105f96a592ede72e29 GIT binary patch literal 10542 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 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/gridsplosion.png b/examples/declarative/particles/launcherContent/icons/gridsplosion.png new file mode 100644 index 0000000000000000000000000000000000000000..ec75453df60f750926978ee73766e4a4a82ff3c8 GIT binary patch literal 8154 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 Djop&b literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/layered.png b/examples/declarative/particles/launcherContent/icons/layered.png new file mode 100644 index 0000000000000000000000000000000000000000..a28e3c4f02a578ec66864f7ad3d0bdcce91ceb09 GIT binary patch literal 8572 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 Oh?1O|Y^AhW(EkBSS@X95 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/overburst.png b/examples/declarative/particles/launcherContent/icons/overburst.png new file mode 100644 index 0000000000000000000000000000000000000000..4ef0435991a56dd952c2fd2072f5c4dc527829f8 GIT binary patch literal 1019 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!oCO|{#X$M%K%B_L9p5^#PSo;nT@~SBYrvQm>mVO?mQ+=%?T68A$EH5CnPW?C$8a6W^NEXVb;LW z6LvjbJv_;QO|0+iA@A?X<+lZwG}|56&apVvBupN`C0rSA4%#U?%l@PR$2Kqxtod8eaM+B-?)b zy6?=z=N@tQWduCye#lu`6in;ezTUWzY5#n8_VXWC`ce$T@?mukxQg-h9w4=-^K^$}9rtD$}zVpkq$hlpAuH*Zn>4=lxM8mu(kw{gwl)Kkr_<{v(rXMfiT##ihJ2mgEaCv;@jtzHWbL`hDgZ_y7Dp ze!Ov|=7OldY=PB<$Di+xG|b(l6Z<_+&aR?y{@%SiZ66(BseGgo`Kr3R@9yoH)|m`j zIA)x*IrF>y#O3@+Yff3DA3O2bfAZ5y)?fAtS%2GqgLetf_muv^CjqU?=c(5Jdvre_ zaIVb%Sv9FsWbCXCJ3VA&`{yWiV@F|z*_20T-5-2iy!yAH%!^9q_IFyz^@0vuGiEcd zyV7XCy#Dx^H|i^NeHLBa)3G6xRq+4Y>n#n}{BnJy`fl?czuzBIC7@Zae<(w)_o;Wr zx-fxFZ5lBy5_&3TX-y8&kqlzTZhS3dNai_F@VBjY)8n^H3+~Rf6BiCrP-GP%hgi6u Z@%M6Zn`2_Do&s|-gQu&X%Q~loCIB5kzFGhP literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/package.png b/examples/declarative/particles/launcherContent/icons/package.png new file mode 100644 index 0000000000000000000000000000000000000000..eba8951c26ca74d64dde03268837e36cc94de358 GIT binary patch literal 3163 zcmbW4S2P@o7KZgUW`xO6Ll7|>qQ^vy-i<_vFwuJrX7q>}CCcC^i84fqLn5Oa1QWzW z^xh?5206+oVGzT0*L}GU=RVx?@b7P}y#blf!R&8fjkui8T*{GpwC?hUWnX#6DZTT$WlcEaZLv~#@st;c|c?mL5~ z9H%O#8j*Nof>Ufv2Efq4RIz;yajGw+5e?iG@u@$Fbd&4Zv4+8Np9~{{=hm+cHT&`j zNo5J_0Iz5l(1~OlXlNz>E#{EjTsywpiX8eni4ow_qSmS{18j@qEVkO;qE}Z$`{g}h zYUZ098UGkg3B-0A&Dd#i_na{#Qd=J|(8u(fBh%-VICph?R-R2#AW{^pVAL9NxSHK- zcUr^l)!o22ZX)*t{%K#DSkx*xYxKaD_Mw(&e}LY|Qt+tRJ%rO=?#nyp4a;_BWzhWc zNp^1c_tvb@yL5EXX~0S#yp#VfF@mjAZ*kJ86y*2%^~Snn#5b=M&$aEuCvM~qz(x2L zoz3h|%E*I&0R~Q3)9iTzUucD|Bpzi`?qWAoWV|B+F}XYC4@O6CfxPUFg#pvSYhbn2 zO4Yp%v)Y~HLxS|u=kBVyE0U|^gRcG*;*)wDI6SD0y~J};OTb>49)EN8EG1|wlwi8T z`t^z;**bU0GTM(eKYh&>ALINyn-j^;#B=gAu)2Sq67-EZ(%*%ipFlwIP3}=`oz;pj z7L1a*q47K*R@1vm2|O^9r;Q=VyCt+;(n*fN{6mFD97EkKM>fTxnD5<=yC2xD%0Dk< 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 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/plasmapatrol.png b/examples/declarative/particles/launcherContent/icons/plasmapatrol.png new file mode 100644 index 0000000000000000000000000000000000000000..fda852b4208609018427a567d5576ec3d679f790 GIT binary patch literal 9839 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 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/portal.png b/examples/declarative/particles/launcherContent/icons/portal.png new file mode 100644 index 0000000000000000000000000000000000000000..9c1f910d0c11f82d682222734ea8c6ad9449dfcd GIT binary patch literal 11359 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 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/rainbow.png b/examples/declarative/particles/launcherContent/icons/rainbow.png new file mode 100644 index 0000000000000000000000000000000000000000..8841ea30f993e2641eb034ad74bf2412230e17f8 GIT binary patch literal 6538 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 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/remove.png b/examples/declarative/particles/launcherContent/icons/remove.png new file mode 100644 index 0000000000000000000000000000000000000000..c37714ee8f297a6a96727cce564b9083e8ed0ac8 GIT binary patch literal 2144 zcmaJ?d0bOh7LK5lqF8Y(izM=R2nLYkC6O(FiiA}_2w@RLjmbkICNCsGLZ-+fpb}GZuN3{=$ca$Uyi+-}K|(Jyj^`&6p<%LM7DpD(apl50wnOexI&i>8C2UB_KPC{UU#Kp0ud7Lq9>L|f7lFevE%L;3u- zXt9Ke{*~|lbS!2i3sEu?6=R7a4rpAgl{S=+?j=Im5=_LxuwzRshQwhKOdN*^A+In; zD2UDB3AFbGd{7YGUm%vS1sv3$;Q<2=Bp#1Tr@44KyCB;T3d4y;p?D*^XxkiJDTp`K z%hlP(hvB(o%fL8^d{iJ=vgQ6|OIg-dI|_Uuh|EAmyyGa>M}+aAg_7yKWpklmXg?N1z)iE*3sVXg+e`|EcjDZ2mpRgjuXm) z8CBuj!u4oCz=`ni#+1}#W>`&v__;z2FqH!Ak~r}@sq9wzN$~2aBL9_Ay%@J!o_k-O z`%h#1bu+lZ7~f!S^F3Gu`yb4={9tYWBYsPx3E?JK+gzLQ#AcICw+O`B1mYdjO?SW^ zu&0F?(Q0bZ>gd+GH>T|n+HPvm?&#Kmx9(s@cI-XSX=c&s~t>Re*(XG!k#*T z@&KQcy?v8?{jZe;Q~1qOR`ye}RM3v;i2c*5!Wn|~j6H2eo(DLejod$%kv%8R(~?j3 zoR5r|Kfsxnr2@|9=jV4%ZV`hvlO@dHFr5Cnb?YFA=obPcD(a9>nwF*nqACG$;X;L` zp`qdDd%ynra2UwsBnevMg~RDo`7=COE_)ISr}nK6-H=VbvPPr1lD_-Q?JF7ahk3=S z;@!oikEvH#HpRu@$SXg4wrFp0k$LLTC%?Iw=;=MMI5#}iGJXE<$(!_c>71tZ@2fS6 zziz9@wC?$7tKydpgtnYJ*CGnuuQcw{nM}A?<#z7%;M0-bnVFg1kuxUcJ1L_U{r9?? z$t}&@2P0zbZsa~}8hZ6D?z%-u+xA9s`7A>*v18TcRK^zG{@mGg{IQ0}%PBjLd=;2W zoHlq~Z+Lg@+S{<^qdBv#m}Ek6Q4GGK%yitS>*JErv56L;^!!6FOM^Qd?|t94^7E^; zwaKsxsZ?bUJ2WsbaI-z(AB}zEvf{X7KX!Tcz1B1RJoBe|-SSUEjO)`XjF@Hb^yySr zgvc#zv9!_^w=RBIWvFY&8f&>E(=Xbf{t6G3P=2!dwY8abzA7+ zYlog(t0kI0$*D7OcX_e=;JN6#&q%^s{{L!f z@=JZcOf7E@Zx8&UaQcMsjGtmLUdv)0QG zojmn%xJwDk*V)u5UA6y4+nG(tdDOO|t1qW>e@5bJ^-ra&%aS5>uuWuCdln}&hpb+K z+0|tk-518uT9d*G9I_%SiD-P+YI5tS^@o4f9G88)*LQhmnO| zd4QlIwf6U+ShO^q*m`;OBSJJ`e0*FHVM1njyt@y&HUKncPsOmCb5KmJepE zJK4jye4xyD#TPAcw#2h_y*fFe6^28hD`PS!%^x(L=LWW}`efRIv3>UIPW^yv?SH<% McQB)7*P)F606@lu2><{9 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/samegame.png b/examples/declarative/particles/launcherContent/icons/samegame.png new file mode 100644 index 0000000000000000000000000000000000000000..d00698264b8d56ac054b97d09b0291a045535397 GIT binary patch literal 8647 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 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/shimmer.png b/examples/declarative/particles/launcherContent/icons/shimmer.png new file mode 100644 index 0000000000000000000000000000000000000000..0ff53196a6149d65c9d7aa18baef61090e4f73d9 GIT binary patch literal 13670 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 DW!2A~ literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/smilefactory.png b/examples/declarative/particles/launcherContent/icons/smilefactory.png new file mode 100644 index 0000000000000000000000000000000000000000..78ea52790fcde32d4c5cb466da63867710f20b86 GIT binary patch literal 2863 zcma)8`#Tek7pA!vGI9yIL?uL%Tq4rg5^^`2RwS}03^B|-x!($Nmyk;nk>#$*Wf<|< zglTROViLlbkIz5weZD`u=REIop7X;w=Y7sOah4Vke!kOuY;0`&x1eC_BMQZ>s*IGqq&Z-8-} z(8w|#`RSOyDY321!3^X!@Ga$+`vQ3?c>C8lx}BM~sJE!Ig4WiklsKO9?UFw?YZ-P7 zcc0faQZbb`)9~E?B-7_C@7~miy)D^0=@6{6hwi&jgL1v&qkq0iy2;nzM^PC*?ohhg zeV03I9g-A9vJogs-!z`->fyZii-i;+Rh`~dVO*&Rg#io+Q&~^$h6GKMPXc43r`-XC zM4Z*&E_0}Ex!t{Y^4?-??NK7zAy2JS%w_qea)Y^8ju(bwWiPew=&`hI?LYU(1h;k4 zDU}=Bl)6J`ezAHhrnBC|drhikuM)nN(RQIFXs^zct?5~89f*e~Jc`?y$h9jXCP`ms z+_!V1f;`2|3I%M+dKq<~7jZ(x9mYkU&@)?OQ<8loVDWY402;zV*;8`VSu7XT(ej%i zEzAZD_s`7?Qmx>$*~H<_S(;zQVy>uYJPe0>T`!b1Qh8LJk`A@wrkca)f_4_hF8hqW zUgL)c+$W?j_Ey!~+fIz|tMmuYtn_*BWMzaEb*|E)dEbZVwCsau7-hd@SDJ~V zL^X^Zi=C~$X_UA!?g^3)iBPPvzY$0XE=>pSX6Of#28o~NADGyRC@2`BE`q@weHoTW zd_j%wkLiMlPL3UBwieZBZr6?KS>bl5wneV%g=TATQ>)W(i#d-6CUKg_zXt+7dZyj+ zZ!j~%V%Pl7p6xs@7qKvyyLBGRG`A%W#nPN!B*g%@=bfy9dONbTXN6;j{HeV6iCn$C z;b#I2SX*Fmz#a!C^dJt!t#MDE%sRVuLb_6*g!%{-Ia)4v#TLt;DGA~$P47LWT--G< z5u`5?1wAZGt&e~7ifUd1F}yuh%yJY8-(L8$$9KRa7U-)hexpym1N$p##M-TG0RB04cChRdeXr$t_(mFkjM5U8?Z_^NM~_uRgcgSOUHvVt0-1g1jTOSPv&G#8bBs}_i$X#|WL=UgKs~6AnNDoD zTW$zRA!SHt@&Scsma9tHONupoC3`--NTv6&!TP@@BN|fxN=J@SHwAJ})yf>o8lWV*tg}$xk-V1v^*=!1XUZ2>^p2>*w1xW@2T^Z2Hr6u1#ta z2_6|21^k>czf$7$IFpqfmcM*j{WwXe{-JbLtKZ*!$TaZv3jlCbsVYPg`b)$0#R;*P zB3Y$iRuEzNVylcJ!CUSzwc7dU5|Tk<;~ctFo#xkD#w%mZ+;4kQ64a~%7xSO>4h^|? zCrUJ}L`H6Q&P;c1w4ciJ8P3yL|1&fjE)1MbQ0uBuL8?^EFd12xh=>RgQBmK`pH_7r zKKS!L*71X?tQZv!T2QG?2&Ns`i7JMw_Fa#@cK;MBlFuCM}oGf+y6;5ZzX%x zF2{`_ql^=5ZR*E8uV`wr1C?LBT9U-B>G&ke;<|8iEl`DqG)it&9_|bFK%wkwonJ)= z+kfL6DLsBlwQOPWJXaCF)SbN4cWSE0_7?Sr@z}hJvwnntJ}0pUGa+^yZv3&Di??L5_L@djyho-VSu;6-iQGAz)zp+URcPMMnR2_0$)e_chKZ4_1z4f^M)T+AV6{s%)FQxlN;B3jj z;{N=}?(Sz4$JSg!K2ic3DOUy`{k+mhX{Bg}NXc9Of}JE%9K&`?6%GwItd^Dv=s_T$ zNoR>6{OFy*i12~4BLEK};29TqN+dNtJ>4}+QC~WMaTQO`QRhSQzA0+?Hl73XX$8Qv zYn1^H5aWabax~@Ck)f(RX&c%-xgUDOsGtH{+7lz literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/snow.png b/examples/declarative/particles/launcherContent/icons/snow.png new file mode 100644 index 0000000000000000000000000000000000000000..d98e14b8ae2bc4cc9983747ec25f158cae9ec232 GIT binary patch literal 5858 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 sJp$hl^ay-I&?E2-L65*U1U&-(51)>{l5>HmC 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 FV1lzyZ@2&e literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/spritestateparticles.png b/examples/declarative/particles/launcherContent/icons/spritestateparticles.png new file mode 100644 index 0000000000000000000000000000000000000000..dd01518ac41a2f4f37a6128191bfa1bde4be8aed GIT binary patch literal 2565 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 beX;)o45SEtF7&9500000NkvXXu0mjfI~g9C literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/spritevariedparticles.png b/examples/declarative/particles/launcherContent/icons/spritevariedparticles.png new file mode 100644 index 0000000000000000000000000000000000000000..495bb6beeecd20dec4fad05bc0e2b534f238b783 GIT binary patch literal 2569 zcmV+k3ikDhP)ro&VcZ1sBX#g)6_<`OmR_ z1}siu2}5!M%ga;9&C-ylRmH3y3Em&tuiL3T+#7Rp0D1?UeDvR5u6EjE+7FHU=7*oG z964zos#v}p3JYV>C(fUT&pwm;`o;n7FVRz!uZu}vh6u7)6*_M^hAJjaf@#xY(kHsQ zJolz<2NtUmSUAc^2rlyfjoSnT!EJ(qKoeYKB!2k?zWma+EGrADsz%Bx3Vi;#uU!Fn z=pir~NAvcvMc4%W{4+E*!tvw2lvu7yuTF5M{T^y&Dx3 z70jA7i_XqY>gwunI-Nf4#VbZ-qyZ8V5|}Y#201x7bar+ImCw!1rMS45_V)JCzA`2l zH9?A^aOKJs`uh4pBFJPi(c0RI$z&SsD`SFD6J)ho`QU>OMwF{ma=GsnSCr7u&_F{& zgJ1c0Mpy(r@(84+!dGAUuUD(#i6>y@Od&?Ag^xZ8+}@HUGKGcNW7QV)^-^8NI!}<&!4C{rCHp84OTa z2|Yb9+)OQAEQH59!Y0USg_IO{{Bdy#6N2cVN+nw4hSLfC{hqdTv03H`8DSH2;)KLu zIvqUybofK$`gM5kJt6Ahhhg5laC{ubJA+~gBWc`-PN?8|*XN09eI-U3~YBhZNsi#~I7=aw1 z8*p#bXn+jh8qf)N#jaFW!<;$dGQ=~&p2dttC@qD-LHT^<4D@$^4p0HvuD<{ZLYB_; zqA63vc?2ja@$W#!8%7n28I7=PncVAiaH$EBEPw*3-Jo%)kX;L18ITDi>EXAf5?+ia zj5=6#qpAw}ilO@wIPLIb0}S>^U0NESMAWj;7v z`;Q5()-z{-ejE%Ze~1V{YAqyXLdI0+y$TN3Dxo?N47oy1LYy1pK!^xI0BRki=7Pfp z4wnJfB!OXaq}~u4M2sL1Taz(0k}bv_w|7>9U`T^+hl#W4L2h7q*$pWQT`voL5= zxY6Up9EHTCjxHw-C*)-ZCTZGaEi1}X&}w48C)R?0Yj-o5JSJP*C8mj(jI;YoV^r$-$!o{KlwZ z?o9tA#S1h50~QB^Hqn;6yyzxxx+D0mJ_j~??6YZu;kF+X1h)wag0MWWEn8%}rnObp zrk{Ua$~!t>-#*amWl^lC2p)SZ8r>axgiO$zZ$ev}>}*&pvh2EPlhj$QvJI4w0PXFv zi>g+`qmM?b+hd222->#ImmH97pPzp6m2)~J7qPX~)wo9X>&G)fBFIgI&8Fs+S2kj? zSU7Ot05+SAj*brU^Yh8d%HqKXAEc$Fg+qt_&cJ|gOf_B+mJL#=R1_2xU^bi4>-DJB zYE&u}dc7Wl!9YPl0lmYs8pb)oBFN!zuyyNJ6h-0s_3J2#g4JrJrKN?owl*$YxPZkw zF>wMQB!XUk8QywJ((W!_zC5DdY&K&yo9XG1)%vtF+1^>cJhGjQ1w!(J?z|IfYJ5Fx zg8?>flxy8OPkC~(+@Y-uoUJg$^73nU+ANHE)>5pz4x9f#$ z)M{1mA@uqeFxXH#&3cI*(%vDt*E zrl!zeQeG};!I8ip|I$fUuaL82NAwTCtX~g3{m$S76&J(Ko$&0l9!&}bWn~h~=H>mG zZhCN|$Fo3gG|EH}O}G=>Lo(9$+ylFIN&NM~3%8cN>nw!pxE=kumbkTh(``;9j|$zLGu$gOgByHU)GqJbK=FLK6pj5yKvE=&BJ8~}8F>Ir=sY!?%9F&=_v2mo#>C^D` z+j0QVA0`ArCr)_2;D!wwsHv$T$)z@Kt@75c7himlnwlE4F12ZFl*K-O&;V4HrqF!Z zPTMsb+qX+h6)m_O$ECkZp)fZw;_WY3umFR>z@9yO0?I60xR9izBzErHiOpuaWqTTc zsgt$*epNcV|J6-%bM$|0rK&8Mm5WlMl9aG--#)ZjErZ^cco_EX-HS$}2}%xl4Y9&p zEzhpbU{Ypu?e>z=WU3dZMwFZ1r|%UjM8=z-)vE)O0~5#(I(bqS?n+BVV*+9IYR~}dJKm`2{Q57uw*bM?v00000NkvXXu0mjfv8C$A literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/stream.png b/examples/declarative/particles/launcherContent/icons/stream.png new file mode 100644 index 0000000000000000000000000000000000000000..a74f9a0340fbea79c842ab0c0c6c5f8b396fe2d0 GIT binary patch literal 26560 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�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 Da>@rR literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/trails.png b/examples/declarative/particles/launcherContent/icons/trails.png new file mode 100644 index 0000000000000000000000000000000000000000..0337ebcfd8d380cb342be189cff2bc1f8bc4ac3c GIT binary patch literal 23168 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 literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/turbulence.png b/examples/declarative/particles/launcherContent/icons/turbulence.png new file mode 100644 index 0000000000000000000000000000000000000000..2b9446ea900076d9efd7bfe3cdf01f8dc5dd8c7f GIT binary patch literal 9684 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 Ws8u2a^mx<&0000UbL?z5Y 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 RgB_%TQ<77Yt(CS2{SP;_&DsC} literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/launcherContent/icons/velocityfrommotion.png b/examples/declarative/particles/launcherContent/icons/velocityfrommotion.png new file mode 100644 index 0000000000000000000000000000000000000000..79baa0829332af02e440404ea4933041d7d5b13d GIT binary patch literal 36360 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 gG8s`;8F=`A0HA~$*A@G*1ONa407*qoM6N<$f Date: Wed, 8 Jun 2011 16:59:33 +1000 Subject: [PATCH 10/48] Don't crash if GL context initialization failed --- src/declarative/items/qsgcanvas.cpp | 84 ++++++++++++++++------------- src/declarative/items/qsgcanvas_p.h | 1 + 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp index 16bd8ce44c..8caf1856ab 100644 --- a/src/declarative/items/qsgcanvas.cpp +++ b/src/declarative/items/qsgcanvas.cpp @@ -222,29 +222,31 @@ void QSGCanvas::showEvent(QShowEvent *e) QGLWidget::showEvent(e); - if (d->threadedRendering) { - d->contextInThread = true; - doneCurrent(); - if (!d->animationDriver) { - d->animationDriver = d->context->createAnimationDriver(this); - connect(d->animationDriver, SIGNAL(started()), this, SLOT(_q_animationStarted()), Qt::DirectConnection); - connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(_q_animationStopped()), Qt::DirectConnection); - } - d->animationDriver->install(); - d->mutex.lock(); - d->thread->start(); - d->wait.wait(&d->mutex); - d->mutex.unlock(); - } else { - makeCurrent(); + if (!d->contextFailed) { + if (d->threadedRendering) { + d->contextInThread = true; + doneCurrent(); + if (!d->animationDriver) { + d->animationDriver = d->context->createAnimationDriver(this); + connect(d->animationDriver, SIGNAL(started()), this, SLOT(_q_animationStarted()), Qt::DirectConnection); + connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(_q_animationStopped()), Qt::DirectConnection); + } + d->animationDriver->install(); + d->mutex.lock(); + d->thread->start(); + d->wait.wait(&d->mutex); + d->mutex.unlock(); + } else { + makeCurrent(); - if (!d->context || !d->context->isReady()) { - d->initializeSceneGraph(); - d->animationDriver = d->context->createAnimationDriver(this); - connect(d->animationDriver, SIGNAL(started()), this, SLOT(update())); - } + if (!d->context || !d->context->isReady()) { + d->initializeSceneGraph(); + d->animationDriver = d->context->createAnimationDriver(this); + connect(d->animationDriver, SIGNAL(started()), this, SLOT(update())); + } - d->animationDriver->install(); + d->animationDriver->install(); + } } } @@ -252,10 +254,12 @@ void QSGCanvas::hideEvent(QHideEvent *e) { Q_D(QSGCanvas); - if (d->threadedRendering) - d->stopRenderingThread(); + if (!d->contextFailed) { + if (d->threadedRendering) + d->stopRenderingThread(); - d->animationDriver->uninstall(); + d->animationDriver->uninstall(); + } QGLWidget::hideEvent(e); } @@ -460,6 +464,7 @@ QSGCanvasPrivate::QSGCanvasPrivate() , hoverItem(0) , dirtyItemList(0) , context(0) + , contextFailed(false) , contextInThread(false) , threadedRendering(false) , exitThread(false) @@ -482,6 +487,11 @@ void QSGCanvasPrivate::init(QSGCanvas *c) { QUnifiedTimer::instance(true)->setConsistentTiming(qmlFixedAnimationStep()); + if (!c->context() || !c->context()->isValid()) { + contextFailed = true; + qWarning("QSGCanvas: Couldn't acquire a GL context."); + } + q_ptr = c; Q_Q(QSGCanvas); @@ -960,18 +970,20 @@ QSGCanvas::~QSGCanvas() d->cleanupNodes(); - // We need to remove all references to textures pointing to "our" QSGContext - // from the QDeclarativePixmapCache. Call into the cache to remove the GL / Scene Graph - // part of those cache entries. - // To "play nice" with other GL apps that are potentially running in the GUI thread, - // We get the current context and only temporarily make our own current - QGLContext *currentContext = const_cast(QGLContext::currentContext()); - makeCurrent(); - extern void qt_declarative_pixmapstore_clean(QSGContext *context); - qt_declarative_pixmapstore_clean(d->context); - delete d->context; - if (currentContext) - currentContext->makeCurrent(); + if (!d->contextFailed) { + // We need to remove all references to textures pointing to "our" QSGContext + // from the QDeclarativePixmapCache. Call into the cache to remove the GL / Scene Graph + // part of those cache entries. + // To "play nice" with other GL apps that are potentially running in the GUI thread, + // We get the current context and only temporarily make our own current + QGLContext *currentContext = const_cast(QGLContext::currentContext()); + makeCurrent(); + extern void qt_declarative_pixmapstore_clean(QSGContext *context); + qt_declarative_pixmapstore_clean(d->context); + delete d->context; + if (currentContext) + currentContext->makeCurrent(); + } } QSGItem *QSGCanvas::rootItem() const diff --git a/src/declarative/items/qsgcanvas_p.h b/src/declarative/items/qsgcanvas_p.h index 6b8034f922..9d37391f57 100644 --- a/src/declarative/items/qsgcanvas_p.h +++ b/src/declarative/items/qsgcanvas_p.h @@ -156,6 +156,7 @@ public: QSGContext *context; + uint contextFailed : 1; uint contextInThread : 1; uint threadedRendering : 1; uint exitThread : 1; From c8a5c00ec1c06f7c00070d55cd61cb8d52dc67cf Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 8 Jun 2011 17:55:41 +1000 Subject: [PATCH 11/48] Immense Particles Refactor Part D Changed the names of several properties: FollowEmitter: emissionShape->emitShape emissionHeight->emitHeight emissionWidth->emitWidth Emitter: particlesPerSecond->emitRate particleDuration->lifeSpan particleDurationVariation->lifeSpanVariation maxParticles->emitCap particleSize->size particleEndSize->endSize particleSizeVariation->sizeVariation ImageParticle: image->source And stopped being silly in the example launcher. --- .../flickr/content/ImageDetails.qml | 6 +- demos/declarative/flickr/content/Progress.qml | 14 ++-- .../declarative/flickr/content/StreamView.qml | 18 ++--- .../flickr/content/images/noise.png | Bin 92003 -> 25447 bytes demos/declarative/flickr/flickr.qml | 22 +++--- .../plasmapatrol/content/BlasterHardpoint.qml | 22 +++--- .../plasmapatrol/content/CannonHardpoint.qml | 16 ++-- .../plasmapatrol/content/Cruiser.qml | 20 ++--- .../plasmapatrol/content/Frigate.qml | 18 ++--- .../plasmapatrol/content/LaserHardpoint.qml | 20 ++--- .../content/PlasmaPatrolParticles.qml | 36 ++++----- .../plasmapatrol/content/Sloop.qml | 12 +-- .../declarative/plasmapatrol/plasmapatrol.qml | 10 +-- .../samegame/SamegameCore/BoomBlock.qml | 10 +-- demos/declarative/samegame/samegame.qml | 6 +- .../imageprovider/imageprovider-example.qml | 4 +- .../declarative/particles/allsmiles/plain.qml | 8 +- .../declarative/particles/allsmiles/smile.qml | 6 +- .../particles/allsmiles/smilefactory.qml | 22 +++--- .../particles/allsmiles/spriteparticles.qml | 16 ++-- .../allsmiles/spritestateparticles.qml | 8 +- .../allsmiles/spritevariedparticles.qml | 8 +- .../particles/allsmiles/ultraparticles.qml | 8 +- .../particles/asteroid/asteroid.qml | 30 ++++---- .../particles/asteroid/blackhole.qml | 38 +++++----- .../particles/custom/blurparticles.qml | 6 +- .../particles/exampleslauncher.qml | 70 +----------------- .../particles/modelparticles/bubbles.qml | 4 +- .../particles/modelparticles/gridsplosion.qml | 8 +- .../particles/modelparticles/package.qml | 4 +- .../particles/modelparticles/stream.qml | 22 +++--- examples/declarative/particles/snow/snow.qml | 8 +- .../particles/spaceexplorer/spaceexplorer.qml | 48 ++++++------ .../particles/trails/dynamicemitters.qml | 18 ++--- .../particles/trails/fireballs.qml | 62 ++++++++-------- .../declarative/particles/trails/layered.qml | 10 +-- .../declarative/particles/trails/list.qml | 10 +-- .../particles/trails/overburst.qml | 14 ++-- .../declarative/particles/trails/portal.qml | 22 +++--- .../declarative/particles/trails/rainbow.qml | 12 +-- .../declarative/particles/trails/shimmer.qml | 10 +-- .../declarative/particles/trails/trails.qml | 22 +++--- .../particles/trails/turbulence.qml | 36 ++++----- .../particles/trails/velocityfrommotion.qml | 40 +++++----- .../toys/dynamicscene/dynamicscene.qml | 10 +-- .../declarative/toys/dynamicscene/qml/Sun.qml | 2 +- .../ui-components/flipable/content/Card.qml | 2 +- .../ui-components/flipable/flipable.qml | 4 +- .../particles/qsgfollowemitter_p.h | 8 +- .../particles/qsgimageparticle_p.h | 2 +- .../particles/qsgparticleemitter_p.h | 14 ++-- 51 files changed, 390 insertions(+), 456 deletions(-) diff --git a/demos/declarative/flickr/content/ImageDetails.qml b/demos/declarative/flickr/content/ImageDetails.qml index 46827ae3bb..74346466e3 100644 --- a/demos/declarative/flickr/content/ImageDetails.qml +++ b/demos/declarative/flickr/content/ImageDetails.qml @@ -242,9 +242,9 @@ Flipable { width: Math.min(bigImage.width * bigImage.scale, flickable.width); height: Math.min(bigImage.height * bigImage.scale, flickable.height); anchors.centerIn: parent - particleSize: 4 - particleDuration: flipDuration - particlesPerSecond: 2048 + size: 4 + lifeSpan: flipDuration + emitRate: 2048 emitting: false } CustomParticle{ diff --git a/demos/declarative/flickr/content/Progress.qml b/demos/declarative/flickr/content/Progress.qml index 30142b4e29..73a91a4aa9 100644 --- a/demos/declarative/flickr/content/Progress.qml +++ b/demos/declarative/flickr/content/Progress.qml @@ -63,7 +63,7 @@ Item{ color: "lightsteelblue" alpha: 0.1 colorVariation: 0.05 - image: "images/particle.png" + source: "images/particle.png" system: barSys } Emitter{ @@ -71,12 +71,12 @@ Item{ x: 2; width: Math.max(parent.width * progress - 4, 0); speed: AngledDirection{ angleVariation: 180; magnitudeVariation: 12 } system: barSys - particlesPerSecond: width; - particleDuration: 1000 - particleSize: 20 - particleSizeVariation: 4 - particleEndSize: 12 - maxParticles: parent.width; + emitRate: width; + lifeSpan: 1000 + size: 20 + sizeVariation: 4 + endSize: 12 + emitCap: parent.width; } Text { diff --git a/demos/declarative/flickr/content/StreamView.qml b/demos/declarative/flickr/content/StreamView.qml index 22e7d5dcd2..d7b608a391 100644 --- a/demos/declarative/flickr/content/StreamView.qml +++ b/demos/declarative/flickr/content/StreamView.qml @@ -73,9 +73,9 @@ Item{ y: -128 height: 32 speed: PointDirection{ y: (container.height + 128)/12 } - particlesPerSecond: 0.4 - particleDuration: 1000000//eventually -1 should mean a million seconds for neatness - maxParticles: 15 + emitRate: 0.4 + lifeSpan: 1000000//eventually -1 should mean a million seconds for neatness + emitCap: 15 } Emitter{ system: sys @@ -84,9 +84,9 @@ Item{ y: -128 height: 32 speed: PointDirection{ y: (container.height + 128)/12 } - particlesPerSecond: 0.4 - particleDuration: 1000000//eventually -1 should mean a million seconds for neatness - maxParticles: 15 + emitRate: 0.4 + lifeSpan: 1000000//eventually -1 should mean a million seconds for neatness + emitCap: 15 } Emitter{ system: sys @@ -95,9 +95,9 @@ Item{ y: -128 height: 32 speed: PointDirection{ y: (container.height + 128)/12 } - particlesPerSecond: 0.4 - particleDuration: 1000000//eventually -1 should mean a million seconds for neatness - maxParticles: 15 + emitRate: 0.4 + lifeSpan: 1000000//eventually -1 should mean a million seconds for neatness + emitCap: 15 } Kill{ system: sys diff --git a/demos/declarative/flickr/content/images/noise.png b/demos/declarative/flickr/content/images/noise.png index abc3c18d5f099a1a989368669111129c171fc879..c5a5ba0053b0219518b6ce34b7b60f67f15f1c40 100644 GIT binary patch literal 25447 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%0ABm3XM{AMsFqx0jyy%E$e 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 0 ? hp * 1 + 20 : 0 - particleDuration: 2400 - maxParticles: (maxHP * 1 + 20)*2.4 + emitRate: hp > 0 ? hp * 1 + 20 : 0 + lifeSpan: 2400 + emitCap: (maxHP * 1 + 20)*2.4 - particleSize: 48 - particleSizeVariation: 16 - particleEndSize: 16 + size: 48 + sizeVariation: 16 + endSize: 16 speed: AngledDirection{angleVariation:360; magnitudeVariation: 32} } @@ -81,11 +81,11 @@ Item { shape: EllipseShape{ fill: false } emitting: hp>0 - particlesPerSecond: 16 - particleDuration: 2000 + emitRate: 16 + lifeSpan: 2000 - particleSize: 48 - particleSizeVariation: 24 + size: 48 + sizeVariation: 24 SpriteGoal{ id: destructor diff --git a/demos/declarative/plasmapatrol/content/Frigate.qml b/demos/declarative/plasmapatrol/content/Frigate.qml index f18de0154a..d31405250e 100644 --- a/demos/declarative/plasmapatrol/content/Frigate.qml +++ b/demos/declarative/plasmapatrol/content/Frigate.qml @@ -58,9 +58,9 @@ Item { system: container.system particle: "frigateShield" anchors.centerIn: parent - particleSize: 92 - particlesPerSecond: 1 - particleDuration: 4800 + size: 92 + emitRate: 1 + lifeSpan: 4800 emitting: hp > 0 } Emitter{ @@ -71,12 +71,12 @@ Item { height: 16 shape: EllipseShape{} - particleSize: 16 - particleSizeVariation: 8 - particleEndSize: 8 - particlesPerSecond: hp > 0 ? hp * 1 + 20 : 0 - particleDuration: 1200 - maxParticles: (maxHP * 1 + 20)*2 + size: 16 + sizeVariation: 8 + endSize: 8 + emitRate: hp > 0 ? hp * 1 + 20 : 0 + lifeSpan: 1200 + emitCap: (maxHP * 1 + 20)*2 } Timer{ id: fireControl diff --git a/demos/declarative/plasmapatrol/content/LaserHardpoint.qml b/demos/declarative/plasmapatrol/content/LaserHardpoint.qml index 3705d29287..e99424856c 100644 --- a/demos/declarative/plasmapatrol/content/LaserHardpoint.qml +++ b/demos/declarative/plasmapatrol/content/LaserHardpoint.qml @@ -57,12 +57,12 @@ Item { emitting: container.show shape: EllipseShape{} speed: TargetedDirection{ targetX: width/2; targetY: width/2; magnitude: -1; proportionalMagnitude: true } - particleDuration: 1000 - particlesPerSecond: 64 + lifeSpan: 1000 + emitRate: 64 - particleSize: 24 - particleSizeVariation: 8 - particleEndSize: 8 + size: 24 + sizeVariation: 8 + endSize: 8 } function fireAt(targetArg, hardpoint){ @@ -97,11 +97,11 @@ Item { mirrored: (emitter.y < 0 || emitter.x < 0) && !(emitter.y < 0 && emitter.x < 0 )//I just want XOR } - particleDuration: 1000 - particlesPerSecond: 8000 - maxParticles: 800 - particleSize: 16 - particleEndSize: 0 + lifeSpan: 1000 + emitRate: 8000 + emitCap: 800 + size: 16 + endSize: 0 speed: PointDirection{xVariation: 4; yVariation: 4} } diff --git a/demos/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml b/demos/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml index 26d5f21359..b65686e59e 100644 --- a/demos/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml +++ b/demos/declarative/plasmapatrol/content/PlasmaPatrolParticles.qml @@ -46,7 +46,7 @@ Item{ ImageParticle{ system: sys particles: ["default"] - image: "pics/blur-circle3.png" + source: "pics/blur-circle3.png" color: "#003A3A3A" colorVariation: 0.1 z: 0 @@ -54,7 +54,7 @@ Item{ ImageParticle{ system: sys particles: ["redTeam"] - image: "pics/blur-circle3.png" + source: "pics/blur-circle3.png" color: "#0028060A" colorVariation: 0.1 z: 0 @@ -62,7 +62,7 @@ Item{ ImageParticle{ system: sys particles: ["greenTeam"] - image: "pics/blur-circle3.png" + source: "pics/blur-circle3.png" color: "#0006280A" colorVariation: 0.1 z: 0 @@ -70,7 +70,7 @@ Item{ ImageParticle{ system: sys particles: ["blaster"] - image: "pics/star2.png" + source: "pics/star2.png" //color: "#0F282406" color: "#0F484416" colorVariation: 0.2 @@ -79,7 +79,7 @@ Item{ ImageParticle{ system: sys particles: ["laser"] - image: "pics/star3.png" + source: "pics/star3.png" //color: "#00123F68" color: "#00428FF8" colorVariation: 0.2 @@ -88,7 +88,7 @@ Item{ ImageParticle{ system: sys particles: ["cannon"] - image: "pics/particle.png" + source: "pics/particle.png" color: "#80FFAAFF" colorVariation: 0.1 z: 2 @@ -96,7 +96,7 @@ Item{ ImageParticle{ system: sys particles: ["cannonCore"] - image: "pics/particle.png" + source: "pics/particle.png" color: "#00666666" colorVariation: 0.8 z: 1 @@ -104,7 +104,7 @@ Item{ ImageParticle{ system: sys particles: ["cannonWake"] - image: "pics/star.png" + source: "pics/star.png" color: "#00CCCCCC" colorVariation: 0.2 z: 1 @@ -112,7 +112,7 @@ Item{ ImageParticle{ system: sys particles: ["frigateShield"] - image: "pics/blur-circle2.png" + source: "pics/blur-circle2.png" color: "#00000000" colorVariation: 0.05 blueVariation: 0.5 @@ -148,12 +148,12 @@ Item{ system: sys particle: "cannonWake" follow: "cannon" - particlesPerParticlePerSecond: 64 - particleDuration: 600 + emitRatePerParticle: 64 + lifeSpan: 600 speed: AngledDirection{ angleVariation: 360; magnitude: 48} - particleSize: 16 - particleEndSize: 8 - particleSizeVariation: 2 + size: 16 + endSize: 8 + sizeVariation: 2 emitting: true width: 1000//XXX: Terrible hack height: 1000 @@ -162,10 +162,10 @@ Item{ system: sys particle: "cannonCore" follow: "cannon" - particlesPerParticlePerSecond: 256 - particleDuration: 128 - particleSize: 24 - particleEndSize: 8 + emitRatePerParticle: 256 + lifeSpan: 128 + size: 24 + endSize: 8 emitting: true width: 1000//XXX: Terrible hack height: 1000 diff --git a/demos/declarative/plasmapatrol/content/Sloop.qml b/demos/declarative/plasmapatrol/content/Sloop.qml index 20b60c5621..1a6f3a9620 100644 --- a/demos/declarative/plasmapatrol/content/Sloop.qml +++ b/demos/declarative/plasmapatrol/content/Sloop.qml @@ -63,15 +63,15 @@ Item { particle: container.shipParticle shape: EllipseShape{} - particlesPerSecond: hp > 0 ? hp + 20 : 0 - particleDuration: blinkInterval - maxParticles: (maxHP + 20) + emitRate: hp > 0 ? hp + 20 : 0 + lifeSpan: blinkInterval + emitCap: (maxHP + 20) acceleration: AngledDirection{angleVariation: 360; magnitude: 8} - particleSize: 24 - particleEndSize: 4 - particleSizeVariation: 8 + size: 24 + endSize: 4 + sizeVariation: 8 width: 16 height: 16 x: 64 diff --git a/demos/declarative/plasmapatrol/plasmapatrol.qml b/demos/declarative/plasmapatrol/plasmapatrol.qml index c490dfd322..dfc36ded01 100644 --- a/demos/declarative/plasmapatrol/plasmapatrol.qml +++ b/demos/declarative/plasmapatrol/plasmapatrol.qml @@ -98,12 +98,12 @@ Rectangle { system: particles emitting: true particle: "default" - particlesPerSecond: 1200 - particleDuration: 1200 + emitRate: 1200 + lifeSpan: 1200 shape: MaskShape{source:"content/pics/TitleText.png"} - particleSize: 16 - particleEndSize: 0 - particleSizeVariation: 8 + size: 16 + endSize: 0 + sizeVariation: 8 speed: AngledDirection{angleVariation:360; magnitudeVariation: 6} } } diff --git a/demos/declarative/samegame/SamegameCore/BoomBlock.qml b/demos/declarative/samegame/SamegameCore/BoomBlock.qml index b234688f8e..2a7e12f6b1 100644 --- a/demos/declarative/samegame/SamegameCore/BoomBlock.qml +++ b/demos/declarative/samegame/SamegameCore/BoomBlock.qml @@ -88,11 +88,11 @@ Item { speed: TargetedDirection{targetX: block.width/2; targetY: block.height/2; magnitude: -60; magnitudeVariation: 60} shape: EllipseShape{fill:true} emitting: false; - particleDuration: 700; particleDurationVariation: 100 - particlesPerSecond: 1000 - maxParticles: 100 //only fires 0.1s bursts (still 2x old number, ImageParticle wants less than 16000 max though) - particleSize: 28 - particleEndSize: 14 + lifeSpan: 700; lifeSpanVariation: 100 + emitRate: 1000 + emitCap: 100 //only fires 0.1s bursts (still 2x old number, ImageParticle wants less than 16000 max though) + size: 28 + endSize: 14 } states: [ diff --git a/demos/declarative/samegame/samegame.qml b/demos/declarative/samegame/samegame.qml index c547e1afa7..88579de5e9 100644 --- a/demos/declarative/samegame/samegame.qml +++ b/demos/declarative/samegame/samegame.qml @@ -81,7 +81,7 @@ Rectangle { system: particleSystem particles: ["red"] color: Qt.darker("red");//Actually want desaturated... - image: "SamegameCore/pics/particle.png" + source: "SamegameCore/pics/particle.png" colorVariation: 0.4 alpha: 0.1 } @@ -89,7 +89,7 @@ Rectangle { system: particleSystem particles: ["green"] color: Qt.darker("green");//Actually want desaturated... - image: "SamegameCore/pics/particle.png" + source: "SamegameCore/pics/particle.png" colorVariation: 0.4 alpha: 0.1 } @@ -97,7 +97,7 @@ Rectangle { system: particleSystem particles: ["blue"] color: Qt.darker("blue");//Actually want desaturated... - image: "SamegameCore/pics/particle.png" + source: "SamegameCore/pics/particle.png" colorVariation: 0.4 alpha: 0.1 } diff --git a/examples/declarative/cppextensions/imageprovider/imageprovider-example.qml b/examples/declarative/cppextensions/imageprovider/imageprovider-example.qml index e25b420025..f8f7b0ee55 100644 --- a/examples/declarative/cppextensions/imageprovider/imageprovider-example.qml +++ b/examples/declarative/cppextensions/imageprovider/imageprovider-example.qml @@ -42,8 +42,8 @@ import "ImageProviderCore" // import the plugin that registers the color image p //![0] Column { - Image { source: "image://colors/yellow" } - Image { source: "image://colors/red" } + Image { source: "source://colors/yellow" } + Image { source: "source://colors/red" } } //![0] diff --git a/examples/declarative/particles/allsmiles/plain.qml b/examples/declarative/particles/allsmiles/plain.qml index 1b456b0c5b..890a5782d1 100644 --- a/examples/declarative/particles/allsmiles/plain.qml +++ b/examples/declarative/particles/allsmiles/plain.qml @@ -9,14 +9,14 @@ Rectangle{ ImageParticle{ id: up system: sys - image: "content/singlesmile.png" + source: "content/singlesmile.png" } Emitter{ anchors.centerIn: parent system: sys - particlesPerSecond: 1000 - particleSize: 20 - particleDuration: 10000 + emitRate: 1000 + size: 20 + lifeSpan: 10000 speed: AngledDirection{angleVariation: 360; magnitudeVariation: 100;} } MouseArea{ diff --git a/examples/declarative/particles/allsmiles/smile.qml b/examples/declarative/particles/allsmiles/smile.qml index 9ce0e3a5e6..6b122e7be5 100644 --- a/examples/declarative/particles/allsmiles/smile.qml +++ b/examples/declarative/particles/allsmiles/smile.qml @@ -129,10 +129,10 @@ Rectangle{ id: emitter system: sys emitting: false - particleDuration: 4000 - maxParticles: 1200 + lifeSpan: 4000 + emitCap: 1200 anchors.fill: parent - particleSize: 32 + size: 32 speed: PointDirection{ xVariation: 12; yVariation: 12 } } MouseArea{ diff --git a/examples/declarative/particles/allsmiles/smilefactory.qml b/examples/declarative/particles/allsmiles/smilefactory.qml index 262644ec56..5b36eee3cd 100644 --- a/examples/declarative/particles/allsmiles/smilefactory.qml +++ b/examples/declarative/particles/allsmiles/smilefactory.qml @@ -49,7 +49,7 @@ Rectangle{ ImageParticle{ system: sys particles: ["goingLeft", "goingRight"] - image: "content/singlesmile.png" + source: "content/singlesmile.png" rotation: 90 rotationSpeed: 90 autoRotation: true @@ -57,7 +57,7 @@ Rectangle{ ImageParticle{ system: sys particles: ["goingDown"] - image: "content/squarefacespriteXX.png" + source: "content/squarefacespriteXX.png" rotation: 180 yVector: PointDirection{ y: 0.5; yVariation: 0.25; xVariation: 0.25; } } @@ -87,9 +87,9 @@ Rectangle{ emitting: false particle: "goingRight" speed: PointDirection{ x: 100 } - particleDuration: 4000 - particlesPerSecond: 2 - particleSize: 32 + lifeSpan: 4000 + emitRate: 2 + size: 32 } Emitter{ id: emitB @@ -99,9 +99,9 @@ Rectangle{ emitting: false particle: "goingLeft" speed: PointDirection{ x: -100 } - particleDuration: 4000 - particlesPerSecond: 2 - particleSize: 32 + lifeSpan: 4000 + emitRate: 2 + size: 32 } Emitter{ id: emitC @@ -111,8 +111,8 @@ Rectangle{ emitting: false particle: "goingDown" speed: PointDirection{ x: 100 } - particleDuration: 4000 - particlesPerSecond: 2 - particleSize: 32 + lifeSpan: 4000 + emitRate: 2 + size: 32 } } diff --git a/examples/declarative/particles/allsmiles/spriteparticles.qml b/examples/declarative/particles/allsmiles/spriteparticles.qml index 1f385f2c20..f5479f1570 100644 --- a/examples/declarative/particles/allsmiles/spriteparticles.qml +++ b/examples/declarative/particles/allsmiles/spriteparticles.qml @@ -48,7 +48,7 @@ Rectangle{ ImageParticle{ id: test particles: ["Test"] - image: "content/particle.png" + source: "content/particle.png" system: sys z: 2 anchors.fill: parent @@ -76,10 +76,10 @@ Rectangle{ particle: "Test" anchors.fill: parent id: particles2 - particlesPerSecond: 6000 - particleDuration: 720 + emitRate: 6000 + lifeSpan: 720 emitting: true - particleSize: 10 + size: 10 shape: mask } Emitter{ @@ -87,12 +87,12 @@ Rectangle{ particle: "Face" anchors.fill: parent id: particles - particlesPerSecond: 60 - particleDuration: 1440 + emitRate: 60 + lifeSpan: 1440 emitting: true speed: PointDirection{xVariation: 10; yVariation: 10;} - particleSize: 30 - particleSizeVariation: 10 + size: 30 + sizeVariation: 10 shape: mask } ParticleSystem{ diff --git a/examples/declarative/particles/allsmiles/spritestateparticles.qml b/examples/declarative/particles/allsmiles/spritestateparticles.qml index 0818686e15..a599c69a63 100644 --- a/examples/declarative/particles/allsmiles/spritestateparticles.qml +++ b/examples/declarative/particles/allsmiles/spritestateparticles.qml @@ -170,13 +170,13 @@ Rectangle{ } Emitter{ system: sys - particlesPerSecond: 16 - particleDuration: 10000 + emitRate: 16 + lifeSpan: 10000 emitting: true speed: AngledDirection{angle: 90; magnitude: 60; angleVariation: 5} acceleration: PointDirection{ y: 10 } - particleSize: 30 - particleSizeVariation: 10 + size: 30 + sizeVariation: 10 width: parent.width height: 100 } diff --git a/examples/declarative/particles/allsmiles/spritevariedparticles.qml b/examples/declarative/particles/allsmiles/spritevariedparticles.qml index e6aeaccea1..d6e13727ff 100644 --- a/examples/declarative/particles/allsmiles/spritevariedparticles.qml +++ b/examples/declarative/particles/allsmiles/spritevariedparticles.qml @@ -96,13 +96,13 @@ Rectangle{ id: particleEmitter system: sys width: parent.width - particlesPerSecond: 16 - particleDuration: 8000 + emitRate: 16 + lifeSpan: 8000 emitting: true speed: AngledDirection{angle: 90; magnitude: 300; magnitudeVariation: 100; angleVariation: 5} acceleration: PointDirection{ y: 10 } - particleSize: 30 - particleSizeVariation: 10 + size: 30 + sizeVariation: 10 } Binding{ target: particleEmitter diff --git a/examples/declarative/particles/allsmiles/ultraparticles.qml b/examples/declarative/particles/allsmiles/ultraparticles.qml index f2b6f8d163..0ea095deb6 100644 --- a/examples/declarative/particles/allsmiles/ultraparticles.qml +++ b/examples/declarative/particles/allsmiles/ultraparticles.qml @@ -84,12 +84,12 @@ Rectangle{ system: sys anchors.centerIn: parent id: particles - particlesPerSecond: 200 - particleDuration: 6000 + emitRate: 200 + lifeSpan: 6000 emitting: true speed: AngledDirection{angleVariation: 360; magnitude: 80; magnitudeVariation: 40} - particleSize: 40 - particleEndSize: 80 + size: 40 + endSize: 80 } Text{ x: 16 diff --git a/examples/declarative/particles/asteroid/asteroid.qml b/examples/declarative/particles/asteroid/asteroid.qml index 223ea81205..2ecfc42a98 100644 --- a/examples/declarative/particles/asteroid/asteroid.qml +++ b/examples/declarative/particles/asteroid/asteroid.qml @@ -68,7 +68,7 @@ Item { ImageParticle { system: sys particles: ["starfield"] - image: "content/star.png" + source: "content/star.png" colorVariation: 0.3 color: "white" } @@ -77,27 +77,27 @@ Item { system: sys particle: "starfield" - particlesPerSecond: 80 - particleDuration: 2500 + emitRate: 80 + lifeSpan: 2500 anchors.centerIn: parent //acceleration: AngledDirection{angleVariation: 360; magnitude: 200}//Is this a better effect, more consistent speed? acceleration: PointDirection{ xVariation: 200; yVariation: 200; } - particleSize: 0 - particleEndSize: 80 - particleSizeVariation: 10 + size: 0 + endSize: 80 + sizeVariation: 10 } Emitter{ system: sys particle: "meteor" - particlesPerSecond: 12 - particleDuration: 5000 + emitRate: 12 + lifeSpan: 5000 emitting: true acceleration: PointDirection{ xVariation: 80; yVariation: 80; } - particleSize: 15 - particleEndSize: 300 + size: 15 + endSize: 300 anchors.centerIn: parent } ImageParticle{ @@ -172,7 +172,7 @@ Item { z:0 system: sys particles: ["exhaust"] - image: "content/particle4.png" + source: "content/particle4.png" color: "orange" SequentialAnimation on color { @@ -196,8 +196,8 @@ Item { system: sys particle: "exhaust" - particlesPerSecond: 300 - particleDuration: 500 + emitRate: 300 + lifeSpan: 500 y: holder.y x: holder.x @@ -207,7 +207,7 @@ Item { acceleration: PointDirection{ xVariation: 10; yVariation: 10; } - particleSize: 4 - particleSizeVariation: 4 + size: 4 + sizeVariation: 4 } } diff --git a/examples/declarative/particles/asteroid/blackhole.qml b/examples/declarative/particles/asteroid/blackhole.qml index 1bc406b00c..4a7ce02538 100644 --- a/examples/declarative/particles/asteroid/blackhole.qml +++ b/examples/declarative/particles/asteroid/blackhole.qml @@ -68,22 +68,22 @@ Rectangle{ Emitter{ particle: "stars" system: particles - particlesPerSecond: 40 - particleDuration: 4000 + emitRate: 40 + lifeSpan: 4000 emitting: true - particleSize: 30 - particleSizeVariation: 10 + size: 30 + sizeVariation: 10 speed: PointDirection{ x: 220; xVariation: 40 } height: parent.height } Emitter{ particle: "roids" system: particles - particlesPerSecond: 10 - particleDuration: 4000 + emitRate: 10 + lifeSpan: 4000 emitting: true - particleSize: 30 - particleSizeVariation: 10 + size: 30 + sizeVariation: 10 speed: PointDirection{ x: 220; xVariation: 40 } height: parent.height } @@ -95,7 +95,7 @@ Rectangle{ id: stars particles: ["stars"] system: particles - image: "content/star.png" + source: "content/star.png" color: "white" colorVariation: 0.1 alpha: 0 @@ -117,7 +117,7 @@ Rectangle{ id: shot particles: ["shot"] system: particles - image: "content/star.png" + source: "content/star.png" color: "#0FF06600" colorVariation: 0.3 @@ -126,7 +126,7 @@ Rectangle{ id: engine particles: ["engine"] system: particles - image: "content/particle4.png" + source: "content/particle4.png" color: "orange" SequentialAnimation on color { @@ -172,12 +172,12 @@ Rectangle{ Emitter{ particle: "engine" system: particles - particlesPerSecond: 200 - particleDuration: 1000 + emitRate: 200 + lifeSpan: 1000 emitting: true - particleSize: 10 - particleEndSize: 4 - particleSizeVariation: 4 + size: 10 + endSize: 4 + sizeVariation: 4 speed: PointDirection{ x: -128; xVariation: 32 } height: parent.height width: 20 @@ -185,10 +185,10 @@ Rectangle{ Emitter{ particle: "shot" system: particles - particlesPerSecond: 32 - particleDuration: 2000 + emitRate: 32 + lifeSpan: 2000 emitting: spacePressed - particleSize: 40 + size: 40 speed: PointDirection{ x: 256; } x: parent.width y: parent.height/2 diff --git a/examples/declarative/particles/custom/blurparticles.qml b/examples/declarative/particles/custom/blurparticles.qml index 7d0f9cc6ac..8a3e9ad803 100644 --- a/examples/declarative/particles/custom/blurparticles.qml +++ b/examples/declarative/particles/custom/blurparticles.qml @@ -11,10 +11,10 @@ Rectangle{ Emitter{ system:sys height: parent.height - particlesPerSecond: 1 - particleDuration: 12000 + emitRate: 1 + lifeSpan: 12000 speed: PointDirection{x:20;} - particleSize: 64 + size: 64 } ShaderEffectSource{ id: theSource diff --git a/examples/declarative/particles/exampleslauncher.qml b/examples/declarative/particles/exampleslauncher.qml index c08123cf44..354bcdf65a 100644 --- a/examples/declarative/particles/exampleslauncher.qml +++ b/examples/declarative/particles/exampleslauncher.qml @@ -52,8 +52,6 @@ Rectangle{ id: shell anchors.fill: parent } - property string emissionMode: "Falling" - onEmissionModeChanged: workaround.active = true VisualDataModel{//TODO: Transitions between modes id: vdm model: [ @@ -111,72 +109,8 @@ Rectangle{ } GridView{ anchors.fill: parent - anchors.bottomMargin: 128 + cellWidth: 120 + cellHeight: 120 model: vdm - visible: emissionMode == "Grid" - opacity: visible?1:0 - Behavior on opacity{NumberAnimation{}} - } - ParticleSystem{ id: sys } - ModelParticle{ - system: sys - model: vdm - } - Kill{ - //TODO: File bug? - id: workaround - system: sys - active: false - onActiveChanged: timer.start() - Timer{ - id: timer - interval: 32 - running: false - repeat: false - onTriggered: workaround.active = false - } - } - Emitter{ - system: sys - emitting: emissionMode == "Falling" - width: parent.width - particlesPerSecond: 2 - particleDuration: 6000 - speed: PointDirection{y:100;} - } - Emitter{ - system: sys - emitting: emissionMode == "Bursting" - anchors.centerIn: parent - particlesPerSecond: 2 - particleDuration: 6000 - speed: AngledDirection{magnitude: 60; angleVariation: 360} - } - Emitter{ - system: sys - emitting: emissionMode == "Shimmering" - anchors.fill: parent - particlesPerSecond: 4 - particleDuration: 4000 - } - Row{ - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - Button{ - text:"Grid" - onClicked: emissionMode = "Grid"; - } - Button{ - text:"Fall" - onClicked: emissionMode = "Falling"; - } - Button{ - text:"Burst" - onClicked: emissionMode = "Bursting"; - } - Button{ - text:"Shimmer" - onClicked: emissionMode = "Shimmering"; - } } } diff --git a/examples/declarative/particles/modelparticles/bubbles.qml b/examples/declarative/particles/modelparticles/bubbles.qml index d0eb3ea044..23f0b82d01 100644 --- a/examples/declarative/particles/modelparticles/bubbles.qml +++ b/examples/declarative/particles/modelparticles/bubbles.qml @@ -63,8 +63,8 @@ Item{ x: parent.width/4 y:parent.height speed: PointDirection{ y: -64; yVariation: 16 } - particlesPerSecond: 1 - particleDuration: 8000 + emitRate: 1 + lifeSpan: 8000 } Wander{ system: sys diff --git a/examples/declarative/particles/modelparticles/gridsplosion.qml b/examples/declarative/particles/modelparticles/gridsplosion.qml index fe2dd261a5..db860125e5 100644 --- a/examples/declarative/particles/modelparticles/gridsplosion.qml +++ b/examples/declarative/particles/modelparticles/gridsplosion.qml @@ -52,9 +52,9 @@ Item{ system: sys id: burster; emitting: false - particlesPerSecond: 1000 - particleDuration: 50000 - maxParticles: 100; + emitRate: 1000 + lifeSpan: 50000 + emitCap: 100; speed: PointDirection{xVariation: 400; yVariation: 400} anchors.centerIn: parent Timer{ @@ -72,7 +72,7 @@ Item{ } ImageParticle{ system: sys - image: "../trails/content/particle.png" + source: "../trails/content/particle.png" color: "black" colorVariation: 0.0 } diff --git a/examples/declarative/particles/modelparticles/package.qml b/examples/declarative/particles/modelparticles/package.qml index 64873802a5..0aa8903270 100644 --- a/examples/declarative/particles/modelparticles/package.qml +++ b/examples/declarative/particles/modelparticles/package.qml @@ -85,7 +85,7 @@ Rectangle { width: 100 x: 50 speed: PointDirection{ y: 40 } - particleDuration: 5000 - particlesPerSecond: 1.6 + lifeSpan: 5000 + emitRate: 1.6 } } diff --git a/examples/declarative/particles/modelparticles/stream.qml b/examples/declarative/particles/modelparticles/stream.qml index 73107ad4f7..5c7a6f7fc4 100644 --- a/examples/declarative/particles/modelparticles/stream.qml +++ b/examples/declarative/particles/modelparticles/stream.qml @@ -75,8 +75,8 @@ Item{ x: -132/2 y: 132/2 speed: PointDirection{ x: 32; xVariation: 8 } - particlesPerSecond: 0.5 - particleDuration: 120000 //TODO: A -1 or something which does 'infinite'? (but need disable fade first) + emitRate: 0.5 + lifeSpan: 120000 //TODO: A -1 or something which does 'infinite'? (but need disable fade first) particle: "photos" } Kill{ @@ -88,7 +88,7 @@ Item{ ImageParticle{ system: sys particles: ["fireworks"] - image: "../trails/content/star.png" + source: "../trails/content/star.png" color: "lightsteelblue" alpha: 0 colorVariation: 0 @@ -163,9 +163,9 @@ Item{ Emitter{ id: centerEmitter speed: PointDirection{ x: 32; xVariation: 8;} - particlesPerSecond: 0.5 - particleDuration: 12000 //TODO: A -1 or something which does 'infinite'? (but need disable fade first) - maxParticles: 20 + emitRate: 0.5 + lifeSpan: 12000 //TODO: A -1 or something which does 'infinite'? (but need disable fade first) + emitCap: 20 particle: "photos" system: sys anchors.centerIn: parent @@ -177,16 +177,16 @@ Item{ id: spawnFireworks particle: "fireworks" system: sys - maxParticles: 400 - particlesPerSecond: 400 - particleDuration: 2800 + emitCap: 400 + emitRate: 400 + lifeSpan: 2800 x: parent.width/2 y: parent.height/2 - 64 width: 8 height: 8 emitting: false - particleSize: 32 - particleEndSize: 8 + size: 32 + endSize: 8 speed: AngledDirection{ magnitude: 160; magnitudeVariation: 120; angleVariation: 90; angle: 270 } acceleration: PointDirection{ y: 160 } } diff --git a/examples/declarative/particles/snow/snow.qml b/examples/declarative/particles/snow/snow.qml index 41adccf7d7..ea2de17046 100644 --- a/examples/declarative/particles/snow/snow.qml +++ b/examples/declarative/particles/snow/snow.qml @@ -65,13 +65,13 @@ Rectangle{ } Emitter { system: particles - particlesPerSecond: 20 - particleDuration: 7000 + emitRate: 20 + lifeSpan: 7000 emitting: true speed: PointDirection{ y:80; yVariation: 40; } acceleration: PointDirection{ y: 4 } - particleSize: 20 - particleSizeVariation: 10 + size: 20 + sizeVariation: 10 width: parent.width height: 100 } diff --git a/examples/declarative/particles/spaceexplorer/spaceexplorer.qml b/examples/declarative/particles/spaceexplorer/spaceexplorer.qml index f303bb4d9a..1bb3cdae02 100644 --- a/examples/declarative/particles/spaceexplorer/spaceexplorer.qml +++ b/examples/declarative/particles/spaceexplorer/spaceexplorer.qml @@ -78,11 +78,11 @@ Rectangle{ Emitter{ particle: "stars2" system: background - particlesPerSecond: 60 - particleDuration: 4000 + emitRate: 60 + lifeSpan: 4000 emitting: true - particleSize: 10 - particleSizeVariation: 10 + size: 10 + sizeVariation: 10 anchors.fill: parent } ParticleSystem{ id: background } @@ -90,7 +90,7 @@ Rectangle{ particles: ["stars2"] system: background anchors.fill: parent - image: "content/star.png" + source: "content/star.png" color: "white" colorVariation: 0.1 } @@ -201,7 +201,7 @@ Rectangle{ particles: ["stars"] anchors.fill: parent system: foreground - image: "content/star.png" + source: "content/star.png" color: "white" colorVariation: 0.1 } @@ -209,7 +209,7 @@ Rectangle{ particles: ["shot"] anchors.fill: parent system: foreground - image: "content/star.png" + source: "content/star.png" color: "orange" colorVariation: 0.3 @@ -219,7 +219,7 @@ Rectangle{ particles: ["engine"] anchors.fill: parent system: foreground - image: "content/particle4.png" + source: "content/particle4.png" color: "orange" SequentialAnimation on color { @@ -327,21 +327,21 @@ Rectangle{ Emitter{ particle: "powerups" system: foreground - particlesPerSecond: 1 - particleDuration: 6000 + emitRate: 1 + lifeSpan: 6000 emitting: !gameOver - particleSize: 60 - particleSizeVariation: 10 + size: 60 + sizeVariation: 10 anchors.fill: parent } Emitter{ particle: "stars" system: foreground - particlesPerSecond: 40 - particleDuration: 4000 + emitRate: 40 + lifeSpan: 4000 emitting: !gameOver - particleSize: 30 - particleSizeVariation: 10 + size: 30 + sizeVariation: 10 anchors.fill: parent } SpriteImage{ @@ -382,12 +382,12 @@ Rectangle{ Emitter{ system: foreground particle: "engine" - particlesPerSecond: 100 - particleDuration: 1000 + emitRate: 100 + lifeSpan: 1000 emitting: !gameOver - particleSize: 10 - particleEndSize: 4 - particleSizeVariation: 4 + size: 10 + endSize: 4 + sizeVariation: 4 speed: PointDirection{ x: -128 * Math.cos(rocket.rotation * (Math.PI / 180)) y: -128 * Math.sin(rocket.rotation * (Math.PI / 180)) @@ -400,10 +400,10 @@ Rectangle{ Emitter{ system: foreground particle: "shot" - particlesPerSecond: 16 - particleDuration: 1600 + emitRate: 16 + lifeSpan: 1600 emitting: !gameOver && shoot - particleSize: 40 + size: 40 speed: PointDirection{ x: 256 * Math.cos(rocket.rotation * (Math.PI / 180)) y: 256 * Math.sin(rocket.rotation * (Math.PI / 180)) diff --git a/examples/declarative/particles/trails/dynamicemitters.qml b/examples/declarative/particles/trails/dynamicemitters.qml index 8e555033a0..588474fb43 100644 --- a/examples/declarative/particles/trails/dynamicemitters.qml +++ b/examples/declarative/particles/trails/dynamicemitters.qml @@ -51,7 +51,7 @@ Rectangle{ } ImageParticle{ system: sys - image: "content/particle.png" + source: "content/particle.png" color: "white" colorVariation: 1.0 alpha: 0.1 @@ -64,10 +64,10 @@ Rectangle{ id: emitMore system: sys emitting: true - particlesPerSecond: 128 - particleDuration: 600 - particleSize: 16 - particleEndSize: 8 + emitRate: 128 + lifeSpan: 600 + size: 16 + endSize: 8 speed: AngledDirection{angleVariation:360; magnitude: 60} } @@ -81,10 +81,10 @@ Rectangle{ } system: sys emitting: true - particlesPerSecond: 64 - particleDuration: 600 - particleSize: 24 - particleEndSize: 8 + emitRate: 64 + lifeSpan: 600 + size: 24 + endSize: 8 NumberAnimation on x{ id: xAnim; to: targetX diff --git a/examples/declarative/particles/trails/fireballs.qml b/examples/declarative/particles/trails/fireballs.qml index cd81168d81..4cc2eacc25 100644 --- a/examples/declarative/particles/trails/fireballs.qml +++ b/examples/declarative/particles/trails/fireballs.qml @@ -57,7 +57,7 @@ Rectangle { anchors.fill: parent particles: ["E"] system: particles - image: "content/particleA.png" + source: "content/particleA.png" colorVariation: 0.2 color: "#00ff400f" } @@ -67,7 +67,7 @@ Rectangle { system: particles anchors.fill: parent particles: ["A", "B"] - image: "content/particle.png" + source: "content/particle.png" colorVariation: 0 color: "#00111111" } @@ -76,7 +76,7 @@ Rectangle { anchors.fill: parent system: particles particles: ["C", "D"] - image: "content/particle.png" + source: "content/particle.png" colorVariation: 0.1 color: "#00ff400f" } @@ -88,15 +88,15 @@ Rectangle { y: parent.height width: parent.width - particlesPerSecond: 350 - particleDuration: 3500 + emitRate: 350 + lifeSpan: 3500 acceleration: PointDirection{ y: -17; xVariation: 3 } speed: PointDirection{xVariation: 3} - particleSize: 24 - particleSizeVariation: 8 - particleEndSize: 4 + size: 24 + sizeVariation: 8 + endSize: 4 } FollowEmitter{ id: fireSmoke @@ -106,15 +106,15 @@ Rectangle { width: root.width height: root.height - 68 - particlesPerParticlePerSecond: 1 - particleDuration: 2000 + emitRatePerParticle: 1 + lifeSpan: 2000 speed: PointDirection{y:-17*6; yVariation: -17; xVariation: 3} acceleration: PointDirection{xVariation: 3} - particleSize: 36 - particleSizeVariation: 8 - particleEndSize: 16 + size: 36 + sizeVariation: 8 + endSize: 16 } FollowEmitter{ id: fireballFlame @@ -123,14 +123,14 @@ Rectangle { particle: "D" follow: "E" - particlesPerParticlePerSecond: 120 - particleDuration: 180 - emissionWidth: 8 - emissionHeight: 8 + emitRatePerParticle: 120 + lifeSpan: 180 + emitWidth: 8 + emitHeight: 8 - particleSize: 16 - particleSizeVariation: 4 - particleEndSize: 4 + size: 16 + sizeVariation: 4 + endSize: 4 } FollowEmitter{ @@ -140,17 +140,17 @@ Rectangle { particle: "A" follow: "E" - particlesPerParticlePerSecond: 128 - particleDuration: 2400 - emissionWidth: 16 - emissionHeight: 16 + emitRatePerParticle: 128 + lifeSpan: 2400 + emitWidth: 16 + emitHeight: 16 speed: PointDirection{yVariation: 16; xVariation: 16} acceleration: PointDirection{y: -16} - particleSize: 24 - particleSizeVariation: 8 - particleEndSize: 8 + size: 24 + sizeVariation: 8 + endSize: 8 } Emitter{ id: balls @@ -160,14 +160,14 @@ Rectangle { y: parent.height width: parent.width - particlesPerSecond: 2 - particleDuration: 7000 + emitRate: 2 + lifeSpan: 7000 speed: PointDirection{y:-17*4*2; xVariation: 6*6} acceleration: PointDirection{y: 17*2; xVariation: 6*6} - particleSize: 12 - particleSizeVariation: 4 + size: 12 + sizeVariation: 4 } } diff --git a/examples/declarative/particles/trails/layered.qml b/examples/declarative/particles/trails/layered.qml index b2895dd617..d4a823b556 100644 --- a/examples/declarative/particles/trails/layered.qml +++ b/examples/declarative/particles/trails/layered.qml @@ -59,20 +59,20 @@ Rectangle{ system: sys y:root.height + 20 width: root.width - particlesPerSecond: 200 - particleDuration: 4000 + emitRate: 200 + lifeSpan: 4000 speed: PointDirection{ y: -120; } } ImageParticle{ system: sys visible: !cloneMode - image: "content/particle2.png" + source: "content/particle2.png" } ImageParticle{ system: sys visible: cloneMode z: 0 - image: "content/particle3.png" + source: "content/particle3.png" } ImageParticle{ system: sys @@ -82,6 +82,6 @@ Rectangle{ height: 240 width: root.width z: 1 - image: "content/particle.png" + source: "content/particle.png" } } diff --git a/examples/declarative/particles/trails/list.qml b/examples/declarative/particles/trails/list.qml index 7874590e28..7e8fb44003 100644 --- a/examples/declarative/particles/trails/list.qml +++ b/examples/declarative/particles/trails/list.qml @@ -54,7 +54,7 @@ Rectangle { anchors.fill: parent system: particles z: 10 - image: "content/star.png" + source: "content/star.png" color: "white" colorVariation: 0.0 } @@ -96,10 +96,10 @@ Rectangle { anchors.fill: parent system: particles; emitting: anim.running - particlesPerSecond: 600 - particleDuration: 600 - particleSize: 16 - particleEndSize: 8 + emitRate: 600 + lifeSpan: 600 + size: 16 + endSize: 8 } } } diff --git a/examples/declarative/particles/trails/overburst.qml b/examples/declarative/particles/trails/overburst.qml index ed9313d471..c3129a1d72 100644 --- a/examples/declarative/particles/trails/overburst.qml +++ b/examples/declarative/particles/trails/overburst.qml @@ -49,7 +49,7 @@ Rectangle{ ImageParticle{ system: sys id: cp - image: "content/particle.png" + source: "content/particle.png" colorVariation: 0.4 color: "#000000FF" } @@ -60,13 +60,13 @@ Rectangle{ emitting: ma.pressed x: ma.mouseX y: ma.mouseY - particlesPerSecond: 16000 - particleDuration: 1000 - maxParticles: 4000 + emitRate: 16000 + lifeSpan: 1000 + emitCap: 4000 acceleration: AngledDirection{angleVariation: 360; magnitude: 360; } - particleSize: 8 - particleEndSize: 16 - particleSizeVariation: 4 + size: 8 + endSize: 16 + sizeVariation: 4 } MouseArea{ anchors.fill: parent diff --git a/examples/declarative/particles/trails/portal.qml b/examples/declarative/particles/trails/portal.qml index ae9b447fb9..8cf323b0f5 100644 --- a/examples/declarative/particles/trails/portal.qml +++ b/examples/declarative/particles/trails/portal.qml @@ -58,7 +58,7 @@ Rectangle{ particles: ["center","edge"] anchors.fill: parent system: particles - image: "content/particle.png" + source: "content/particle.png" colorVariation: 0.1 color: "#009999FF" } @@ -66,12 +66,12 @@ Rectangle{ anchors.fill: parent particle: "center" system: particles - particlesPerSecond: 200 - particleDuration: 2000 + emitRate: 200 + lifeSpan: 2000 emitting: true - particleSize: 20 - particleSizeVariation: 2 - particleEndSize: 0 + size: 20 + sizeVariation: 2 + endSize: 0 shape: EllipseShape{fill: false} speed: TargetedDirection{ targetX: root.width/2 @@ -84,12 +84,12 @@ Rectangle{ anchors.fill: parent particle: "edge" system: particles - particlesPerSecond: 4000 - particleDuration: 2000 + emitRate: 4000 + lifeSpan: 2000 emitting: true - particleSize: 20 - particleSizeVariation: 2 - particleEndSize: 0 + size: 20 + sizeVariation: 2 + endSize: 0 shape: EllipseShape{fill: false} speed: TargetedDirection{ targetX: root.width/2 diff --git a/examples/declarative/particles/trails/rainbow.qml b/examples/declarative/particles/trails/rainbow.qml index 543a9b6182..c0b61bf89e 100644 --- a/examples/declarative/particles/trails/rainbow.qml +++ b/examples/declarative/particles/trails/rainbow.qml @@ -53,14 +53,14 @@ Rectangle { colorVariation: 0.5 alpha: 0 - image: "content/particle.png" + source: "content/particle.png" colorTable: "content/colortable.png" sizeTable: "content/colortable.png" } Emitter{ system: particles - particlesPerSecond: 500 - particleDuration: 2000 + emitRate: 500 + lifeSpan: 2000 y: root.height / 2 + Math.sin(t * 2) * root.height * 0.3 x: root.width / 2 + Math.cos(t) * root.width * 0.3 @@ -75,8 +75,8 @@ Rectangle { speed: PointDirection{ xVariation: 5; yVariation: 5;} acceleration: PointDirection{ xVariation: 5; yVariation: 5;} - particleSize: 16 - //particleEndSize: 8 - //particleSizeVariation: 8 + size: 16 + //endSize: 8 + //sizeVariation: 8 } } diff --git a/examples/declarative/particles/trails/shimmer.qml b/examples/declarative/particles/trails/shimmer.qml index b3157f68cd..2bd4f6980f 100644 --- a/examples/declarative/particles/trails/shimmer.qml +++ b/examples/declarative/particles/trails/shimmer.qml @@ -56,7 +56,7 @@ Rectangle{ ImageParticle{ anchors.fill: parent system: particles - image: "content/star.png" + source: "content/star.png" sizeTable: "content/sparkleSize.png" alpha: 0 colorVariation: 0.6 @@ -64,10 +64,10 @@ Rectangle{ Emitter{ anchors.fill: parent system: particles - particlesPerSecond: 2000 - particleDuration: 2000 + emitRate: 2000 + lifeSpan: 2000 emitting: true - particleSize: 30 - particleSizeVariation: 10 + size: 30 + sizeVariation: 10 } } diff --git a/examples/declarative/particles/trails/trails.qml b/examples/declarative/particles/trails/trails.qml index 6ee8a6ec37..689de4eb41 100644 --- a/examples/declarative/particles/trails/trails.qml +++ b/examples/declarative/particles/trails/trails.qml @@ -49,7 +49,7 @@ Rectangle{ ImageParticle{ system: sys id: cp - image: "content/particle.png" + source: "content/particle.png" color: "#00FFFFFF" colorVariation: 0.4 } @@ -58,12 +58,12 @@ Rectangle{ id: bursty system: sys emitting: false - particlesPerSecond: 2000 - particleDuration: 500 + emitRate: 2000 + lifeSpan: 500 acceleration: AngledDirection{ angle: 90; angleVariation: 360; magnitude: 640; } - particleSize: 8 - particleEndSize: 16 - particleSizeVariation: 4 + size: 8 + endSize: 16 + sizeVariation: 4 } Emitter{ system: sys @@ -71,12 +71,12 @@ Rectangle{ emitting: ma.pressed x: ma.mouseX y: ma.mouseY - particlesPerSecond: 400 - particleDuration: 2000 + emitRate: 400 + lifeSpan: 2000 acceleration: AngledDirection{ angle: 90; angleVariation: 22; magnitude: 32; } - particleSize: 8 - particleEndSize: 16 - particleSizeVariation: 8 + size: 8 + endSize: 16 + sizeVariation: 8 } MouseArea{ id: ma diff --git a/examples/declarative/particles/trails/turbulence.qml b/examples/declarative/particles/trails/turbulence.qml index bde25bc472..6159b3e9c9 100644 --- a/examples/declarative/particles/trails/turbulence.qml +++ b/examples/declarative/particles/trails/turbulence.qml @@ -69,14 +69,14 @@ Rectangle{ ImageParticle{ particles: ["smoke"] system: ps - image: "content/particle.png" + source: "content/particle.png" color: "#11111111" colorVariation: 0 } ImageParticle{ particles: ["flame"] system: ps - image: "content/particle.png" + source: "content/particle.png" color: "#11ff400f" colorVariation: 0.1 } @@ -85,11 +85,11 @@ Rectangle{ system: ps particle: "flame" - particlesPerSecond: 120 - particleDuration: 1200 - particleSize: 20 - particleEndSize: 10 - particleSizeVariation: 10 + emitRate: 120 + lifeSpan: 1200 + size: 20 + endSize: 10 + sizeVariation: 10 acceleration: PointDirection{ y: -40 } speed: AngledDirection{ angle: 270; magnitude: 20; angleVariation: 22; magnitudeVariation: 5 } } @@ -101,12 +101,12 @@ Rectangle{ particle: "smoke" follow: "flame" - particlesPerParticlePerSecond: 4 - particleDuration: 2400 - particleDurationVariation: 400 - particleSize: 16 - particleEndSize: 8 - particleSizeVariation: 8 + emitRatePerParticle: 4 + lifeSpan: 2400 + lifeSpanVariation: 400 + size: 16 + endSize: 8 + sizeVariation: 8 acceleration: PointDirection{ y: -40 } speed: AngledDirection{ angle: 270; magnitude: 40; angleVariation: 22; magnitudeVariation: 5 } } @@ -118,11 +118,11 @@ Rectangle{ particle: "smoke" follow: "flame" - particlesPerParticlePerSecond: 1 - particleDuration: 2400 - particleSize: 36 - particleEndSize: 24 - particleSizeVariation: 8 + emitRatePerParticle: 1 + lifeSpan: 2400 + size: 36 + endSize: 24 + sizeVariation: 8 acceleration: PointDirection{ y: -40 } speed: AngledDirection{ angle: 270; magnitude: 40; angleVariation: 22; magnitudeVariation: 5 } } diff --git a/examples/declarative/particles/trails/velocityfrommotion.qml b/examples/declarative/particles/trails/velocityfrommotion.qml index d2e4ed2917..d361c99f89 100644 --- a/examples/declarative/particles/trails/velocityfrommotion.qml +++ b/examples/declarative/particles/trails/velocityfrommotion.qml @@ -77,7 +77,7 @@ Rectangle { ParticleSystem{ id: sys1 } ImageParticle{ system: sys1 - image: "content/particle.png" + source: "content/particle.png" color: "cyan" alpha: 0 SequentialAnimation on color { @@ -109,8 +109,8 @@ Rectangle { id: trailsNormal system: sys1 - particlesPerSecond: 500 - particleDuration: 2000 + emitRate: 500 + lifeSpan: 2000 y: mouseArea.pressed ? mouseArea.mouseY : circle.cy @@ -120,8 +120,8 @@ Rectangle { acceleration: PointDirection{xVariation: 10; yVariation: 10;} speedFromMovement: 8 - particleSize: 8 - particleSizeVariation: 4 + size: 8 + sizeVariation: 4 } ParticleSystem { id: sys2 } ImageParticle{ @@ -142,14 +142,14 @@ Rectangle { } } colorVariation: 0.5 - image: "content/star.png" + source: "content/star.png" } Emitter{ id: trailsStars system: sys2 - particlesPerSecond: 100 - particleDuration: 2200 + emitRate: 100 + lifeSpan: 2200 y: mouseArea.pressed ? mouseArea.mouseY : circle.cy @@ -159,12 +159,12 @@ Rectangle { acceleration: PointDirection{xVariation: 10; yVariation: 10;} speedFromMovement: 8 - particleSize: 22 - particleSizeVariation: 4 + size: 22 + sizeVariation: 4 } ParticleSystem { id: sys3; } ImageParticle{ - image: "content/particle.png" + source: "content/particle.png" system: sys3 color: "orange" alpha: 0 @@ -189,8 +189,8 @@ Rectangle { id: trailsNormal2 system: sys3 - particlesPerSecond: 300 - particleDuration: 2000 + emitRate: 300 + lifeSpan: 2000 y: mouseArea.pressed ? mouseArea.mouseY : circle2.cy x: mouseArea.pressed ? mouseArea.mouseX : circle2.cx @@ -200,13 +200,13 @@ Rectangle { speed: PointDirection{xVariation: 4; yVariation: 4;} acceleration: PointDirection{xVariation: 10; yVariation: 10;} - particleSize: 12 - particleSizeVariation: 4 + size: 12 + sizeVariation: 4 } ParticleSystem { id: sys4; } ImageParticle{ system: sys4 - image: "content/star.png" + source: "content/star.png" color: "green" alpha: 0 SequentialAnimation on color { @@ -229,8 +229,8 @@ Rectangle { id: trailsStars2 system: sys4 - particlesPerSecond: 50 - particleDuration: 2200 + emitRate: 50 + lifeSpan: 2200 y: mouseArea.pressed ? mouseArea.mouseY : circle2.cy @@ -240,8 +240,8 @@ Rectangle { speed: PointDirection{xVariation: 2; yVariation: 2;} acceleration: PointDirection{xVariation: 10; yVariation: 10;} - particleSize: 22 - particleSizeVariation: 4 + size: 22 + sizeVariation: 4 } diff --git a/examples/declarative/toys/dynamicscene/dynamicscene.qml b/examples/declarative/toys/dynamicscene/dynamicscene.qml index a436b41b88..174204768a 100644 --- a/examples/declarative/toys/dynamicscene/dynamicscene.qml +++ b/examples/declarative/toys/dynamicscene/dynamicscene.qml @@ -147,27 +147,27 @@ Item { PaletteItem { anchors.verticalCenter: parent.verticalCenter componentFile: "Sun.qml" - image: "../images/sun.png" + source: "../images/sun.png" } PaletteItem { anchors.verticalCenter: parent.verticalCenter componentFile: "GenericSceneItem.qml" - image: "../images/moon.png" + source: "../images/moon.png" } PaletteItem { anchors.verticalCenter: parent.verticalCenter componentFile: "PerspectiveItem.qml" - image: "../images/tree_s.png" + source: "../images/tree_s.png" } PaletteItem { anchors.verticalCenter: parent.verticalCenter componentFile: "PerspectiveItem.qml" - image: "../images/rabbit_brown.png" + source: "../images/rabbit_brown.png" } PaletteItem { anchors.verticalCenter: parent.verticalCenter componentFile: "PerspectiveItem.qml" - image: "../images/rabbit_bw.png" + source: "../images/rabbit_bw.png" } } } diff --git a/examples/declarative/toys/dynamicscene/qml/Sun.qml b/examples/declarative/toys/dynamicscene/qml/Sun.qml index d632461262..df3246d356 100644 --- a/examples/declarative/toys/dynamicscene/qml/Sun.qml +++ b/examples/declarative/toys/dynamicscene/qml/Sun.qml @@ -44,7 +44,7 @@ Image { id: sun property bool created: false - property string image: "../images/sun.png" + property string source: "../images/sun.png" source: image diff --git a/examples/declarative/ui-components/flipable/content/Card.qml b/examples/declarative/ui-components/flipable/content/Card.qml index 6374dd8eda..6574733abb 100644 --- a/examples/declarative/ui-components/flipable/content/Card.qml +++ b/examples/declarative/ui-components/flipable/content/Card.qml @@ -43,7 +43,7 @@ import QtQuick 1.0 Flipable { id: container - property alias image: frontImage.source + property alias source: frontImage.source property bool flipped: true property int xAxis: 0 property int yAxis: 0 diff --git a/examples/declarative/ui-components/flipable/flipable.qml b/examples/declarative/ui-components/flipable/flipable.qml index 6d0235ecb4..3b23aa0d69 100644 --- a/examples/declarative/ui-components/flipable/flipable.qml +++ b/examples/declarative/ui-components/flipable/flipable.qml @@ -49,7 +49,7 @@ Rectangle { Row { anchors.centerIn: parent; spacing: 30 - Card { image: "content/9_club.png"; angle: 180; yAxis: 1 } - Card { image: "content/5_heart.png"; angle: 540; xAxis: 1 } + Card { source: "content/9_club.png"; angle: 180; yAxis: 1 } + Card { source: "content/5_heart.png"; angle: 540; xAxis: 1 } } } diff --git a/src/declarative/particles/qsgfollowemitter_p.h b/src/declarative/particles/qsgfollowemitter_p.h index 314bd4e7c1..bf5d2fe8ef 100644 --- a/src/declarative/particles/qsgfollowemitter_p.h +++ b/src/declarative/particles/qsgfollowemitter_p.h @@ -56,13 +56,13 @@ class QSGFollowEmitter : public QSGParticleEmitter Q_OBJECT Q_PROPERTY(QString follow READ follow WRITE setFollow NOTIFY followChanged) //### Remove, and just document that particles per second is per particle? But has count issues - Q_PROPERTY(int particlesPerParticlePerSecond READ particlesPerParticlePerSecond WRITE setParticlesPerParticlePerSecond NOTIFY particlesPerParticlePerSecondChanged) + Q_PROPERTY(int emitRatePerParticle READ particlesPerParticlePerSecond WRITE setParticlesPerParticlePerSecond NOTIFY particlesPerParticlePerSecondChanged) //TODO: Document that FollowEmitter's box is where it follows. It emits in a rect centered on the followed particle //TODO: A set of properties that can involve the particle size of the followed - Q_PROPERTY(QSGParticleExtruder* emissionShape READ emissonShape WRITE setEmissionShape NOTIFY emissionShapeChanged) - Q_PROPERTY(qreal emissionHeight READ emitterYVariation WRITE setEmitterYVariation NOTIFY emitterYVariationChanged) - Q_PROPERTY(qreal emissionWidth READ emitterXVariation WRITE setEmitterXVariation NOTIFY emitterXVariationChanged) + Q_PROPERTY(QSGParticleExtruder* emitShape READ emissonShape WRITE setEmissionShape NOTIFY emissionShapeChanged) + Q_PROPERTY(qreal emitHeight READ emitterYVariation WRITE setEmitterYVariation NOTIFY emitterYVariationChanged) + Q_PROPERTY(qreal emitWidth READ emitterXVariation WRITE setEmitterXVariation NOTIFY emitterXVariationChanged) public: explicit QSGFollowEmitter(QSGItem *parent = 0); diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h index 1318647cc9..c6ec4c2c6a 100644 --- a/src/declarative/particles/qsgimageparticle_p.h +++ b/src/declarative/particles/qsgimageparticle_p.h @@ -130,7 +130,7 @@ struct IntermediateVertices { class QSGImageParticle : public QSGParticlePainter { Q_OBJECT - Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged) + Q_PROPERTY(QUrl source READ image WRITE setImage NOTIFY imageChanged) Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged) Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged) Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged) diff --git a/src/declarative/particles/qsgparticleemitter_p.h b/src/declarative/particles/qsgparticleemitter_p.h index ad6a5f645f..9fafd9d43d 100644 --- a/src/declarative/particles/qsgparticleemitter_p.h +++ b/src/declarative/particles/qsgparticleemitter_p.h @@ -66,14 +66,14 @@ class QSGParticleEmitter : public QSGItem Q_PROPERTY(QSGParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged) Q_PROPERTY(bool emitting READ emitting WRITE setEmitting NOTIFY emittingChanged) - Q_PROPERTY(qreal particlesPerSecond READ particlesPerSecond WRITE setParticlesPerSecond NOTIFY particlesPerSecondChanged) - Q_PROPERTY(int particleDuration READ particleDuration WRITE setParticleDuration NOTIFY particleDurationChanged) - Q_PROPERTY(int particleDurationVariation READ particleDurationVariation WRITE setParticleDurationVariation NOTIFY particleDurationVariationChanged) - Q_PROPERTY(int maxParticles READ maxParticleCount WRITE setMaxParticleCount NOTIFY maxParticleCountChanged) + Q_PROPERTY(qreal emitRate READ particlesPerSecond WRITE setParticlesPerSecond NOTIFY particlesPerSecondChanged) + Q_PROPERTY(int lifeSpan READ particleDuration WRITE setParticleDuration NOTIFY particleDurationChanged) + Q_PROPERTY(int lifeSpanVariation READ particleDurationVariation WRITE setParticleDurationVariation NOTIFY particleDurationVariationChanged) + Q_PROPERTY(int emitCap READ maxParticleCount WRITE setMaxParticleCount NOTIFY maxParticleCountChanged) - Q_PROPERTY(qreal particleSize READ particleSize WRITE setParticleSize NOTIFY particleSizeChanged) - Q_PROPERTY(qreal particleEndSize READ particleEndSize WRITE setParticleEndSize NOTIFY particleEndSizeChanged) - Q_PROPERTY(qreal particleSizeVariation READ particleSizeVariation WRITE setParticleSizeVariation NOTIFY particleSizeVariationChanged) + Q_PROPERTY(qreal size READ particleSize WRITE setParticleSize NOTIFY particleSizeChanged) + Q_PROPERTY(qreal endSize READ particleEndSize WRITE setParticleEndSize NOTIFY particleEndSizeChanged) + Q_PROPERTY(qreal sizeVariation READ particleSizeVariation WRITE setParticleSizeVariation NOTIFY particleSizeVariationChanged) Q_PROPERTY(QSGStochasticDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged) Q_PROPERTY(QSGStochasticDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) From e726fa507dd93224bbb8c36ff53f90f61be76061 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 9 Jun 2011 15:10:45 +1000 Subject: [PATCH 12/48] Rectify over-zealous script results. --- demos/declarative/minehunt/MinehuntCore/Explosion.qml | 2 +- demos/declarative/photoviewer/PhotoViewerCore/AlbumDelegate.qml | 2 +- demos/declarative/snake/content/Cookie.qml | 2 +- demos/declarative/snake/content/Link.qml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/demos/declarative/minehunt/MinehuntCore/Explosion.qml b/demos/declarative/minehunt/MinehuntCore/Explosion.qml index 668d7b1a19..33eabf033c 100644 --- a/demos/declarative/minehunt/MinehuntCore/Explosion.qml +++ b/demos/declarative/minehunt/MinehuntCore/Explosion.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 1.0 -import QtQuick.Particles 1.0 +import Qt.labs.particles 1.0 Item { property bool explode : false diff --git a/demos/declarative/photoviewer/PhotoViewerCore/AlbumDelegate.qml b/demos/declarative/photoviewer/PhotoViewerCore/AlbumDelegate.qml index c6b3e1b577..9fcd68b1ff 100644 --- a/demos/declarative/photoviewer/PhotoViewerCore/AlbumDelegate.qml +++ b/demos/declarative/photoviewer/PhotoViewerCore/AlbumDelegate.qml @@ -85,7 +85,7 @@ Component { anchors.centerIn: parent; anchors.verticalCenterOffset: -30 path: Path { PathAttribute { name: 'z'; value: 9999.0 } - PathLineShape { x: 1; y: 1 } + PathLine { x: 1; y: 1 } PathAttribute { name: 'z'; value: 0.0 } } } diff --git a/demos/declarative/snake/content/Cookie.qml b/demos/declarative/snake/content/Cookie.qml index 6efed6a154..a076978999 100644 --- a/demos/declarative/snake/content/Cookie.qml +++ b/demos/declarative/snake/content/Cookie.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 1.0 -import QtQuick.Particles 1.0 +import Qt.labs.particles 1.0 Item { id: root diff --git a/demos/declarative/snake/content/Link.qml b/demos/declarative/snake/content/Link.qml index 4c58c4dd5c..8c1f4866bb 100644 --- a/demos/declarative/snake/content/Link.qml +++ b/demos/declarative/snake/content/Link.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 1.0 -import QtQuick.Particles 1.0 +import Qt.labs.particles 1.0 Item { id:link property bool dying: false From 283a37447b8c400957c4da7dda35a84ea86ac4ad Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 9 Jun 2011 15:17:52 +1000 Subject: [PATCH 13/48] Automatically set system if parent is a ParticleSystem --- src/declarative/particles/qsgparticleaffector.cpp | 2 ++ src/declarative/particles/qsgparticleemitter.cpp | 2 ++ src/declarative/particles/qsgparticlepainter.cpp | 4 +++- src/declarative/particles/qsgparticlesystem.cpp | 10 ++++++++-- src/declarative/particles/qsgparticlesystem_p.h | 1 + 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/declarative/particles/qsgparticleaffector.cpp b/src/declarative/particles/qsgparticleaffector.cpp index d4b588a44f..96c5cfb25b 100644 --- a/src/declarative/particles/qsgparticleaffector.cpp +++ b/src/declarative/particles/qsgparticleaffector.cpp @@ -56,6 +56,8 @@ QSGParticleAffector::QSGParticleAffector(QSGItem *parent) : void QSGParticleAffector::componentComplete() { + if(!m_system && qobject_cast(parentItem())) + setSystem(qobject_cast(parentItem())); if(!m_system) qWarning() << "Affector created without a particle system specified";//TODO: useful QML warnings, like line number? QSGItem::componentComplete(); diff --git a/src/declarative/particles/qsgparticleemitter.cpp b/src/declarative/particles/qsgparticleemitter.cpp index c04c86cfe5..20f3c6bb4b 100644 --- a/src/declarative/particles/qsgparticleemitter.cpp +++ b/src/declarative/particles/qsgparticleemitter.cpp @@ -76,6 +76,8 @@ QSGParticleEmitter::~QSGParticleEmitter() void QSGParticleEmitter::componentComplete() { + if(!m_system && qobject_cast(parentItem())) + setSystem(qobject_cast(parentItem())); if(!m_system) qWarning() << "Emitter created without a particle system specified";//TODO: useful QML warnings, like line number? QSGItem::componentComplete(); diff --git a/src/declarative/particles/qsgparticlepainter.cpp b/src/declarative/particles/qsgparticlepainter.cpp index 8814c61b56..a5e8c0a3e6 100644 --- a/src/declarative/particles/qsgparticlepainter.cpp +++ b/src/declarative/particles/qsgparticlepainter.cpp @@ -54,8 +54,10 @@ QSGParticlePainter::QSGParticlePainter(QSGItem *parent) : void QSGParticlePainter::componentComplete() { + if(!m_system && qobject_cast(parentItem())) + setSystem(qobject_cast(parentItem())); if(!m_system) - qWarning() << "Particle created without a particle system specified";//TODO: useful QML warnings, like line number? + qWarning() << "ParticlePainter created without a particle system specified";//TODO: useful QML warnings, like line number? QSGItem::componentComplete(); } diff --git a/src/declarative/particles/qsgparticlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp index a2aa377c82..4a8f75bb04 100644 --- a/src/declarative/particles/qsgparticlesystem.cpp +++ b/src/declarative/particles/qsgparticlesystem.cpp @@ -67,7 +67,9 @@ QSGParticleData::QSGParticleData() } QSGParticleSystem::QSGParticleSystem(QSGItem *parent) : - QSGItem(parent), m_particle_count(0), m_running(true) , m_startTime(0), m_overwrite(false) + QSGItem(parent), m_particle_count(0), m_running(true) + , m_startTime(0), m_overwrite(false) + , m_componentComplete(false) { m_groupIds = QHash(); } @@ -111,7 +113,9 @@ void QSGParticleSystem::setRunning(bool arg) void QSGParticleSystem::componentComplete() { QSGItem::componentComplete(); - reset(); + m_componentComplete = true; + if(!m_emitters.isEmpty() && !m_particles.isEmpty()) + reset(); } void QSGParticleSystem::initializeSystem() @@ -191,6 +195,8 @@ void QSGParticleSystem::initializeSystem() void QSGParticleSystem::reset() { + if(!m_componentComplete) + return;//Batch starting reset()s a little //Clear guarded pointers which have been deleted int cleared = 0; cleared += m_emitters.removeAll(0); diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h index aab3c5d50f..9730ff35e8 100644 --- a/src/declarative/particles/qsgparticlesystem_p.h +++ b/src/declarative/particles/qsgparticlesystem_p.h @@ -163,6 +163,7 @@ private: qint64 m_startTime; int m_nextGroupId; bool m_overwrite; + bool m_componentComplete; }; //TODO: Clean up all this into ParticleData From 206a299923ca985b9ab8b47550f55a74f5473157 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 7 Jun 2011 10:46:00 +0200 Subject: [PATCH 14/48] Fix memory leak with AnimatedImage elements. When textures are constructed from pixmaps, we need to store the context they were constructed in so that they may also be cleaned up later. Change-Id: Ibdf5f60b598914d630c622341fae206f46cb9629 --- src/declarative/util/qdeclarativepixmapcache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp index dce938995d..108d2579ac 100644 --- a/src/declarative/util/qdeclarativepixmapcache.cpp +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -1119,6 +1119,7 @@ QSGTexture *QDeclarativePixmap::texture(QSGContext *context) const return d->texture; else if (d->pixmapStatus == Ready) { d->texture = context->createTexture(d->pixmap.toImage()); + d->context = context; return d->texture; } } From 73081387d84c4b77dafeda927d1fc1ebee6cfdf2 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Thu, 9 Jun 2011 18:29:50 +1000 Subject: [PATCH 15/48] Add a DragTarget element. Provides an area that can be used to handle events when other items are dragged over it. Task-number: QMLNG-32 --- .../dragtarget/dragtarget.qmlproject | 16 + .../dragtarget/lists/listmodel.qml | 256 +++++++++++++ .../dragtarget/lists/lists.qmlproject | 16 + .../declarative/dragtarget/text/dragtext.qml | 142 +++++++ .../dragtarget/text/text.qmlproject | 16 + .../declarative/dragtarget/tiles/DragTile.qml | 59 +++ .../declarative/dragtarget/tiles/DropTile.qml | 30 ++ .../declarative/dragtarget/tiles/tiles.qml | 85 +++++ src/declarative/items/items.pri | 3 + src/declarative/items/qsgcanvas.cpp | 86 +++++ src/declarative/items/qsgcanvas_p.h | 3 + src/declarative/items/qsgdragtarget.cpp | 361 ++++++++++++++++++ src/declarative/items/qsgdragtarget_p.h | 137 +++++++ src/declarative/items/qsgevent.h | 137 +++++++ src/declarative/items/qsgitem.cpp | 42 ++ src/declarative/items/qsgitem.h | 5 + src/declarative/items/qsgitem_p.h | 1 + src/declarative/items/qsgitemsmodule.cpp | 4 + src/declarative/items/qsgmousearea.cpp | 139 ++++++- src/declarative/items/qsgmousearea_p.h | 32 +- src/declarative/items/qsgmousearea_p_p.h | 1 + 21 files changed, 1563 insertions(+), 8 deletions(-) create mode 100644 examples/declarative/dragtarget/dragtarget.qmlproject create mode 100644 examples/declarative/dragtarget/lists/listmodel.qml create mode 100644 examples/declarative/dragtarget/lists/lists.qmlproject create mode 100644 examples/declarative/dragtarget/text/dragtext.qml create mode 100644 examples/declarative/dragtarget/text/text.qmlproject create mode 100644 examples/declarative/dragtarget/tiles/DragTile.qml create mode 100644 examples/declarative/dragtarget/tiles/DropTile.qml create mode 100644 examples/declarative/dragtarget/tiles/tiles.qml create mode 100644 src/declarative/items/qsgdragtarget.cpp create mode 100644 src/declarative/items/qsgdragtarget_p.h create mode 100644 src/declarative/items/qsgevent.h diff --git a/examples/declarative/dragtarget/dragtarget.qmlproject b/examples/declarative/dragtarget/dragtarget.qmlproject new file mode 100644 index 0000000000..d4909f8685 --- /dev/null +++ b/examples/declarative/dragtarget/dragtarget.qmlproject @@ -0,0 +1,16 @@ +import QmlProject 1.0 + +Project { + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + JavaScriptFiles { + directory: "." + } + ImageFiles { + directory: "." + } + /* List of plugin directories passed to QML runtime */ + // importPaths: [ " ../exampleplugin " ] +} diff --git a/examples/declarative/dragtarget/lists/listmodel.qml b/examples/declarative/dragtarget/lists/listmodel.qml new file mode 100644 index 0000000000..50b1d397c7 --- /dev/null +++ b/examples/declarative/dragtarget/lists/listmodel.qml @@ -0,0 +1,256 @@ +import QtQuick 2.0 + + +Rectangle { + id: root + color: "grey" + + width: 720 + height: 380 + + Component { + id: draggedText + Text { + x: rootTarget.dragX - 10 + y: rootTarget.dragY - 10 + width: 20 + height: 20 + + text: rootTarget.dragData.display + font.pixelSize: 18 + } + } + + DragTarget { + id: rootTarget + + anchors.fill: parent + } + + Loader { + anchors.fill: parent + sourceComponent: rootTarget.containsDrag ? draggedText : undefined + } + + GridView { + id: gridView + + width: 240 + height: 360 + + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.margins: 10 + + cellWidth: 60 + cellHeight: 60 + + model: ListModel { + id: gridModel + + ListElement { display: "1" } + ListElement { display: "2" } + ListElement { display: "3" } + ListElement { display: "4" } + ListElement { display: "5" } + ListElement { display: "6" } + ListElement { display: "7" } + ListElement { display: "8" } + ListElement { display: "9" } + ListElement { display: "10" } + ListElement { display: "11" } + ListElement { display: "12" } + ListElement { display: "13" } + ListElement { display: "14" } + ListElement { display: "15" } + ListElement { display: "16" } + ListElement { display: "17" } + ListElement { display: "18" } + ListElement { display: "19" } + ListElement { display: "20" } + ListElement { display: "21" } + ListElement { display: "22" } + ListElement { display: "23" } + ListElement { display: "24" } + } + + delegate: Rectangle { + id: root + + width: 60 + height: 60 + + color: "black" + + Text { + anchors.fill: parent + color: draggable.drag.active ? "gold" : "white" + text: display + font.pixelSize: 16 + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + MouseArea { + id: draggable + + property int initialIndex + + width: 60 + height: 60 + + drag.data: model + drag.keys: ["grid"] + drag.target: draggable + + states: State { + when: !draggable.drag.active + PropertyChanges { target: draggable; x: 0; y: 0 } + } + } + } + + DragTarget { + anchors.fill: parent + + keys: [ "grid" ] + onPositionChanged: { + var index = gridView.indexAt(drag.x, drag.y) + if (index != -1) + gridModel.move(drag.data.index, index, 1) + } + } + + DragTarget { + property int dragIndex + anchors.fill: parent + + keys: [ "list" ] + onEntered: { + dragIndex = gridView.indexAt(drag.x, drag.y) + if (dragIndex != -1) { + gridModel.insert(dragIndex, { "display": drag.data.display }) + } else { + event.accepted = false + } + } + onPositionChanged: { + var index = gridView.indexAt(drag.x, drag.y); + if (index != -1) { + gridModel.move(dragIndex, index, 1) + dragIndex = index + } + } + onExited: gridModel.remove(dragIndex, 1) + } + } + + ListView { + id: listView + + width: 240 + height: 360 + + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: 10 + + model: ListModel { + id: listModel + + ListElement { display: "a" } + ListElement { display: "b" } + ListElement { display: "c" } + ListElement { display: "d"} + ListElement { display: "e" } + ListElement { display: "f" } + ListElement { display: "g" } + ListElement { display: "h" } + ListElement { display: "i" } + ListElement { display: "j" } + ListElement { display: "k" } + ListElement { display: "l" } + ListElement { display: "m" } + ListElement { display: "n" } + ListElement { display: "o" } + ListElement { display: "p" } + ListElement { display: "q" } + ListElement { display: "r" } + ListElement { display: "s" } + ListElement { display: "t" } + ListElement { display: "u" } + ListElement { display: "v" } + ListElement { display: "w" } + ListElement { display: "x" } + } + + delegate: Rectangle { + id: root + + width: 240 + height: 15 + + color: "black" + + Text { + anchors.fill: parent + color: draggable.drag.active ? "gold" : "white" + text: display + font.pixelSize: 12 + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + MouseArea { + id: draggable + + width: 240 + height: 15 + + drag.data: model + drag.keys: ["list"] + drag.target: draggable + + states: State { + when: !draggable.drag.active + PropertyChanges { target: draggable; x: 0; y: 0 } + } + } + } + + DragTarget { + anchors.fill: parent + + keys: [ "list" ] + onPositionChanged: { + var index = listView.indexAt(drag.x, drag.y) + if (index != -1) + listModel.move(drag.data.index, index, 1) + } + } + + DragTarget { + property int dragIndex + anchors.fill: parent + + keys: [ "grid" ] + + onEntered: { + dragIndex = listView.indexAt(drag.x, drag.y) + if (dragIndex != -1) { + listModel.insert(dragIndex, { "display": drag.data.display }) + } else { + event.accepted = false + } + } + onPositionChanged: { + var index = listView.indexAt(drag.x, drag.y); + if (index != -1) { + listModel.move(dragIndex, index, 1) + dragIndex = index + } + } + onExited: listModel.remove(dragIndex, 1) + } + } +} diff --git a/examples/declarative/dragtarget/lists/lists.qmlproject b/examples/declarative/dragtarget/lists/lists.qmlproject new file mode 100644 index 0000000000..d4909f8685 --- /dev/null +++ b/examples/declarative/dragtarget/lists/lists.qmlproject @@ -0,0 +1,16 @@ +import QmlProject 1.0 + +Project { + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + JavaScriptFiles { + directory: "." + } + ImageFiles { + directory: "." + } + /* List of plugin directories passed to QML runtime */ + // importPaths: [ " ../exampleplugin " ] +} diff --git a/examples/declarative/dragtarget/text/dragtext.qml b/examples/declarative/dragtarget/text/dragtext.qml new file mode 100644 index 0000000000..c4a4f24f74 --- /dev/null +++ b/examples/declarative/dragtarget/text/dragtext.qml @@ -0,0 +1,142 @@ +import QtQuick 2.0 + +Item { + id: root + width: 320; height: 480 + + Rectangle { + id: inputRect + anchors.left: parent.left; anchors.right: parent.right; anchors.top: parent.top + anchors.margins: 2 + height: input.implicitHeight + 4 + + border.width: 1 + + TextInput { + id: input + anchors.fill: parent; anchors.margins: 2 + + text: "the quick brown fox jumped over the lazy dog" + + DragTarget { + id: inputTarget + + anchors.fill: parent + + Component { + id: draggedInputText + Text { + x: inputTarget.dragX + y: inputTarget.dragY + text: inputTarget.dragData + color: "blue" + font: input.font + } + } + + Loader { + sourceComponent: parent.containsDrag ? draggedInputText : undefined + } + } + + + MouseArea { + id: inputDraggable + + anchors.fill: parent + enabled: input.selectionStart != input.selectionEnd + + drag.data: input.selectedText + drag.target: inputDraggable + + drag.onDragged: { + var position = input.positionAt(mouse.x); + mouse.accepted = position >= input.selectionStart && position < input.selectionEnd + } + + MouseArea { + anchors.fill: parent + + onPressed: { + var position = input.positionAt(mouse.x); + if (position < input.selectionStart || position >= input.selectionEnd) { + input.cursorPosition = position + } else { + mouse.accepted = false + } + } + onPositionChanged: input.moveCursorSelection(input.positionAt(mouse.x)) + } + } + } + } + + Rectangle { + id: editRect + anchors.left: parent.left; anchors.right: parent.right; + anchors.top: inputRect.bottom; anchors.bottom: parent.bottom + anchors.margins: 2 + + border.width: 1 + + TextEdit { + id: edit + anchors.fill: parent; anchors.margins: 2 + + text: "the quick brown fox jumped over the lazy dog" + font.pixelSize: 18 + wrapMode: TextEdit.WordWrap + + DragTarget { + id: editTarget + + anchors.fill: parent + + + Component { + id: draggedEditText + Text { + x: editTarget.dragX + y: editTarget.dragY + text: editTarget.dragData + color: "red" + font: edit.font + } + } + + Loader { + sourceComponent: parent.containsDrag ? draggedEditText : undefined + } + } + + MouseArea { + id: editDraggable + + anchors.fill: parent + enabled: edit.selectionStart != edit.selectionEnd + + drag.data: edit.selectedText + drag.target: editDraggable + + drag.onDragged: { + var position = edit.positionAt(mouse.x, mouse.y); + mouse.accepted = position >= edit.selectionStart && position < edit.selectionEnd + } + + MouseArea { + anchors.fill: parent + + onPressed: { + var position = edit.positionAt(mouse.x, mouse.y); + if (position < edit.selectionStart || position >= edit.selectionEnd) { + edit.cursorPosition = position + } else { + mouse.accepted = false + } + } + onPositionChanged: edit.moveCursorSelection(edit.positionAt(mouse.x, mouse.y)) + } + } + } + } +} diff --git a/examples/declarative/dragtarget/text/text.qmlproject b/examples/declarative/dragtarget/text/text.qmlproject new file mode 100644 index 0000000000..d4909f8685 --- /dev/null +++ b/examples/declarative/dragtarget/text/text.qmlproject @@ -0,0 +1,16 @@ +import QmlProject 1.0 + +Project { + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + JavaScriptFiles { + directory: "." + } + ImageFiles { + directory: "." + } + /* List of plugin directories passed to QML runtime */ + // importPaths: [ " ../exampleplugin " ] +} diff --git a/examples/declarative/dragtarget/tiles/DragTile.qml b/examples/declarative/dragtarget/tiles/DragTile.qml new file mode 100644 index 0000000000..213373a392 --- /dev/null +++ b/examples/declarative/dragtarget/tiles/DragTile.qml @@ -0,0 +1,59 @@ +import QtQuick 2.0 + +Rectangle { + id: dragRectangle + + property Item dropTarget + + property string colorKey + + color: colorKey + + width: 100; height: 100 + + Text { + anchors.fill: parent + color: "white" + font.pixelSize: 90 + text: modelData + 1 + horizontalAlignment:Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + MouseArea { + id: draggable + + anchors.fill: parent + + drag.target: parent + drag.keys: [ colorKey ] + + drag.onDropped: dropTarget = dropItem + + states: [ + State { + when: dragRectangle.dropTarget != undefined && !draggable.drag.active + ParentChange { + target: dragRectangle + parent: dropTarget + x: 0 + y: 0 + } + }, + State { + when: dragRectangle.dropTarget != undefined && draggable.drag.active + ParentChange { + target: dragRectangle + parent: dropTarget + } + }, + State { + when: !draggable.drag.active + AnchorChanges { + target: dragRectangle + anchors.horizontalCenter: parent.horizontalCenter + } + } + ] + } +} diff --git a/examples/declarative/dragtarget/tiles/DropTile.qml b/examples/declarative/dragtarget/tiles/DropTile.qml new file mode 100644 index 0000000000..9d968753db --- /dev/null +++ b/examples/declarative/dragtarget/tiles/DropTile.qml @@ -0,0 +1,30 @@ +import QtQuick 2.0 + +Rectangle { + id: dropRectangle + + property string colorKey + + color: colorKey + + width: 100; height: 100 + + DragTarget { + id: dragTarget + + anchors.fill: parent + + keys: [ colorKey ] + dropItem: dropRectangle + } + + states: [ + State { + when: dragTarget.containsDrag + PropertyChanges { + target: dropRectangle + color: "grey" + } + } + ] +} diff --git a/examples/declarative/dragtarget/tiles/tiles.qml b/examples/declarative/dragtarget/tiles/tiles.qml new file mode 100644 index 0000000000..d8bcb39bd2 --- /dev/null +++ b/examples/declarative/dragtarget/tiles/tiles.qml @@ -0,0 +1,85 @@ +import QtQuick 2.0 + +Rectangle { + id: root + + width: 620 + height: 410 + + color: "black" + + DragTarget { + id: resetTarget + + anchors.fill: parent + } + + Grid { + id: redDestination + + anchors.left: redSource.right; anchors.top: parent.top; + anchors.margins: 5 + width: 300 + height: 300 + + opacity: 0.5 + + columns: 3 + + Repeater { + model: 9 + delegate: DropTile { + colorKey: "red" + } + } + } + + Grid { + id: blueDestination + + anchors.right: blueSource.left; anchors.bottom: parent.bottom; + anchors.margins: 5 + width: 300 + height: 300 + + opacity: 0.5 + + columns: 3 + + Repeater { + model: 9 + delegate: DropTile { + colorKey: "blue" + } + } + } + + Column { + id: redSource + + anchors.left: parent.left; anchors.top: parent.top; anchors.bottom: parent.bottom + anchors.margins: 5 + width: 100 + + Repeater { + model: 9 + delegate: DragTile { + colorKey: "red" + } + } + } + Column { + id: blueSource + + anchors.right: parent.right; anchors.top: parent.top; anchors.bottom: parent.bottom + anchors.margins: 5 + width: 100 + + Repeater { + model: 9 + delegate: DragTile { + colorKey: "blue" + } + } + } +} diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri index f29a82e77e..bf92025b75 100644 --- a/src/declarative/items/items.pri +++ b/src/declarative/items/items.pri @@ -64,6 +64,8 @@ HEADERS += \ $$PWD/qsgspriteengine_p.h \ $$PWD/qsgsprite_p.h \ $$PWD/qsgspriteimage_p.h \ + $$PWD/qsgevent.h \ + $$PWD/qsgdragtarget_p.h \ SOURCES += \ $$PWD/qsgevents.cpp \ @@ -106,6 +108,7 @@ SOURCES += \ $$PWD/qsgspriteengine.cpp \ $$PWD/qsgsprite.cpp \ $$PWD/qsgspriteimage.cpp \ + $$PWD/qsgdragtarget.cpp \ SOURCES += \ $$PWD/qsgshadereffectitem.cpp \ diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp index f991609dfe..3a88fbb790 100644 --- a/src/declarative/items/qsgcanvas.cpp +++ b/src/declarative/items/qsgcanvas.cpp @@ -45,6 +45,8 @@ #include "qsgitem.h" #include "qsgitem_p.h" +#include "qsgevent.h" + #include #include @@ -987,6 +989,12 @@ bool QSGCanvas::event(QEvent *e) d->clearHover(); d->lastMousePosition = QPoint(); break; + case QSGEvent::SGDragEnter: + case QSGEvent::SGDragExit: + case QSGEvent::SGDragMove: + case QSGEvent::SGDragDrop: + d->deliverDragEvent(static_cast(e)); + break; default: break; } @@ -1446,6 +1454,78 @@ bool QSGCanvasPrivate::deliverTouchPoints(QSGItem *item, QTouchEvent *event, con return false; } +void QSGCanvasPrivate::deliverDragEvent(QSGDragEvent *event) +{ + Q_Q(QSGCanvas); + if (event->type() == QSGEvent::SGDragExit || event->type() == QSGEvent::SGDragDrop) { + if (QSGItem *grabItem = event->grabItem()) { + event->setPosition(grabItem->mapFromScene(event->scenePosition())); + q->sendEvent(grabItem, event); + } + } else if (!deliverDragEvent(rootItem, event)) { + if (QSGItem *grabItem = event->grabItem()) { + QSGDragEvent exitEvent(QSGEvent::SGDragExit, *event); + exitEvent.setPosition(grabItem->mapFromScene(event->scenePosition())); + q->sendEvent(grabItem, &exitEvent); + event->setDropItem(0); + event->setGrabItem(0); + } + event->setAccepted(false); + } +} + +bool QSGCanvasPrivate::deliverDragEvent(QSGItem *item, QSGDragEvent *event) +{ + Q_Q(QSGCanvas); + QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { + QPointF p = item->mapFromScene(event->scenePosition()); + if (!QRectF(0, 0, item->width(), item->height()).contains(p)) + return false; + } + + QList children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QSGItem *child = children.at(ii); + if (!child->isVisible() || !child->isEnabled()) + continue; + if (deliverDragEvent(child, event)) + return true; + } + + QPointF p = item->mapFromScene(event->scenePosition()); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + event->setPosition(p); + + if (event->type() == QSGEvent::SGDragMove && item != event->grabItem()) { + QSGDragEvent enterEvent(QSGEvent::SGDragEnter, *event); + q->sendEvent(item, &enterEvent); + if (enterEvent.isAccepted()) { + if (QSGItem *grabItem = event->grabItem()) { + QSGDragEvent exitEvent(QSGEvent::SGDragExit, *event); + q->sendEvent(grabItem, &exitEvent); + } + event->setDropItem(enterEvent.dropItem()); + event->setGrabItem(item); + } else { + return false; + } + } + + q->sendEvent(item, event); + if (event->isAccepted()) { + event->setGrabItem(item); + return true; + } + event->setAccepted(true); + } + + return false; +} + bool QSGCanvasPrivate::sendFilteredMouseEvent(QSGItem *target, QSGItem *item, QGraphicsSceneMouseEvent *event) { if (!target) @@ -1521,6 +1601,12 @@ bool QSGCanvas::sendEvent(QSGItem *item, QEvent *e) case QEvent::TouchEnd: QSGItemPrivate::get(item)->deliverTouchEvent(static_cast(e)); break; + case QSGEvent::SGDragEnter: + case QSGEvent::SGDragExit: + case QSGEvent::SGDragMove: + case QSGEvent::SGDragDrop: + QSGItemPrivate::get(item)->deliverDragEvent(static_cast(e)); + break; default: break; } diff --git a/src/declarative/items/qsgcanvas_p.h b/src/declarative/items/qsgcanvas_p.h index 90132f8c91..9b2683cc38 100644 --- a/src/declarative/items/qsgcanvas_p.h +++ b/src/declarative/items/qsgcanvas_p.h @@ -55,6 +55,7 @@ #include "qsgitem.h" #include "qsgcanvas.h" +#include "qsgevent.h" #include #include @@ -116,6 +117,8 @@ public: bool deliverHoverEvent(QSGItem *, QGraphicsSceneHoverEvent *); void sendHoverEvent(QEvent::Type, QSGItem *, QGraphicsSceneHoverEvent *); void clearHover(); + void deliverDragEvent(QSGDragEvent *); + bool deliverDragEvent(QSGItem *item, QSGDragEvent *); QDeclarativeGuard hoverItem; enum FocusOption { diff --git a/src/declarative/items/qsgdragtarget.cpp b/src/declarative/items/qsgdragtarget.cpp new file mode 100644 index 0000000000..c1ed167ee6 --- /dev/null +++ b/src/declarative/items/qsgdragtarget.cpp @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdragtarget_p.h" +#include "qsgitem_p.h" +#include "qsgcanvas.h" + +/*! + \qmlclass DragEvent QSGDragEvent + \brief The DragEvent object provides information about a drag event. + + The position of the drag event can be obtained from the \l x and \l + properties, the \l keys property identifies the drag keys of the event + source and the \l data property contains the payload of the drag event. +*/ + +/*! + \qmlproperty real DragEvent::x + + This property holds the x coordinate of a drag event. +*/ + +/*! + \qmlproperty real DragEvent::y + + This property holds the y coordinate of a drag event. +*/ + +/*! + \qmlproperty stringlist DragEvent::keys + + This property holds a list of keys identifying the data type or source of a + drag event. +*/ + +/*! + \qmlproperty variant DragEvent::data + + This property holds data payload of a drag event. +*/ + +/*! + \qmlproperty real DragEvent::accepted + + This property holds whether the drag event was accepted by a handler. + + The default value is true. +*/ + +class QSGDragTargetPrivate : public QSGItemPrivate +{ + Q_DECLARE_PUBLIC(QSGDragTarget) + +public: + QSGDragTargetPrivate(); + ~QSGDragTargetPrivate(); + + bool hasMatchingKey(const QStringList &keys) const; + + QStringList keys; + QRegExp keyRegExp; + QVariant dragData; + QPointF dragPosition; + QSGItem *dropItem; + bool containsDrag : 1; +}; + +QSGDragTargetPrivate::QSGDragTargetPrivate() + : dropItem(0) + , containsDrag(false) +{ +} + +QSGDragTargetPrivate::~QSGDragTargetPrivate() +{ +} + +/*! + \qmlclass DragTarget QSGDragTarget + \brief The DragTarget item provides drag and drop handling. + + A DragTarget is an invisible item which receives events when another item + is dragged over it. + + A MouseArea item can be used to drag items. + + The \l keys property can be used to filter drag events which don't include + a matching key. + + The \l dropItem property is communicated to the source of a drag event as + the recipient of a drop on the drag target. + + The \l delegate property provides a means to specify a component to be + instantiated for each active drag over a drag target. +*/ + +QSGDragTarget::QSGDragTarget(QSGItem *parent) + : QSGItem(*new QSGDragTargetPrivate, parent) +{ +} + +QSGDragTarget::~QSGDragTarget() +{ +} + +/*! + \qmlproperty bool DragTarget::containsDrag + + This property identifies whether the DragTarget currently contains any + dragged items. +*/ + +bool QSGDragTarget::containsDrag() const +{ + Q_D(const QSGDragTarget); + return d->containsDrag; +} + +/*! + \qmlproperty stringlist DragTarget::keys + + This property holds a list of drag keys a DragTarget will accept. +*/ + +QStringList QSGDragTarget::keys() const +{ + Q_D(const QSGDragTarget); + return d->keys; +} + +void QSGDragTarget::setKeys(const QStringList &keys) +{ + Q_D(QSGDragTarget); + if (d->keys != keys) { + d->keys = keys; + + if (keys.isEmpty()) { + d->keyRegExp = QRegExp(); + } else { + QString pattern = QLatin1Char('(') + QRegExp::escape(keys.first()); + for (int i = 1; i < keys.count(); ++i) + pattern += QLatin1Char('|') + QRegExp::escape(keys.at(i)); + pattern += QLatin1Char(')'); + d->keyRegExp = QRegExp(pattern.replace(QLatin1String("\\*"), QLatin1String(".+"))); + } + emit keysChanged(); + } +} + +/*! + \qmlproperty Item DragTarget::dropItem + + This property identifies an item as the recipient of a drop event within + a DragTarget. + + \sa MouseArea::drag.dropItem +*/ + +QSGItem *QSGDragTarget::dropItem() const +{ + Q_D(const QSGDragTarget); + return d->dropItem; +} + +void QSGDragTarget::setDropItem(QSGItem *item) +{ + Q_D(QSGDragTarget); + if (d->dropItem != item) { + d->dropItem = item; + emit dropItemChanged(); + } +} + +void QSGDragTarget::resetDropItem() +{ + Q_D(QSGDragTarget); + if (d->dropItem) { + d->dropItem = 0; + emit dropItemChanged(); + } +} + +qreal QSGDragTarget::dragX() const +{ + Q_D(const QSGDragTarget); + return d->dragPosition.x(); +} + +qreal QSGDragTarget::dragY() const +{ + Q_D(const QSGDragTarget); + return d->dragPosition.y(); +} + +QVariant QSGDragTarget::dragData() const +{ + Q_D(const QSGDragTarget); + return d->dragData; +} + +/*! + \qmlsignal DragTarget::onPositionChanged(DragEvent drag) + \qmlattachedsignal DragTarget::onPositionChanged(DragEvent drag) + + This handler is called when the position of a drag has changed. +*/ + +void QSGDragTarget::dragMoveEvent(QSGDragEvent *event) +{ + Q_D(QSGDragTarget); + if (!d->containsDrag) { + event->setAccepted(false); + return; + } + + event->setDropItem(d->dropItem); + + d->dragPosition = event->position(); + emit dragPositionChanged(); + + QSGDragTargetEvent dragTargetEvent(event); + emit positionChanged(&dragTargetEvent); +} + +bool QSGDragTargetPrivate::hasMatchingKey(const QStringList &keys) const +{ + if (keyRegExp.isEmpty()) + return true; + + foreach (const QString &key, keys) { + if (keyRegExp.exactMatch(key)) + return true; + } + return false; +} + +/*! + \qmlsignal DragTarget::onEntered(DragEvent drag) + \qmlattachedsignal DragTarget::onEntered(DragEvent drag) + + This handler is called when a drag enters the bounds of a DragTarget. +*/ + +void QSGDragTarget::dragEnterEvent(QSGDragEvent *event) +{ + Q_D(QSGDragTarget); + if (!d->effectiveEnable || !d->hasMatchingKey(event->keys()) || d->containsDrag) { + event->setAccepted(false); + return; + } + + event->setDropItem(d->dropItem); + + QSGDragTargetEvent dragTargetEvent(event); + emit entered(&dragTargetEvent); + + if (event->isAccepted()) { + + d->dragData = event->data(); + d->containsDrag = true; + if (!d->dragData.isNull()) + emit dragDataChanged(); + emit containsDragChanged(); + } +} + +/*! + \qmlsignal DragTarget::onExited(DragEvent drag) + \qmlattachedsignal DragTarget::onExited(DragEvent drag) + + This handler is called when a drag exits the bounds of a DragTarget. +*/ + +void QSGDragTarget::dragExitEvent(QSGDragEvent *event) +{ + Q_D(QSGDragTarget); + if (!d->containsDrag) { + event->setAccepted(false); + return; + } + + QSGDragTargetEvent dragTargetEvent(event); + emit exited(&dragTargetEvent); + + d->containsDrag = false; + emit containsDragChanged(); + if (!d->dragData.isNull()) { + d->dragData = QVariant(); + emit dragDataChanged(); + } +} + +/*! + \qmlsignal DragTarget::onDropped(DragEvent drag) + \qmlattachedsignal DragTarget::onDropped(DragEvent drag) + + This handler is called when a drop event occurs within the bounds of a + a DragTarget. +*/ + +void QSGDragTarget::dragDropEvent(QSGDragEvent *event) +{ + Q_D(QSGDragTarget); + if (!d->containsDrag) { + event->setAccepted(false); + return; + } + + event->setDropItem(d->dropItem); + + QSGDragTargetEvent dragTargetEvent(event); + emit dropped(&dragTargetEvent); + + d->containsDrag = false; + emit containsDragChanged(); + if (!d->dragData.isNull()) { + d->dragData = QVariant(); + emit dragDataChanged(); + } +} + +QT_END_NAMESPACE + diff --git a/src/declarative/items/qsgdragtarget_p.h b/src/declarative/items/qsgdragtarget_p.h new file mode 100644 index 0000000000..ad13e11854 --- /dev/null +++ b/src/declarative/items/qsgdragtarget_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDRAGTARGET_P_H +#define QSGDRAGTARGET_P_H + +#include "qsgitem.h" +#include "qsgevent.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGDragTargetEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal x READ x) + Q_PROPERTY(qreal y READ y) + Q_PROPERTY(QVariant data READ data) + Q_PROPERTY(QStringList keys READ keys) + Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) +public: + QSGDragTargetEvent(QSGDragEvent *event) : _event(event) {} + + qreal x() const { return _event->x(); } + qreal y() const { return _event->y(); } + + QVariant data() const { return _event->data(); } + QStringList keys() const { return _event->keys(); } + + bool accepted() const { return _event->isAccepted(); } + void setAccepted(bool accepted) { _event->setAccepted(accepted); } + +private: + QSGDragEvent *_event; +}; + +class QSGDragTargetPrivate; +class Q_AUTOTEST_EXPORT QSGDragTarget : public QSGItem +{ + Q_OBJECT + Q_PROPERTY(bool containsDrag READ containsDrag NOTIFY containsDragChanged) + Q_PROPERTY(QSGItem *dropItem READ dropItem WRITE setDropItem NOTIFY dropItemChanged RESET resetDropItem) + Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged) + Q_PROPERTY(qreal dragX READ dragX NOTIFY dragPositionChanged) + Q_PROPERTY(qreal dragY READ dragY NOTIFY dragPositionChanged) + Q_PROPERTY(QVariant dragData READ dragData NOTIFY dragDataChanged) + +public: + QSGDragTarget(QSGItem *parent=0); + ~QSGDragTarget(); + + bool containsDrag() const; + void setContainsDrag(bool drag); + + QStringList keys() const; + void setKeys(const QStringList &keys); + + QSGItem *dropItem() const; + void setDropItem(QSGItem *item); + void resetDropItem(); + + qreal dragX() const; + qreal dragY() const; + QVariant dragData() const; + +Q_SIGNALS: + void containsDragChanged(); + void keysChanged(); + void dropItemChanged(); + void dragPositionChanged(); + void dragDataChanged(); + + void entered(QSGDragTargetEvent *drag); + void exited(QSGDragTargetEvent *drag); + void positionChanged(QSGDragTargetEvent *drag); + void dropped(QSGDragTargetEvent *drag); + +protected: + void dragMoveEvent(QSGDragEvent *event); + void dragEnterEvent(QSGDragEvent *event); + void dragExitEvent(QSGDragEvent *event); + void dragDropEvent(QSGDragEvent *event); + +private: + Q_DISABLE_COPY(QSGDragTarget) + Q_DECLARE_PRIVATE(QSGDragTarget) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QSGDragTargetEvent) +QML_DECLARE_TYPE(QSGDragTarget) + +QT_END_HEADER + +#endif // QSGDRAGTARGET_P_H diff --git a/src/declarative/items/qsgevent.h b/src/declarative/items/qsgevent.h new file mode 100644 index 0000000000..93b53d1312 --- /dev/null +++ b/src/declarative/items/qsgevent.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDRAGEVENT_H +#define QDRAGEVENT_H + +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGItem; + +class Q_DECLARATIVE_EXPORT QSGEvent : public QEvent +{ +public: + // XXX: Merge types into QEvent or formally reserve a suitable range. + // Alternatively start from QEvent::User and add a SGUser value for use by items. + enum SGType + { + SGDragEnter = 600, + SGDragExit, + SGDragMove, + SGDragDrop + }; + + QSGEvent(QSGEvent::SGType type) : QEvent(Type(type)) {} + + SGType type() const { return SGType(QEvent::type()); } +}; + +class Q_DECLARATIVE_EXPORT QSGDragEvent : public QSGEvent +{ +public: + QSGDragEvent( + SGType type, + const QPointF &scenePosition, + const QVariant &data, + const QStringList &keys, + QSGItem *grabItem = 0) + : QSGEvent(type) + , _scenePosition(scenePosition), + _data(data) + , _keys(keys) + , _dropItem(0) + , _grabItem(grabItem) + { + } + QSGDragEvent(SGType type, const QSGDragEvent &event) + : QSGEvent(type) + , _scenePosition(event._scenePosition) + , _position(event._position) + , _data(event._data) + , _keys(event._keys) + , _dropItem(event._dropItem) + , _grabItem(event._grabItem) + { + } + + QVariant data() const { return _data; } + + qreal x() const { return _position.x(); } + qreal y() const { return _position.y(); } + QPointF position() const { return _position; } + void setPosition(const QPointF &position) { _position = position; } + + QPointF scenePosition() const { return _scenePosition; } + + QStringList keys() const { return _keys; } + + QSGItem *dropItem() const { return _dropItem; } + void setDropItem(QSGItem *dropItem) { _dropItem = dropItem; } + + QSGItem *grabItem() const { return _grabItem; } + void setGrabItem(QSGItem *item) { _grabItem = item; } + +private: + QPointF _scenePosition; + QPointF _position; + QVariant _data; + QStringList _keys; + QSGItem *_dropItem; + QSGItem *_grabItem; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/declarative/items/qsgitem.cpp b/src/declarative/items/qsgitem.cpp index f2d26955aa..328da16ff4 100644 --- a/src/declarative/items/qsgitem.cpp +++ b/src/declarative/items/qsgitem.cpp @@ -45,6 +45,7 @@ #include "qsgcanvas.h" #include #include "qsgcanvas_p.h" +#include "qsgevent.h" #include "qsgevents_p_p.h" @@ -1689,6 +1690,26 @@ void QSGItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) Q_UNUSED(event); } +void QSGItem::dragMoveEvent(QSGDragEvent *event) +{ + event->setAccepted(false); +} + +void QSGItem::dragEnterEvent(QSGDragEvent *event) +{ + event->setAccepted(false); +} + +void QSGItem::dragExitEvent(QSGDragEvent *event) +{ + event->setAccepted(false); +} + +void QSGItem::dragDropEvent(QSGDragEvent *event) +{ + event->setAccepted(false); +} + bool QSGItem::childMouseEventFilter(QSGItem *, QEvent *) { return false; @@ -2151,6 +2172,27 @@ void QSGItemPrivate::deliverHoverEvent(QGraphicsSceneHoverEvent *e) } } +void QSGItemPrivate::deliverDragEvent(QSGDragEvent *e) +{ + Q_Q(QSGItem); + switch (e->type()) { + default: + Q_ASSERT(!"Unknown event type"); + case QSGEvent::SGDragEnter: + q->dragEnterEvent(e); + break; + case QSGEvent::SGDragExit: + q->dragExitEvent(e); + break; + case QSGEvent::SGDragMove: + q->dragMoveEvent(e); + break; + case QSGEvent::SGDragDrop: + q->dragDropEvent(e); + break; + } +} + void QSGItem::itemChange(ItemChange change, const ItemChangeData &value) { Q_UNUSED(change); diff --git a/src/declarative/items/qsgitem.h b/src/declarative/items/qsgitem.h index 564d819000..995b5cbcd1 100644 --- a/src/declarative/items/qsgitem.h +++ b/src/declarative/items/qsgitem.h @@ -89,6 +89,7 @@ class QSGKeyEvent; class QSGAnchors; class QSGItemPrivate; class QSGCanvas; +class QSGDragEvent; class QSGEngine; class QTouchEvent; class QSGNode; @@ -363,6 +364,10 @@ protected: virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event); virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event); virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + virtual void dragMoveEvent(QSGDragEvent *event); + virtual void dragEnterEvent(QSGDragEvent *event); + virtual void dragExitEvent(QSGDragEvent *event); + virtual void dragDropEvent(QSGDragEvent *event); virtual bool childMouseEventFilter(QSGItem *, QEvent *); virtual void geometryChanged(const QRectF &newGeometry, diff --git a/src/declarative/items/qsgitem_p.h b/src/declarative/items/qsgitem_p.h index 0f4a3213b4..300ccdc49e 100644 --- a/src/declarative/items/qsgitem_p.h +++ b/src/declarative/items/qsgitem_p.h @@ -324,6 +324,7 @@ public: void deliverWheelEvent(QGraphicsSceneWheelEvent *); void deliverTouchEvent(QTouchEvent *); void deliverHoverEvent(QGraphicsSceneHoverEvent *); + void deliverDragEvent(QSGDragEvent *); bool calcEffectiveVisible() const; void setEffectiveVisibleRecur(bool); diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp index a29776fc68..f1e3a0cb91 100644 --- a/src/declarative/items/qsgitemsmodule.cpp +++ b/src/declarative/items/qsgitemsmodule.cpp @@ -77,6 +77,7 @@ #include "qsgcontext2d_p.h" #include "qsgsprite_p.h" #include "qsgspriteimage_p.h" +#include "qsgdragtarget_p.h" static QDeclarativePrivate::AutoParentResult qsgitem_autoParent(QObject *obj, QObject *parent) { @@ -189,6 +190,9 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(); qmlRegisterType(uri, major, minor,"AnchorAnimation"); qmlRegisterType(uri, major, minor,"ParentAnimation"); + + qmlRegisterType("QtQuick", 2, 0, "DragTarget"); + qmlRegisterType(); } void QSGItemsModule::defineModule() diff --git a/src/declarative/items/qsgmousearea.cpp b/src/declarative/items/qsgmousearea.cpp index 887d78a64d..6b4311e698 100644 --- a/src/declarative/items/qsgmousearea.cpp +++ b/src/declarative/items/qsgmousearea.cpp @@ -43,6 +43,7 @@ #include "qsgmousearea_p.h" #include "qsgmousearea_p_p.h" #include "qsgcanvas.h" +#include "qsgevent.h" #include "qsgevents_p_p.h" #include @@ -54,8 +55,8 @@ QT_BEGIN_NAMESPACE static const int PressAndHoldDelay = 800; QSGDrag::QSGDrag(QObject *parent) -: QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), -_active(false), _filterChildren(false) +: QObject(parent), _target(0), _dropItem(0), _grabItem(0), _axis(XandYAxis), _xmin(-FLT_MAX), +_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false) { } @@ -78,12 +79,70 @@ void QSGDrag::setTarget(QSGItem *t) void QSGDrag::resetTarget() { - if (!_target) + if (_target == 0) return; _target = 0; emit targetChanged(); } +/*! + \qmlproperty Item MouseArea::drag.dropItem + + This property holds the item an active drag will be dropped on if released + at the current position. +*/ + +QSGItem *QSGDrag::dropItem() const +{ + return _dropItem; +} + +void QSGDrag::setDropItem(QSGItem *item) +{ + if (_dropItem != item) { + _dropItem = item; + emit dropItemChanged(); + } +} + +QSGItem *QSGDrag::grabItem() const +{ + return _grabItem; +} + +void QSGDrag::setGrabItem(QSGItem *item) +{ + _grabItem = item; +} + +/*! + \qmlproperty variant MouseArea::drag.data + + This property holds the data sent to recipients of drag events generated + by a MouseArea. +*/ + +QVariant QSGDrag::data() const +{ + return _data; +} + +void QSGDrag::setData(const QVariant &data) +{ + if (_data != data) { + _data = data; + emit dataChanged(); + } +} + +void QSGDrag::resetData() +{ + if (!_data.isNull()) { + _data = QVariant(); + emit dataChanged(); + } +} + QSGDrag::Axis QSGDrag::axis() const { return _axis; @@ -175,9 +234,30 @@ void QSGDrag::setFilterChildren(bool filter) emit filterChildrenChanged(); } +/*! + \qmlproperty stringlist MouseArea::drag.keys + + This property holds a list of keys drag recipients can use to identify the + source or data type of a drag event. +*/ + +QStringList QSGDrag::keys() const +{ + return _keys; +} + +void QSGDrag::setKeys(const QStringList &keys) +{ + if (_keys != keys) { + _keys = keys; + emit keysChanged(); + } +} + QSGMouseAreaPrivate::QSGMouseAreaPrivate() : absorb(true), hovered(false), pressed(false), longPress(false), - moved(false), stealMouse(false), doubleClick(false), preventStealing(false), drag(0) + moved(false), stealMouse(false), doubleClick(false), preventStealing(false), dragRejected(false), + drag(0) { Q_Q(QSGMouseArea); forwardTo = QDeclarativeListProperty(q, forwardToList); @@ -384,6 +464,7 @@ void QSGMouseArea::mousePressEvent(QGraphicsSceneMouseEvent *event) QSGItem::mousePressEvent(event); else { d->longPress = false; + d->dragRejected = false; d->saveEvent(event); if (d->drag) { d->dragX = drag()->axis() & QSGDrag::XAxis; @@ -440,8 +521,24 @@ void QSGMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event) qreal dx = qAbs(curLocalPos.x() - startLocalPos.x()); qreal dy = qAbs(curLocalPos.y() - startLocalPos.y()); - if (keepMouseGrab() && d->stealMouse) - d->drag->setActive(true); + if (keepMouseGrab() && d->stealMouse && !d->dragRejected && !d->drag->active()) { + QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + d->drag->emitDragged(&me); + if (me.isAccepted()) { + d->drag->setActive(true); + QSGDragEvent dragEvent( + QSGEvent::SGDragEnter, + d->startScene, + d->drag->data(), + d->drag->keys()); + QCoreApplication::sendEvent(canvas(), &dragEvent); + + d->drag->setGrabItem(dragEvent.grabItem()); + d->drag->setDropItem(dragEvent.dropItem()); + } else { + d->dragRejected = true; + } + } if (d->dragX && d->drag->active()) { qreal x = (curLocalPos.x() - startLocalPos.x()) + d->startX; @@ -470,6 +567,18 @@ void QSGMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event) } d->moved = true; + + if (d->drag->active()) { + QSGDragEvent dragEvent( + QSGEvent::SGDragMove, + event->scenePos(), + d->drag->data(), + d->drag->keys(), + d->drag->grabItem()); + QCoreApplication::sendEvent(canvas(), &dragEvent); + d->drag->setGrabItem(dragEvent.grabItem()); + d->drag->setDropItem(dragEvent.dropItem()); + } } QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); emit mousePositionChanged(&me); @@ -490,8 +599,24 @@ void QSGMouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) } else { d->saveEvent(event); setPressed(false); - if (d->drag) + if (d->drag && d->drag->active()) { + QSGDragEvent dragEvent( + QSGEvent::SGDragDrop, + event->scenePos(), + d->drag->data(), + d->drag->keys(), + d->drag->grabItem()); + QCoreApplication::sendEvent(canvas(), &dragEvent); + d->drag->setGrabItem(0); + if (dragEvent.isAccepted()) { + d->drag->setDropItem(dragEvent.dropItem()); + d->drag->emitDropped(dragEvent.dropItem()); + } else { + d->drag->emitCanceled(); + } + d->drag->setDropItem(0); d->drag->setActive(false); + } // If we don't accept hover, we need to reset containsMouse. if (!acceptHoverEvents()) setHovered(false); diff --git a/src/declarative/items/qsgmousearea_p.h b/src/declarative/items/qsgmousearea_p.h index 469b9f7168..d7248bca37 100644 --- a/src/declarative/items/qsgmousearea_p.h +++ b/src/declarative/items/qsgmousearea_p.h @@ -51,12 +51,15 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) +class QSGMouseEvent; class Q_AUTOTEST_EXPORT QSGDrag : public QObject { Q_OBJECT Q_ENUMS(Axis) Q_PROPERTY(QSGItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget) + Q_PROPERTY(QSGItem *dropItem READ dropItem NOTIFY dropItemChanged) + Q_PROPERTY(QVariant data READ data WRITE setData NOTIFY dataChanged RESET resetData) Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged) Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged) Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged) @@ -64,6 +67,7 @@ class Q_AUTOTEST_EXPORT QSGDrag : public QObject Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged) Q_PROPERTY(bool active READ active NOTIFY activeChanged) Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged) + Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged) //### consider drag and drop public: @@ -74,6 +78,16 @@ public: void setTarget(QSGItem *); void resetTarget(); + QSGItem *dropItem() const; + void setDropItem(QSGItem *item); + + QSGItem *grabItem() const; + void setGrabItem(QSGItem *grabItem); + + QVariant data() const; + void setData(const QVariant &data); + void resetData(); + enum Axis { XAxis=0x01, YAxis=0x02, XandYAxis=0x03 }; Axis axis() const; void setAxis(Axis); @@ -93,8 +107,17 @@ public: bool filterChildren() const; void setFilterChildren(bool); + QStringList keys() const; + void setKeys(const QStringList &keys); + + void emitDragged(QSGMouseEvent *event) { emit dragged(event); } + void emitDropped(QSGItem *dropItem) { emit dropped(dropItem); } + void emitCanceled() { emit canceled(); } + Q_SIGNALS: void targetChanged(); + void dropItemChanged(); + void dataChanged(); void axisChanged(); void minimumXChanged(); void maximumXChanged(); @@ -102,9 +125,17 @@ Q_SIGNALS: void maximumYChanged(); void activeChanged(); void filterChildrenChanged(); + void keysChanged(); + void dragged(QSGMouseEvent *mouse); + void dropped(QSGItem *dropItem); + void canceled(); private: + QStringList _keys; + QVariant _data; QSGItem *_target; + QSGItem *_dropItem; + QSGItem *_grabItem; Axis _axis; qreal _xmin; qreal _xmax; @@ -115,7 +146,6 @@ private: Q_DISABLE_COPY(QSGDrag) }; -class QSGMouseEvent; class QSGMouseAreaPrivate; class Q_AUTOTEST_EXPORT QSGMouseArea : public QSGItem { diff --git a/src/declarative/items/qsgmousearea_p_p.h b/src/declarative/items/qsgmousearea_p_p.h index e736c059a2..11f7089503 100644 --- a/src/declarative/items/qsgmousearea_p_p.h +++ b/src/declarative/items/qsgmousearea_p_p.h @@ -96,6 +96,7 @@ public: bool stealMouse : 1; bool doubleClick : 1; bool preventStealing : 1; + bool dragRejected : 1; QSGDrag *drag; QPointF startScene; qreal startX; From a22053ad9ee145bb312beaa673c9d1efa9ccf2a3 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 9 Jun 2011 19:00:33 +1000 Subject: [PATCH 16/48] Do delegate management outside the render thread Very simple implementation, there's probably a better way to do it. --- .../particles/qsgmodelparticle.cpp | 43 +++++++++++++------ .../particles/qsgmodelparticle_p.h | 4 +- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/declarative/particles/qsgmodelparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp index 94ce082c9d..704b9a298c 100644 --- a/src/declarative/particles/qsgmodelparticle.cpp +++ b/src/declarative/particles/qsgmodelparticle.cpp @@ -42,6 +42,7 @@ #include "qsgmodelparticle_p.h" #include #include +#include #include QT_BEGIN_NAMESPACE @@ -50,6 +51,12 @@ QSGModelParticle::QSGModelParticle(QSGItem *parent) : QSGParticlePainter(parent), m_ownModel(false), m_comp(0), m_model(0), m_fade(true), m_modelCount(0) { setFlag(QSGItem::ItemHasContents); + QTimer* manageDelegates = new QTimer(this);//TODO: don't leak + connect(manageDelegates, SIGNAL(timeout()), + this, SLOT(processPending())); + manageDelegates->setInterval(16); + manageDelegates->setSingleShot(false); + manageDelegates->start(); } QVariant QSGModelParticle::model() const @@ -153,27 +160,38 @@ void QSGModelParticle::load(QSGParticleData* d) if(m_stasis.contains(m_items[pos])) qWarning() << "Current model particles prefers overwrite:false"; //remove old item from the particle that is dying to make room for this one - m_items[pos]->setOpacity(0.); + m_deletables << m_items[pos]; m_available << m_idx[pos]; - m_model->release(m_items[pos]); m_idx[pos] = -1; m_items[pos] = 0; m_data[pos] = 0; m_activeCount--; } - m_items[pos] = m_model->item(m_available.first()); - m_idx[pos] = m_available.first(); - m_available.pop_front(); - QSGModelParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); - if(mpa){ - mpa->m_mp = this; - mpa->attach(); - } - m_items[pos]->setParentItem(this); + m_requests << pos; m_data[pos] = d; m_activeCount++; } +void QSGModelParticle::processPending() +{//can't create/delete arbitrary items in the render thread + foreach(QSGItem* item, m_deletables){ + item->setOpacity(0.); + m_model->release(item); + } + + foreach(int pos, m_requests){ + m_items[pos] = m_model->item(m_available.first()); + m_idx[pos] = m_available.first(); + m_available.pop_front(); + QSGModelParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); + if(mpa){ + mpa->m_mp = this; + mpa->attach(); + } + m_items[pos]->setParentItem(this); + } +} + void QSGModelParticle::reload(QSGParticleData* d) { //No-op unless we start copying the data. @@ -242,9 +260,8 @@ void QSGModelParticle::prepareNextFrame() continue; } if(t >= 1.0){//Usually happens from load - item->setOpacity(0.); m_available << m_idx[i]; - m_model->release(m_items[i]); + m_deletables << item; m_idx[i] = -1; m_items[i] = 0; m_data[i] = 0; diff --git a/src/declarative/particles/qsgmodelparticle_p.h b/src/declarative/particles/qsgmodelparticle_p.h index 4dd8bfa710..04533a73ce 100644 --- a/src/declarative/particles/qsgmodelparticle_p.h +++ b/src/declarative/particles/qsgmodelparticle_p.h @@ -96,12 +96,14 @@ protected: void prepareNextFrame(); private slots: void updateCount(); + void processPending(); private: bool m_ownModel; QDeclarativeComponent* m_comp; QSGVisualDataModel *m_model; QVariant m_dataSource; - QList > m_deletables; + QList m_deletables; + QList< int > m_requests; int m_particleCount; bool m_fade; From 281d15b90c95c64dca5ce674b41045efc4419b95 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 10 Jun 2011 12:29:04 +1000 Subject: [PATCH 17/48] Create property cache in case of Component{} root Change-Id: I36f969d09d8ce5e0b68b657b514586990e88ebe4 Task-number: QTBUG-19354 (cherry picked from commit af1350249ea9de68e1c4b72ed1179948aa28ca19) --- src/declarative/qml/qdeclarativecompiler.cpp | 6 ++++++ .../qdeclarativelanguage/data/NestedComponentRoot.qml | 6 ++++++ .../qdeclarativelanguage/data/nestedComponentRoots.qml | 4 ++++ .../qdeclarativelanguage/tst_qdeclarativelanguage.cpp | 7 +++++++ 4 files changed, 23 insertions(+) create mode 100644 tests/auto/declarative/qdeclarativelanguage/data/NestedComponentRoot.qml create mode 100644 tests/auto/declarative/qdeclarativelanguage/data/nestedComponentRoots.qml diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index 72010c0ef2..43bb58c88a 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -650,6 +650,7 @@ bool QDeclarativeCompiler::compile(QDeclarativeEngine *engine, out->dumpInstructions(); if (compilerStatDump()) dumpStats(); + Q_ASSERT(out->rootPropertyCache); } else { reset(out); } @@ -1230,6 +1231,11 @@ void QDeclarativeCompiler::genComponent(QDeclarativeParser::Object *obj) id.setId.index = obj->idIndex; output->addInstruction(id); } + + if (obj == unitRoot) { + output->rootPropertyCache = output->types[obj->type].createPropertyCache(engine); + output->rootPropertyCache->addref(); + } } bool QDeclarativeCompiler::buildComponent(QDeclarativeParser::Object *obj, diff --git a/tests/auto/declarative/qdeclarativelanguage/data/NestedComponentRoot.qml b/tests/auto/declarative/qdeclarativelanguage/data/NestedComponentRoot.qml new file mode 100644 index 0000000000..785a27dd79 --- /dev/null +++ b/tests/auto/declarative/qdeclarativelanguage/data/NestedComponentRoot.qml @@ -0,0 +1,6 @@ +import QtQuick 1.0 + +Component { + Item { + } +} diff --git a/tests/auto/declarative/qdeclarativelanguage/data/nestedComponentRoots.qml b/tests/auto/declarative/qdeclarativelanguage/data/nestedComponentRoots.qml new file mode 100644 index 0000000000..361bcbcb56 --- /dev/null +++ b/tests/auto/declarative/qdeclarativelanguage/data/nestedComponentRoots.qml @@ -0,0 +1,4 @@ +import QtQuick 1.0 + +NestedComponentRoot { +} diff --git a/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp b/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp index 132c947d93..3c6ce9da6d 100644 --- a/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp +++ b/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp @@ -135,6 +135,7 @@ private slots: void reservedWords_data(); void reservedWords(); void inlineAssignmentsOverrideBindings(); + void nestedComponentRoots(); void basicRemote_data(); void basicRemote(); @@ -1428,6 +1429,12 @@ void tst_qdeclarativelanguage::inlineAssignmentsOverrideBindings() delete o; } +// QTBUG-19354 +void tst_qdeclarativelanguage::nestedComponentRoots() +{ + QDeclarativeComponent component(&engine, TEST_FILE("nestedComponentRoots.qml")); +} + // Import tests (QT-558) void tst_qdeclarativelanguage::importsBuiltin_data() { From 77dfbea79094be6b763319fcca03f6b48ab8248e Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 10 Jun 2011 13:46:44 +1000 Subject: [PATCH 18/48] Add JavaScript benchmark --- tests/benchmarks/declarative/declarative.pro | 1 + .../javascript/data/NestedIdObject.qml | 9 ++ .../javascript/data/intQObjectProperty.qml | 13 ++ .../declarative/javascript/data/localId.qml | 13 ++ .../declarative/javascript/data/nestedId.qml | 13 ++ .../javascript/data/stringQObjectProperty.qml | 14 ++ .../declarative/javascript/javascript.pro | 11 ++ .../declarative/javascript/testtypes.cpp | 48 +++++++ .../declarative/javascript/testtypes.h | 65 +++++++++ .../declarative/javascript/tst_javascript.cpp | 123 ++++++++++++++++++ 10 files changed, 310 insertions(+) create mode 100644 tests/benchmarks/declarative/javascript/data/NestedIdObject.qml create mode 100644 tests/benchmarks/declarative/javascript/data/intQObjectProperty.qml create mode 100644 tests/benchmarks/declarative/javascript/data/localId.qml create mode 100644 tests/benchmarks/declarative/javascript/data/nestedId.qml create mode 100644 tests/benchmarks/declarative/javascript/data/stringQObjectProperty.qml create mode 100644 tests/benchmarks/declarative/javascript/javascript.pro create mode 100644 tests/benchmarks/declarative/javascript/testtypes.cpp create mode 100644 tests/benchmarks/declarative/javascript/testtypes.h create mode 100644 tests/benchmarks/declarative/javascript/tst_javascript.cpp diff --git a/tests/benchmarks/declarative/declarative.pro b/tests/benchmarks/declarative/declarative.pro index a827978d63..f2dfdf971a 100644 --- a/tests/benchmarks/declarative/declarative.pro +++ b/tests/benchmarks/declarative/declarative.pro @@ -3,6 +3,7 @@ TEMPLATE = subdirs SUBDIRS += \ binding \ creation \ + javascript \ holistic \ pointers \ qdeclarativecomponent \ diff --git a/tests/benchmarks/declarative/javascript/data/NestedIdObject.qml b/tests/benchmarks/declarative/javascript/data/NestedIdObject.qml new file mode 100644 index 0000000000..410ee00ddc --- /dev/null +++ b/tests/benchmarks/declarative/javascript/data/NestedIdObject.qml @@ -0,0 +1,9 @@ +import QtQuick 1.0 + +QtObject { + function runtest() { + for (var ii = 0; ii < 5000000; ++ii) { + root + } + } +} diff --git a/tests/benchmarks/declarative/javascript/data/intQObjectProperty.qml b/tests/benchmarks/declarative/javascript/data/intQObjectProperty.qml new file mode 100644 index 0000000000..c3e6ebc16a --- /dev/null +++ b/tests/benchmarks/declarative/javascript/data/intQObjectProperty.qml @@ -0,0 +1,13 @@ +import Qt.test 1.0 + +TestObject { + id: root + + function runtest() { + var r = root; + + for (var ii = 0; ii < 5000000; ++ii) { + r.intValue + } + } +} diff --git a/tests/benchmarks/declarative/javascript/data/localId.qml b/tests/benchmarks/declarative/javascript/data/localId.qml new file mode 100644 index 0000000000..474d0760a3 --- /dev/null +++ b/tests/benchmarks/declarative/javascript/data/localId.qml @@ -0,0 +1,13 @@ +// Benchmarks the cost of accessing an id in the same file as the script. + +import QtQuick 1.0 + +QtObject { + id: root + + function runtest() { + for (var ii = 0; ii < 5000000; ++ii) { + root + } + } +} diff --git a/tests/benchmarks/declarative/javascript/data/nestedId.qml b/tests/benchmarks/declarative/javascript/data/nestedId.qml new file mode 100644 index 0000000000..49c4e3ca43 --- /dev/null +++ b/tests/benchmarks/declarative/javascript/data/nestedId.qml @@ -0,0 +1,13 @@ +// Benchmarks the cost of accessing an id in a parent context of the script. + +import QtQuick 1.0 + +QtObject { + id: root + + property variant object: NestedIdObject {} + function runtest() { + object.runtest(); + } +} + diff --git a/tests/benchmarks/declarative/javascript/data/stringQObjectProperty.qml b/tests/benchmarks/declarative/javascript/data/stringQObjectProperty.qml new file mode 100644 index 0000000000..ccd8a791b6 --- /dev/null +++ b/tests/benchmarks/declarative/javascript/data/stringQObjectProperty.qml @@ -0,0 +1,14 @@ +import Qt.test 1.0 + +TestObject { + id: root + + function runtest() { + var r = root; + + for (var ii = 0; ii < 5000000; ++ii) { + r.stringValue + } + } +} + diff --git a/tests/benchmarks/declarative/javascript/javascript.pro b/tests/benchmarks/declarative/javascript/javascript.pro new file mode 100644 index 0000000000..7395cef07e --- /dev/null +++ b/tests/benchmarks/declarative/javascript/javascript.pro @@ -0,0 +1,11 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_javascript +QT += declarative +macx:CONFIG -= app_bundle + +SOURCES += tst_javascript.cpp testtypes.cpp +HEADERS += testtypes.h + +# Define SRCDIR equal to test's source directory +DEFINES += SRCDIR=\\\"$$PWD\\\" diff --git a/tests/benchmarks/declarative/javascript/testtypes.cpp b/tests/benchmarks/declarative/javascript/testtypes.cpp new file mode 100644 index 0000000000..f490b770fd --- /dev/null +++ b/tests/benchmarks/declarative/javascript/testtypes.cpp @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "testtypes.h" +#include + +void registerTypes() +{ + qmlRegisterType("Qt.test", 1,0, "TestObject"); +} diff --git a/tests/benchmarks/declarative/javascript/testtypes.h b/tests/benchmarks/declarative/javascript/testtypes.h new file mode 100644 index 0000000000..c89f10757b --- /dev/null +++ b/tests/benchmarks/declarative/javascript/testtypes.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTTYPES_H +#define TESTTYPES_H + +#include + +class TestObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int intValue READ intValue); + Q_PROPERTY(QString stringValue READ stringValue); + +public: + TestObject() : m_string("Hello world!") {} + + int intValue() const { return 13; } + QString stringValue() const { return m_string; } + +private: + QString m_string; +}; + +void registerTypes(); + +#endif // TESTTYPES_H diff --git a/tests/benchmarks/declarative/javascript/tst_javascript.cpp b/tests/benchmarks/declarative/javascript/tst_javascript.cpp new file mode 100644 index 0000000000..73c1d7c7ad --- /dev/null +++ b/tests/benchmarks/declarative/javascript/tst_javascript.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "testtypes.h" + +class tst_javascript : public QObject +{ + Q_OBJECT + +public: + tst_javascript(); + virtual ~tst_javascript(); + +private slots: + void run_data(); + void run(); + +private: + QDeclarativeEngine engine; +}; + +tst_javascript::tst_javascript() +{ + registerTypes(); +} + +tst_javascript::~tst_javascript() +{ +} + +void tst_javascript::run_data() +{ + QTest::addColumn("file"); + + QDir dir(SRCDIR "/data"); + + QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); + + for (int ii = 0; ii < files.count(); ++ii) { + QString file = files.at(ii); + if (file.endsWith(".qml") && file.at(0).isLower()) { + + QString testName = file.left(file.length() - 4 /* strlen(".qml") */); + QString fileName = QLatin1String(SRCDIR) + QLatin1String("/data/") + file; + + + QTest::newRow(qPrintable(testName)) << fileName; + + } + } +} + +void tst_javascript::run() +{ + QFETCH(QString, file); + + QDeclarativeComponent c(&engine, file); + + if (c.isError()) { + qWarning() << c.errors(); + } + + QVERIFY(!c.isError()); + + QObject *o = c.create(); + QVERIFY(o != 0); + + QMetaMethod method = o->metaObject()->method(o->metaObject()->indexOfMethod("runtest()")); + + QBENCHMARK { + method.invoke(o); + } + + delete o; +} + +QTEST_MAIN(tst_javascript) + +#include "tst_javascript.moc" From 7b28a7eebeb8cbe5d356fc9b24651a36d73ab1ad Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Fri, 10 Jun 2011 16:07:33 +1000 Subject: [PATCH 19/48] Add delegate property to ItemParticle Also add burst(n,x,y) to ParticleEmitter, and a demo that uses both. --- .../particles/custom/blurparticles.qml | 40 +++++++ .../particles/custom/content/particle.png | Bin 0 -> 861 bytes .../particles/custom/fireworks.qml | 101 ++++++++++++++++++ src/declarative/particles/qsgitemparticle.cpp | 78 +++++++++----- src/declarative/particles/qsgitemparticle_p.h | 23 +++- .../particles/qsgmodelparticle.cpp | 2 + .../particles/qsgparticleemitter.cpp | 7 ++ .../particles/qsgparticleemitter_p.h | 1 + 8 files changed, 224 insertions(+), 28 deletions(-) create mode 100644 examples/declarative/particles/custom/content/particle.png create mode 100644 examples/declarative/particles/custom/fireworks.qml diff --git a/examples/declarative/particles/custom/blurparticles.qml b/examples/declarative/particles/custom/blurparticles.qml index 8a3e9ad803..15da9ba9db 100644 --- a/examples/declarative/particles/custom/blurparticles.qml +++ b/examples/declarative/particles/custom/blurparticles.qml @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + import QtQuick 2.0 import QtQuick.Particles 2.0 diff --git a/examples/declarative/particles/custom/content/particle.png b/examples/declarative/particles/custom/content/particle.png new file mode 100644 index 0000000000000000000000000000000000000000..5c83896d22cdc3c352ff8db97b0b1f2cd2b27125 GIT binary patch literal 861 zcmV-j1ETziP)+uAJ)4!|9fhtYq5Mes-Q6Cx`#|iAbtLlW1enS0*|y zF-AG6Gn?YQHEL+EwV9p=GaO)E)yZ7P#iXw|#tVf#fLG>e0M8MG5RhWevjLdwSw8|D zuhsAfhEd!df^ArA-nL0W%try}N6#(n2}a&~HjZ3K1|mYlq5;_jfW`*`(KwJuF^0aF zr|o?q9tcN*0bGIM4C4r3BJqHu(7x$~T@X|zFe7+8*#8m!?`2AGwkkTtGl1Oy=)z}^ za?OjA&6)WL6h>fq1P)}{8Ug2Y{)hlFC`Rwu^8|a|n->6P9tZ))^CJcdb95FnFqIco-dA~TBvoZEtK3xiQ00Nnht$!z>dj77iw^4%*jqDp zZD1CAt~vjv$|vGRaP<8{tX^>mj-pqPqBoYlt$tiP2UO4awgu8L>g9a^HftXLG|wl< z_dubRj^6csCNE_t0!@Bc=vDb2Gx}h141K;yv1t6N z@~vY~yv9W(!@+w;iAO4L4;GiR<^30@7ZB=z zn!ZBk!aAnfmthA}fMheB+Tv7T@{3nr@9t&K{~=@$rCk7(@3;zVH! zvloHbj!dUC%TCG`;L#&_FJ$XtpU+m~XD1CuI zc@2l1<)Sa-o^Ue7qb+KR6O3Nz=1lO&iqE=DX=|0Rn@J7;KJ%G;oE1#M-Frdf!wD`X z?0tf5l1|YPARrR`(9I|SiAL&X!V~vHCYyK{k|O{Dgl?cqi8vhSmEQ8??O1QAM}TM# nN5v_UffYd2w;a8m{}o^W-^qmk*A=;y00000NkvXXu0mjf5*>;S literal 0 HcmV?d00001 diff --git a/examples/declarative/particles/custom/fireworks.qml b/examples/declarative/particles/custom/fireworks.qml new file mode 100644 index 0000000000..edd4accfdf --- /dev/null +++ b/examples/declarative/particles/custom/fireworks.qml @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Particles 2.0 + +Rectangle{ + width: 360 + height: 600 + color: "black" + ParticleSystem{ + id: otherSys + anchors.fill: parent + Emitter{ + id: emitter + emitting: false + emitRate: 100 + lifeSpan: 1000 + emitCap: 1000 + speed: AngledDirection{angleVariation:180; magnitudeVariation: 60} + } + + ImageParticle{ + source: "content/particle.png" + } + } + Component{ + id: firework + Item{ + id: container + width: 48 + height: 48 + Image{ + width: 48 + height: 48 + id: img + source: "content/particle.png" + } + Timer{ + interval: 1000 + 4000*Math.random() + running: true + repeat: false + onTriggered: { + img.visible = false; + emitter.burst(100, container.x+24, container.y+24); + } + } + } + } + ParticleSystem{ + anchors.fill: parent + Emitter{ + width: parent.width + y: parent.height + emitRate: 2 + lifeSpan: 6000 + speed: PointDirection{y:-100} + } + ItemParticle{ + delegate: firework + } + } +} + diff --git a/src/declarative/particles/qsgitemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp index 819c823155..498dd90a87 100644 --- a/src/declarative/particles/qsgitemparticle.cpp +++ b/src/declarative/particles/qsgitemparticle.cpp @@ -42,14 +42,22 @@ #include "qsgitemparticle_p.h" #include #include +#include +#include #include QT_BEGIN_NAMESPACE QSGItemParticle::QSGItemParticle(QSGItem *parent) : - QSGParticlePainter(parent), m_fade(true) + QSGParticlePainter(parent), m_fade(true), m_delegate(0) { setFlag(QSGItem::ItemHasContents); + QTimer* manageDelegates = new QTimer(this);//TODO: don't leak + connect(manageDelegates, SIGNAL(timeout()), + this, SLOT(tick())); + manageDelegates->setInterval(16); + manageDelegates->setSingleShot(false); + manageDelegates->start(); } @@ -79,33 +87,54 @@ void QSGItemParticle::give(QSGItem *item) void QSGItemParticle::load(QSGParticleData* d) { - if(m_pendingItems.isEmpty()) - return; int pos = particleTypeIndex(d); - if(m_items[pos]){ + m_data[pos] = d; + m_loadables << pos; +} + +void QSGItemParticle::tick() +{ + foreach(QSGItem* item, m_deletables){ + if(m_fade) + item->setOpacity(0.); + QSGItemParticleAttached* mpa; + if((mpa = qobject_cast(qmlAttachedPropertiesObject(item)))) + mpa->detach();//reparent as well? + //TODO: Delete iff we created it + m_activeCount--; + m_deletables.removeAll(item); + } + + foreach(int pos, m_loadables){ if(m_stasis.contains(m_items[pos])) qWarning() << "Current model particles prefers overwrite:false"; //remove old item from the particle that is dying to make room for this one - m_items[pos]->setOpacity(0.); - QSGItemParticleAttached* mpa; - if((mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos], false)))) - mpa->detach();//reparent as well? + if(m_items[pos]){ + m_deletables << m_items[pos]; + m_activeCount--; + } m_items[pos] = 0; - m_data[pos] = 0; - m_activeCount--; + if(!m_pendingItems.isEmpty()){ + m_items[pos] = m_pendingItems.front(); + m_pendingItems.pop_front(); + }else if(m_delegate){ + m_items[pos] = qobject_cast(m_delegate->create(qmlContext(this))); + } + if(m_items[pos]){ + m_items[pos]->setX(m_data[pos]->curX() - m_items[pos]->width()/2);//TODO: adjust for system? + m_items[pos]->setY(m_data[pos]->curY() - m_items[pos]->height()/2); + QSGItemParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); + if(mpa){ + mpa->m_mp = this; + mpa->attach(); + } + m_items[pos]->setParentItem(this); + if(m_fade) + m_items[pos]->setOpacity(0.); + m_activeCount++; + } + m_loadables.removeAll(pos); } - m_items[pos] = m_pendingItems.front(); - m_pendingItems.pop_front(); - m_items[pos]->setX(d->curX() - m_items[pos]->width()/2); - m_items[pos]->setY(d->curY() - m_items[pos]->height()/2); - QSGItemParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); - if(mpa){ - mpa->m_mp = this; - mpa->attach(); - } - m_items[pos]->setParentItem(this); - m_data[pos] = d; - m_activeCount++; } void QSGItemParticle::reload(QSGParticleData* d) @@ -173,10 +202,7 @@ void QSGItemParticle::prepareNextFrame() continue; } if(t >= 1.0){//Usually happens from load - item->setOpacity(0.); - QSGItemParticleAttached* mpa; - if((mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[i])))) - mpa->detach();//reparent as well? + m_deletables << item; m_items[i] = 0; m_data[i] = 0; m_activeCount--; diff --git a/src/declarative/particles/qsgitemparticle_p.h b/src/declarative/particles/qsgitemparticle_p.h index fa3516b631..3b7db519de 100644 --- a/src/declarative/particles/qsgitemparticle_p.h +++ b/src/declarative/particles/qsgitemparticle_p.h @@ -55,8 +55,8 @@ class QSGItemParticleAttached; class QSGItemParticle : public QSGParticlePainter { Q_OBJECT - Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged) + Q_PROPERTY(QDeclarativeComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) public: explicit QSGItemParticle(QSGItem *parent = 0); @@ -69,9 +69,16 @@ public: virtual int count(); static QSGItemParticleAttached *qmlAttachedProperties(QObject *object); + QDeclarativeComponent* delegate() const + { + return m_delegate; + } + signals: void fadeChanged(); + void delegateChanged(QDeclarativeComponent* arg); + public slots: //TODO: Add a follow mode, where moving the delegate causes the logical particle to go with it? void freeze(QSGItem* item); @@ -80,11 +87,22 @@ public slots: void give(QSGItem* item);//give from modelparticle void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();} + void setDelegate(QDeclarativeComponent* arg) + { + if (m_delegate != arg) { + m_delegate = arg; + emit delegateChanged(arg); + } + } + protected: virtual void reset(); void prepareNextFrame(); +private slots: + void tick(); private: - QList > m_deletables; + QList m_deletables; + QList< int > m_loadables; int m_particleCount; bool m_fade; @@ -96,6 +114,7 @@ private: QSet m_stasis; qreal m_lastT; int m_activeCount; + QDeclarativeComponent* m_delegate; }; class QSGItemParticleAttached : public QObject diff --git a/src/declarative/particles/qsgmodelparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp index 704b9a298c..b0b4fa4ad2 100644 --- a/src/declarative/particles/qsgmodelparticle.cpp +++ b/src/declarative/particles/qsgmodelparticle.cpp @@ -177,6 +177,7 @@ void QSGModelParticle::processPending() foreach(QSGItem* item, m_deletables){ item->setOpacity(0.); m_model->release(item); + m_deletables.removeAll(item); } foreach(int pos, m_requests){ @@ -189,6 +190,7 @@ void QSGModelParticle::processPending() mpa->attach(); } m_items[pos]->setParentItem(this); + m_requests.removeAll(pos); } } diff --git a/src/declarative/particles/qsgparticleemitter.cpp b/src/declarative/particles/qsgparticleemitter.cpp index 20f3c6bb4b..143338f798 100644 --- a/src/declarative/particles/qsgparticleemitter.cpp +++ b/src/declarative/particles/qsgparticleemitter.cpp @@ -121,6 +121,13 @@ void QSGParticleEmitter::burst(int num) m_burstQueue << qMakePair(num, QPointF(x(), y())); } +void QSGParticleEmitter::burst(int num, qreal x, qreal y) +{ + if(!particleCount()) + qWarning() << "burst called on an emitter with a particle count of zero"; + m_burstQueue << qMakePair(num, QPointF(x, y)); +} + void QSGParticleEmitter::setMaxParticleCount(int arg) { if (m_maxParticleCount != arg) { diff --git a/src/declarative/particles/qsgparticleemitter_p.h b/src/declarative/particles/qsgparticleemitter_p.h index 9fafd9d43d..65aca0c83e 100644 --- a/src/declarative/particles/qsgparticleemitter_p.h +++ b/src/declarative/particles/qsgparticleemitter_p.h @@ -142,6 +142,7 @@ signals: public slots: void pulse(qreal seconds); void burst(int num); + void burst(int num, qreal x, qreal y); void setEmitting(bool arg); From 53bbcf5038a9ec5c5381e25968cb51b76cf4a2d6 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Fri, 10 Jun 2011 13:40:48 +0200 Subject: [PATCH 20/48] Disable vsync animations by default Change-Id: Ia614915ddb96f5c51e9883885479f1269ab361ed --- src/declarative/items/qsgcanvas.cpp | 72 +++++++++++++++++++++++------ src/declarative/items/qsgcanvas.h | 3 ++ src/declarative/items/qsgcanvas_p.h | 2 + tools/qmlscene/main.cpp | 6 +++ 4 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp index 3a88fbb790..950797a27e 100644 --- a/src/declarative/items/qsgcanvas.cpp +++ b/src/declarative/items/qsgcanvas.cpp @@ -174,7 +174,7 @@ void QSGCanvas::paintEvent(QPaintEvent *) int lastFrame = frameTimer.restart(); #endif - if (d->animationDriver->isRunning()) + if (d->animationDriver && d->animationDriver->isRunning()) d->animationDriver->advance(); #ifdef FRAME_TIMING @@ -222,7 +222,7 @@ void QSGCanvas::paintEvent(QPaintEvent *) QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Painting); - if (d->animationDriver->isRunning()) + if (d->animationDriver && d->animationDriver->isRunning()) update(); } else { if (isUpdatesEnabled()) { @@ -252,12 +252,14 @@ void QSGCanvas::showEvent(QShowEvent *e) if (!d->contextFailed) { if (d->threadedRendering) { - if (!d->animationDriver) { - d->animationDriver = d->context->createAnimationDriver(this); - connect(d->animationDriver, SIGNAL(started()), d->thread, SLOT(animationStarted()), Qt::DirectConnection); - connect(d->animationDriver, SIGNAL(stopped()), d->thread, SLOT(animationStopped()), Qt::DirectConnection); + if (d->vsyncAnimations) { + if (!d->animationDriver) { + d->animationDriver = d->context->createAnimationDriver(this); + connect(d->animationDriver, SIGNAL(started()), d->thread, SLOT(animationStarted()), Qt::DirectConnection); + connect(d->animationDriver, SIGNAL(stopped()), d->thread, SLOT(animationStopped()), Qt::DirectConnection); + } + d->animationDriver->install(); } - d->animationDriver->install(); d->thread->startRenderThread(); setUpdatesEnabled(true); } else { @@ -265,11 +267,14 @@ void QSGCanvas::showEvent(QShowEvent *e) if (!d->context || !d->context->isReady()) { d->initializeSceneGraph(); - d->animationDriver = d->context->createAnimationDriver(this); - connect(d->animationDriver, SIGNAL(started()), this, SLOT(update())); + if (d->vsyncAnimations) { + d->animationDriver = d->context->createAnimationDriver(this); + connect(d->animationDriver, SIGNAL(started()), this, SLOT(update())); + } } - d->animationDriver->install(); + if (d->animationDriver) + d->animationDriver->install(); } } } @@ -283,12 +288,52 @@ void QSGCanvas::hideEvent(QHideEvent *e) d->thread->stopRenderThread(); } - d->animationDriver->uninstall(); + if (d->animationDriver) + d->animationDriver->uninstall(); } QGLWidget::hideEvent(e); } + + +/*! + Sets weither this canvas should use vsync driven animations. + + This option can only be set on one single QSGCanvas, and that it's + vsync signal will then be used to drive all animations in the + process. + + This feature is primarily useful for single QSGCanvas, QML-only + applications. + + \warning Enabling vsync on multiple QSGCanvas instances has + undefined behavior. + */ +void QSGCanvas::setVSyncAnimations(bool enabled) +{ + Q_D(QSGCanvas); + if (isVisible()) { + qWarning("QSGCanvas::setVSyncAnimations: Cannot be changed when widget is shown"); + return; + } + d->vsyncAnimations = enabled; +} + + + +/*! + Returns true if this canvas should use vsync driven animations; + otherwise returns false. + */ +bool QSGCanvas::vsyncAnimations() const +{ + Q_D(const QSGCanvas); + return d->vsyncAnimations; +} + + + void QSGCanvas::focusOutEvent(QFocusEvent *event) { Q_D(QSGCanvas); @@ -384,6 +429,7 @@ QSGCanvasPrivate::QSGCanvasPrivate() , threadedRendering(false) , animationRunning(false) , renderThreadAwakened(false) + , vsyncAnimations(false) , thread(0) , animationDriver(0) { @@ -958,7 +1004,7 @@ bool QSGCanvas::event(QEvent *e) d->thread->syncAlreadyHappened = false; - if (d->animationRunning) { + if (d->animationRunning && d->animationDriver) { #ifdef THREAD_DEBUG qDebug("GUI: Advancing animations...\n"); #endif @@ -2053,7 +2099,7 @@ void QSGCanvasRenderThread::run() // but we don't want to lock an extra time. wake(); - if (!d->animationRunning && !isExternalUpdatePending) { + if (!d->animationRunning && !isExternalUpdatePending && !shouldExit) { #ifdef THREAD_DEBUG printf(" RenderThread: nothing to do, going to sleep...\n"); #endif diff --git a/src/declarative/items/qsgcanvas.h b/src/declarative/items/qsgcanvas.h index d0d0c79d5e..8913e41b86 100644 --- a/src/declarative/items/qsgcanvas.h +++ b/src/declarative/items/qsgcanvas.h @@ -75,6 +75,9 @@ public: QSGEngine *sceneGraphEngine() const; + void setVSyncAnimations(bool enabled); + bool vsyncAnimations() const; + QImage grabFrameBuffer(); Q_SIGNALS: diff --git a/src/declarative/items/qsgcanvas_p.h b/src/declarative/items/qsgcanvas_p.h index 9b2683cc38..7f7182ee52 100644 --- a/src/declarative/items/qsgcanvas_p.h +++ b/src/declarative/items/qsgcanvas_p.h @@ -160,6 +160,8 @@ public: uint animationRunning: 1; uint renderThreadAwakened : 1; + uint vsyncAnimations : 1; + QSGCanvasRenderThread *thread; QSize widgetSize; QSize viewportSize; diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index 765a9dc2fb..d351b27e7e 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -252,6 +252,7 @@ struct Options , scenegraphOnGraphicsview(false) , clip(false) , versionDetection(true) + , vsync(true) { } @@ -263,6 +264,7 @@ struct Options bool scenegraphOnGraphicsview; bool clip; bool versionDetection; + bool vsync; }; #if defined(QMLSCENE_BUNDLE) @@ -440,6 +442,7 @@ static void usage() qWarning(" --sg-on-gv [--clip] ....................... Scenegraph on graphicsview (and clip to item)"); #endif qWarning(" --no-version-detection .................... Do not try to detect the version of the .qml file"); + qWarning(" --no-vsync-animations ..................... Do not use vsync based animations"); qWarning(" "); exit(1); @@ -474,6 +477,8 @@ int main(int argc, char ** argv) options.versionDetection = false; else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("-i") && i + 1 < argc) imports.append(QString::fromLatin1(argv[++i])); + else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--no-vsync-animations")) + options.vsync = false; else if (QString::fromLatin1(argv[i]).toLower() == QLatin1String("--help") || QString::fromLatin1(argv[i]).toLower() == QLatin1String("-help") || QString::fromLatin1(argv[i]).toLower() == QLatin1String("--h") @@ -520,6 +525,7 @@ int main(int argc, char ** argv) if (options.versionDetection) checkAndAdaptVersion(options.file); QSGView *qxView = new MyQSGView(); + qxView->setVSyncAnimations(options.vsync); engine = qxView->engine(); for (int i = 0; i < imports.size(); ++i) engine->addImportPath(imports.at(i)); From e05d75b690fab95259afae8f295df8e9df242115 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 8 Jun 2011 11:01:26 +0200 Subject: [PATCH 21/48] QDeclarativeDebug: Fix QJSDebugService if launched with ',block' If the debugger is launched in blocking mode the service will be enabled from the start. Reviewed-by: Thorbjorn Lindeijer (cherry picked from commit c038e3505309bb954123493cb5f96c73e114f3d0) --- src/declarative/debugger/qjsdebuggeragent.cpp | 13 ++++++++++++- src/declarative/debugger/qjsdebuggeragent_p.h | 2 ++ src/declarative/debugger/qjsdebugservice.cpp | 10 ++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/declarative/debugger/qjsdebuggeragent.cpp b/src/declarative/debugger/qjsdebuggeragent.cpp index 9b76592c48..dff637b7da 100644 --- a/src/declarative/debugger/qjsdebuggeragent.cpp +++ b/src/declarative/debugger/qjsdebuggeragent.cpp @@ -56,7 +56,7 @@ class QJSDebuggerAgentPrivate { public: QJSDebuggerAgentPrivate(QJSDebuggerAgent *q) - : q(q), state(NoState) + : q(q), state(NoState), isInitialized(false) {} void continueExec(); @@ -79,6 +79,7 @@ public: QHash fileNameToBreakpoints; QStringList watchExpressions; QSet knownObjectIds; + bool isInitialized; }; namespace { @@ -252,6 +253,14 @@ QJSDebuggerAgent::~QJSDebuggerAgent() delete d; } +/*! + Indicates whether the agent got the list of breakpoints. + */ +bool QJSDebuggerAgent::isInitialized() const +{ + return d->isInitialized; +} + void QJSDebuggerAgent::setBreakpoints(const JSAgentBreakpoints &breakpoints) { d->breakpoints = breakpoints; @@ -259,6 +268,8 @@ void QJSDebuggerAgent::setBreakpoints(const JSAgentBreakpoints &breakpoints) d->fileNameToBreakpoints.clear(); foreach (const JSAgentBreakpointData &bp, breakpoints) d->fileNameToBreakpoints.insertMulti(fileName(QString::fromUtf8(bp.fileUrl)), bp); + + d->isInitialized = true; } void QJSDebuggerAgent::setWatchExpressions(const QStringList &watchExpressions) diff --git a/src/declarative/debugger/qjsdebuggeragent_p.h b/src/declarative/debugger/qjsdebuggeragent_p.h index 5aa3c9ccbc..309588eb2f 100644 --- a/src/declarative/debugger/qjsdebuggeragent_p.h +++ b/src/declarative/debugger/qjsdebuggeragent_p.h @@ -145,6 +145,8 @@ public: QJSDebuggerAgent(QDeclarativeEngine *engine, QObject *parent = 0); ~QJSDebuggerAgent(); + bool isInitialized() const; + void setBreakpoints(const JSAgentBreakpoints &); void setWatchExpressions(const QStringList &); diff --git a/src/declarative/debugger/qjsdebugservice.cpp b/src/declarative/debugger/qjsdebugservice.cpp index 4ce2c906bc..ad84f656f7 100644 --- a/src/declarative/debugger/qjsdebugservice.cpp +++ b/src/declarative/debugger/qjsdebugservice.cpp @@ -71,6 +71,16 @@ void QJSDebugService::addEngine(QDeclarativeEngine *engine) Q_ASSERT(!m_engines.contains(engine)); m_engines.append(engine); + + if (status() == Enabled && !m_engines.isEmpty() && !m_agent) { + m_agent = new QJSDebuggerAgent(engine, engine); + connect(m_agent, SIGNAL(stopped(bool,QString)), + this, SLOT(executionStopped(bool,QString))); + + while (!m_agent->isInitialized()) { + waitForMessage(); + } + } } void QJSDebugService::removeEngine(QDeclarativeEngine *engine) From 434df838c11063dfea95b1d26d0576ea661d10ba Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 8 Jun 2011 20:51:07 +0200 Subject: [PATCH 22/48] QDeclarativeDebug: Don't hang if started with ',block' argument Fixes regression introduced in a59261454071 Reviewed-by: Christiaan Janssen (cherry picked from commit e3b5de8651586cf5484fd8ab217cddf17f0fe01e) --- src/declarative/debugger/qpacketprotocol.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/declarative/debugger/qpacketprotocol.cpp b/src/declarative/debugger/qpacketprotocol.cpp index a40a9ab8a6..9caaa79011 100644 --- a/src/declarative/debugger/qpacketprotocol.cpp +++ b/src/declarative/debugger/qpacketprotocol.cpp @@ -198,6 +198,8 @@ public Q_SLOTS: packets.append(inProgress); inProgressSize = -1; inProgress.clear(); + + waitingForPacket = false; emit readyRead(); } else return; From e86bdcd3ce686eb14bb70e0b18b227f713a902b6 Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Tue, 14 Jun 2011 10:21:52 +1000 Subject: [PATCH 23/48] Fix failing test. Change ccf706d0bb2d9f70f5a8c18e4aab8ee7e6369817 changed the instruction dump() format. Change-Id: I86677cc176568728b8044d8abfce9121773e9c56 --- .../qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp b/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp index be1f9e05af..fda16409b7 100644 --- a/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp +++ b/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp @@ -535,7 +535,7 @@ void tst_qdeclarativeinstruction::dump() << "25\t\tSTORE_VARIANT_OBJECT\t22" << "26\t\tSTORE_INTERFACE\t\t23" << "27\t\tSTORE_SIGNAL\t\t2\t3\t\t\"console.log(1921)\"" - << "28\t\tSTORE_SCRIPT_STRING\t24\t3\t1" + << "28\t\tSTORE_SCRIPT_STRING\t24\t3\t1\t0" << "29\t\tASSIGN_SIGNAL_OBJECT\t0\t\t\t\"mySignal\"" << "30\t\tASSIGN_CUSTOMTYPE\t25\t6\t9" << "31\t\tSTORE_BINDING\t26\t3\t2" From 7b22f02961a97c0fa07127ea3ff26b8435a617b8 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Tue, 14 Jun 2011 14:09:30 +1000 Subject: [PATCH 24/48] Safer cleanup in model/item particle --- src/declarative/particles/qsgitemparticle.cpp | 4 ++-- .../particles/qsgmodelparticle.cpp | 22 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/declarative/particles/qsgitemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp index 498dd90a87..42f0062148 100644 --- a/src/declarative/particles/qsgitemparticle.cpp +++ b/src/declarative/particles/qsgitemparticle.cpp @@ -102,8 +102,8 @@ void QSGItemParticle::tick() mpa->detach();//reparent as well? //TODO: Delete iff we created it m_activeCount--; - m_deletables.removeAll(item); } + m_deletables.clear(); foreach(int pos, m_loadables){ if(m_stasis.contains(m_items[pos])) @@ -133,8 +133,8 @@ void QSGItemParticle::tick() m_items[pos]->setOpacity(0.); m_activeCount++; } - m_loadables.removeAll(pos); } + m_loadables.clear(); } void QSGItemParticle::reload(QSGParticleData* d) diff --git a/src/declarative/particles/qsgmodelparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp index b0b4fa4ad2..f87c0d31b9 100644 --- a/src/declarative/particles/qsgmodelparticle.cpp +++ b/src/declarative/particles/qsgmodelparticle.cpp @@ -177,21 +177,23 @@ void QSGModelParticle::processPending() foreach(QSGItem* item, m_deletables){ item->setOpacity(0.); m_model->release(item); - m_deletables.removeAll(item); } + m_deletables.clear(); foreach(int pos, m_requests){ - m_items[pos] = m_model->item(m_available.first()); - m_idx[pos] = m_available.first(); - m_available.pop_front(); - QSGModelParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); - if(mpa){ - mpa->m_mp = this; - mpa->attach(); + if(!m_available.isEmpty()){ + m_items[pos] = m_model->item(m_available.first()); + m_idx[pos] = m_available.first(); + m_available.pop_front(); + QSGModelParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); + if(mpa){ + mpa->m_mp = this; + mpa->attach(); + } + m_items[pos]->setParentItem(this); } - m_items[pos]->setParentItem(this); - m_requests.removeAll(pos); } + m_requests.clear(); } void QSGModelParticle::reload(QSGParticleData* d) From b556af1c73ea8eeed4aafe28fd28c2c1c6074a90 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 14 Jun 2011 09:49:58 +0200 Subject: [PATCH 25/48] Initialize member variable --- src/declarative/items/qsgshadereffectitem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/declarative/items/qsgshadereffectitem.cpp b/src/declarative/items/qsgshadereffectitem.cpp index 44e075c6f7..3fae0f15fd 100644 --- a/src/declarative/items/qsgshadereffectitem.cpp +++ b/src/declarative/items/qsgshadereffectitem.cpp @@ -540,6 +540,7 @@ QSGNode *QSGShaderEffectItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeD s.fragmentCode = qt_default_fragment_code; if (s.vertexCode.isEmpty()) s.vertexCode = qt_default_vertex_code; + s.className = metaObject()->className(); m_material.setProgramSource(s); node->markDirty(QSGNode::DirtyMaterial); From 129161d232e1d30cd248385eb6d91f5c92e3c246 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 15 Jun 2011 09:48:49 +0200 Subject: [PATCH 26/48] Disable attributes from shader effects Reviewed-by: Samuel --- src/declarative/items/qsgshadereffectnode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/declarative/items/qsgshadereffectnode.cpp b/src/declarative/items/qsgshadereffectnode.cpp index 74cd995d0d..5d3efb934c 100644 --- a/src/declarative/items/qsgshadereffectnode.cpp +++ b/src/declarative/items/qsgshadereffectnode.cpp @@ -84,6 +84,7 @@ QSGCustomMaterialShader::QSGCustomMaterialShader(const QSGShaderEffectMaterialKe void QSGCustomMaterialShader::deactivate() { + QSGMaterialShader::deactivate(); glDisable(GL_CULL_FACE); } From 4c0f5e2da92e861228abc89e4b9b116803a761ce Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 15 Jun 2011 10:08:43 +0200 Subject: [PATCH 27/48] Print out a warning when attributes are enabled --- .../scenegraph/coreapi/qsgrenderer.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/declarative/scenegraph/coreapi/qsgrenderer.cpp b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp index dbf704b64a..cc24cc066b 100644 --- a/src/declarative/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp @@ -229,6 +229,21 @@ void QSGRenderer::renderScene(const Bindable &bindable) int bindTime = frameTimer.elapsed(); #endif +#ifndef QT_NO_DEBUG + // Sanity check that attribute registers are disabled + { + GLint count; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &count); + GLint enabled; + for (int i=0; i Date: Wed, 15 Jun 2011 18:52:26 +1000 Subject: [PATCH 28/48] Rewrite particle system to cope with changing particle counts And might do it again... Caught up in this massive change were the following smaller ones: -Some custom particle examples -delegate property on ItemParticle and an example with it --- .../particles/custom/fireworks.qml | 37 +-- .../declarative/particles/custom/shader.qml | 84 ++++++ .../particles/trails/dynamicemitters.qml | 8 +- .../particles/qsgcustomparticle.cpp | 143 +++++----- .../particles/qsgcustomparticle_p.h | 10 +- .../particles/qsgfollowemitter.cpp | 12 +- .../particles/qsgimageparticle.cpp | 255 +++++++++++------- .../particles/qsgimageparticle_p.h | 13 +- src/declarative/particles/qsgitemparticle.cpp | 24 +- src/declarative/particles/qsgitemparticle_p.h | 4 +- .../particles/qsgmodelparticle.cpp | 18 +- .../particles/qsgmodelparticle_p.h | 4 +- .../particles/qsgparticleaffector.cpp | 36 +-- .../particles/qsgparticleaffector_p.h | 6 +- .../particles/qsgparticlepainter.cpp | 46 +++- .../particles/qsgparticlepainter_p.h | 45 +++- .../particles/qsgparticlesystem.cpp | 249 +++++++++-------- .../particles/qsgparticlesystem_p.h | 28 +- src/declarative/particles/qsgspritegoal.cpp | 6 +- src/declarative/particles/qsgturbulence.cpp | 50 ++-- 20 files changed, 662 insertions(+), 416 deletions(-) create mode 100644 examples/declarative/particles/custom/shader.qml diff --git a/examples/declarative/particles/custom/fireworks.qml b/examples/declarative/particles/custom/fireworks.qml index edd4accfdf..b73a5e234f 100644 --- a/examples/declarative/particles/custom/fireworks.qml +++ b/examples/declarative/particles/custom/fireworks.qml @@ -45,22 +45,6 @@ Rectangle{ width: 360 height: 600 color: "black" - ParticleSystem{ - id: otherSys - anchors.fill: parent - Emitter{ - id: emitter - emitting: false - emitRate: 100 - lifeSpan: 1000 - emitCap: 1000 - speed: AngledDirection{angleVariation:180; magnitudeVariation: 60} - } - - ImageParticle{ - source: "content/particle.png" - } - } Component{ id: firework Item{ @@ -79,14 +63,28 @@ Rectangle{ repeat: false onTriggered: { img.visible = false; - emitter.burst(100, container.x+24, container.y+24); + emitter.burst(100); } } + Emitter{ + anchors.centerIn: parent + id: emitter + system: syssy + particle: "works" + emitting: false + emitRate: 100 + lifeSpan: 1000 + //speed: AngledDirection{angle: 270; angleVariation:60; magnitudeVariation: 60; magnitude: 20} + speed: PointDirection{y:-60; yVariation: 80; xVariation: 80} + acceleration: PointDirection{y:100; yVariation: 20} + } } } ParticleSystem{ anchors.fill: parent + id: syssy Emitter{ + particle: "fire" width: parent.width y: parent.height emitRate: 2 @@ -94,8 +92,13 @@ Rectangle{ speed: PointDirection{y:-100} } ItemParticle{ + particles: ["fire"] delegate: firework } + ImageParticle{ + particles: ["works"] + source: "content/particle.png" + } } } diff --git a/examples/declarative/particles/custom/shader.qml b/examples/declarative/particles/custom/shader.qml new file mode 100644 index 0000000000..d83e7864c4 --- /dev/null +++ b/examples/declarative/particles/custom/shader.qml @@ -0,0 +1,84 @@ +import QtQuick 2.0 +import QtQuick.Particles 2.0 + +ParticleSystem{ + id: root + width: 1024 + height: 768 + Rectangle{ + z: -1 + anchors.fill: parent + color: "black" + Text{ + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 36 + color: "white" + text: "It's all in the fragment shader." + } + } + Emitter{ + emitRate: 400 + lifeSpan: 8000 + size: 24 + sizeVariation: 16 + speed: PointDirection{x: root.width/10; y: root.height/10;} + //acceleration: AngledDirection{angle:225; magnitude: root.width/36; angleVariation: 45; magnitudeVariation: 80} + acceleration: PointDirection{x: -root.width/40; y: -root.height/40; xVariation: -root.width/20; yVariation: -root.width/20} + } + CustomParticle{ + //TODO: Someway that you don't have to rewrite the basics for a simple addition + vertexShader:" + attribute highp vec2 vPos; + attribute highp vec2 vTex; + attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize + attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration + attribute highp float r; + + uniform highp mat4 qt_ModelViewProjectionMatrix; + uniform highp float timestamp; + uniform lowp float qt_Opacity; + + varying highp vec2 fTex; + varying lowp float fFade; + varying highp vec2 fPos; + + void main() { + fTex = vTex; + highp float size = vData.z; + highp float endSize = vData.w; + + highp float t = (timestamp - vData.x) / vData.y; + + highp float currentSize = mix(size, endSize, t * t); + + if (t < 0. || t > 1.) + currentSize = 0.; + + highp vec2 pos = vPos + - currentSize / 2. + currentSize * vTex // adjust size + + vVec.xy * t * vData.y // apply speed vector.. + + 0.5 * vVec.zw * pow(t * vData.y, 2.); + + gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); + + highp float fadeIn = min(t * 20., 1.); + highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.)); + + fFade = fadeIn * fadeOut * qt_Opacity; + fPos = vec2(pos.x/1024., pos.y/768.); + } + " + fragmentShader: " + varying highp vec2 fPos; + varying lowp float fFade; + varying highp vec2 fTex; + void main() {//*2 because this generates dark colors mostly + highp vec2 circlePos = fTex*2.0 - vec2(1.0,1.0); + highp float dist = length(circlePos); + highp float circleFactor = max(min(1.0 - dist, 1.0), 0.0); + gl_FragColor = vec4(fPos.x*2.0 - fPos.y, fPos.y*2.0 - fPos.x, fPos.x*fPos.y*2.0, 0.0) * circleFactor * fFade; + }" + + } +} diff --git a/examples/declarative/particles/trails/dynamicemitters.qml b/examples/declarative/particles/trails/dynamicemitters.qml index 588474fb43..f338c204db 100644 --- a/examples/declarative/particles/trails/dynamicemitters.qml +++ b/examples/declarative/particles/trails/dynamicemitters.qml @@ -81,7 +81,7 @@ Rectangle{ } system: sys emitting: true - emitRate: 64 + emitRate: 32 lifeSpan: 600 size: 24 endSize: 8 @@ -107,12 +107,12 @@ Rectangle{ MouseArea{ anchors.fill: parent onClicked:{ - for(var i=0; i<16; i++){ + for(var i=0; i<8; i++){ var obj = emitterComp.createObject(root); obj.x = mouse.x obj.y = mouse.y - obj.targetX = Math.random() * 640 - obj.targetY = Math.random() * 480 + obj.targetX = Math.random() * 240 - 120 + obj.x + obj.targetY = Math.random() * 240 - 120 + obj.y obj.life = Math.round(Math.random() * 2400) + 200 obj.go(); } diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp index 808ff1c427..f91307ed5c 100644 --- a/src/declarative/particles/qsgcustomparticle.cpp +++ b/src/declarative/particles/qsgcustomparticle.cpp @@ -44,16 +44,8 @@ #include QT_BEGIN_NAMESPACE -/* - "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" - "attribute highp vec4 qt_Vertex; \n" - "attribute highp vec2 qt_MultiTexCoord0; \n" - "varying highp vec2 qt_TexCoord0; \n" - "void main() { \n" - " qt_TexCoord0 = qt_MultiTexCoord0; \n" - " gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; \n" - "}"; -*/ + +//TODO: Can we make the code such that you don't have to copy the whole vertex shader just to add one little calculation? //Includes comments because the code isn't self explanatory static const char qt_particles_default_vertex_code[] = "attribute highp vec2 vPos; \n" @@ -86,23 +78,6 @@ static const char qt_particles_default_fragment_code[] =//TODO: Default frag req " gl_FragColor = texture2D(source, fTex) * qt_Opacity; \n" "}"; -/* -static const char qt_particles_default_vertex_code[] = - "attribute highp vec2 vPos; \n" - "attribute highp vec2 vTex; \n" - "uniform highp mat4 qt_ModelViewProjectionMatrix; \n" - "void main() { \n" - " highp float currentSize = 1000.0; \n" - " highp vec2 pos = vec2(100.0,100.0) \n" - " - currentSize / 2. + currentSize * vTex; // adjust size \n" - " gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); \n" - "}"; -static const char qt_particles_default_fragment_code[] =//TODO: Default frag requires source? - "void main() { \n" - " gl_FragColor = vec4(0,255,0,255); \n" - "}"; -*/ - static const char qt_position_attribute_name[] = "qt_Vertex"; static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0"; @@ -148,8 +123,36 @@ QSGCustomParticle::QSGCustomParticle(QSGItem* parent) : QSGParticlePainter(parent) , m_pleaseReset(true) , m_dirtyData(true) + , m_resizePending(false) { setFlag(QSGItem::ItemHasContents); + m_defaultVertices = new PlainVertices; + PlainVertex* vertices = (PlainVertex*) m_defaultVertices; + for (int i=0; i<4; ++i) { + vertices[i].x = 0; + vertices[i].y = 0; + vertices[i].t = -1; + vertices[i].lifeSpan = 0; + vertices[i].size = 0; + vertices[i].endSize = 0; + vertices[i].sx = 0; + vertices[i].sy = 0; + vertices[i].ax = 0; + vertices[i].ay = 0; + vertices[i].r = 0; + } + + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; } void QSGCustomParticle::componentComplete() @@ -199,10 +202,17 @@ void QSGCustomParticle::setVertexShader(const QByteArray &code) emit vertexShaderChanged(); } -void QSGCustomParticle::setCount(int c) +void QSGCustomParticle::resize(int oldCount, int newCount) { - QSGParticlePainter::setCount(c); - m_pleaseReset = true; + if(!m_node) + return; + if(!m_resizePending){ + m_pendingResizeVector.resize(oldCount); + PlainVertices *particles = (PlainVertices *) m_node->geometry()->vertexData(); + for(int i=0; iisRunning()) prepareNextFrame(); @@ -425,6 +437,32 @@ void QSGCustomParticle::prepareNextFrame(){ buildData(); } +void QSGCustomParticle::performPendingResize() +{ + m_resizePending = false; + if(!m_node) + return; + Q_ASSERT(m_pendingResizeVector.size() == m_count);//XXX + PlainVertices tmp[m_count];//###More vast memcpys that will decrease performance + for(int i=0; isetFlag(QSGNode::OwnsGeometry, false); + m_node->geometry()->allocate(m_count*4, m_count*6); + memcpy(m_node->geometry()->vertexData(), tmp, sizeof(PlainVertices) * m_count); + quint16 *indices = m_node->geometry()->indexDataAsUShort(); + for (int i=0; isetFlag(QSGNode::OwnsGeometry, true); +} + QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() { if (m_count * 4 > 0xffff) { @@ -437,6 +475,7 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() return 0; } + m_resizePending = false;//reset resizes as well. //Create Particle Geometry int vCount = m_count * 4; @@ -446,32 +485,9 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() PlainVertex *vertices = (PlainVertex *) g->vertexData(); for (int p=0; pindexDataAsUShort(); @@ -493,23 +509,6 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() node->setGeometry(g); node->setMaterial(&m_material); - /* - //For debugging, just use grid vertices like ShaderEffect - node->setGeometry(0); - QSGShaderEffectMesh* mesh = new QSGGridMesh(); - node->setFlag(QSGNode::OwnsGeometry, false); - - qDebug() << m_source.attributeNames; - QSGGeometry* geometry = node->geometry(); - geometry = mesh->updateGeometry(geometry, m_source.attributeNames, QRectF(0,0,width(),height())); - if(!geometry) - qDebug() << "Should have written the error handling"; - else - qDebug() << "Mesh Loaded"; - node->setGeometry(geometry); - qDebug() << QString("INIT") << geometry << (QObject*)node; - node->setFlag(QSGNode::OwnsGeometry, true); - */ QSGShaderEffectProgram s = m_source; if (s.fragmentCode.isEmpty()) s.fragmentCode = qt_particles_default_fragment_code; diff --git a/src/declarative/particles/qsgcustomparticle_p.h b/src/declarative/particles/qsgcustomparticle_p.h index 95144ef638..863da052f3 100644 --- a/src/declarative/particles/qsgcustomparticle_p.h +++ b/src/declarative/particles/qsgcustomparticle_p.h @@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QSGNode; - +struct PlainVertices; //Genealogy: Hybrid of UltraParticle and ShaderEffectItem class QSGCustomParticle : public QSGParticlePainter { @@ -64,7 +64,6 @@ public: explicit QSGCustomParticle(QSGItem* parent=0); virtual void load(QSGParticleData*); virtual void reload(QSGParticleData*); - virtual void setCount(int c); QByteArray fragmentShader() const { return m_source.fragmentCode; } void setFragmentShader(const QByteArray &code); @@ -84,14 +83,17 @@ protected: void disconnectPropertySignals(); void connectPropertySignals(); void reset(); + void resize(int oldCount, int newCount); void updateProperties(); void lookThroughShaderCode(const QByteArray &code); virtual void componentComplete(); QSGShaderEffectNode *buildCustomNode(); + void performPendingResize(); private: void buildData(); + bool m_pleaseReset; bool m_dirtyData; QSGShaderEffectProgram m_source; @@ -105,6 +107,10 @@ private: QSGShaderEffectMaterial m_material; QSGShaderEffectNode* m_node; qreal m_lastTime; + + bool m_resizePending; + QVector m_pendingResizeVector; + PlainVertices* m_defaultVertices; }; QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgfollowemitter.cpp b/src/declarative/particles/qsgfollowemitter.cpp index 442cff9ec8..28a082f776 100644 --- a/src/declarative/particles/qsgfollowemitter.cpp +++ b/src/declarative/particles/qsgfollowemitter.cpp @@ -54,6 +54,7 @@ QSGFollowEmitter::QSGFollowEmitter(QSGItem *parent) : , m_emissionExtruder(0) , m_defaultEmissionExtruder(new QSGParticleExtruder(this)) { + //TODO: If followed increased their size connect(this, SIGNAL(followChanged(QString)), this, SLOT(recalcParticlesPerSecond())); connect(this, SIGNAL(particleDurationChanged(int)), @@ -67,7 +68,7 @@ void QSGFollowEmitter::recalcParticlesPerSecond(){ return; m_followCount = m_system->m_groupData[m_system->m_groupIds[m_follow]]->size; if(!m_followCount){ - setParticlesPerSecond(1000);//XXX: Fix this horrendous hack, needed so they aren't turned off from start (causes crashes - test that when gone you don't crash with 0 PPPS) + setParticlesPerSecond(1);//XXX: Fix this horrendous hack, needed so they aren't turned off from start (causes crashes - test that when gone you don't crash with 0 PPPS) }else{ setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount); m_lastEmission.resize(m_followCount); @@ -111,16 +112,15 @@ void QSGFollowEmitter::emitWindow(int timeStamp) int gId = m_system->m_groupIds[m_follow]; int gId2 = m_system->m_groupIds[m_particle]; - for(int i=0; im_groupData[gId]->size; i++){ - pt = m_lastEmission[i]; - QSGParticleData* d = m_system->m_data[i + m_system->m_groupData[gId]->start]; + foreach(QSGParticleData *d, m_system->m_groupData[gId]->data){ if(!d || !d->stillAlive()) continue; + pt = m_lastEmission[d->index]; if(pt < d->pv.t) pt = d->pv.t; if(!effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){ - m_lastEmission[i] = time;//jump over this time period without emitting, because it's outside + m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside continue; } while(pt < time || !m_burstQueue.isEmpty()){ @@ -187,7 +187,7 @@ void QSGFollowEmitter::emitWindow(int timeStamp) pt += particleRatio; } } - m_lastEmission[i] = pt; + m_lastEmission[d->index] = pt; } m_lastTimeStamp = time; diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp index c9df5f4dbd..15bc88b4c3 100644 --- a/src/declarative/particles/qsgimageparticle.cpp +++ b/src/declarative/particles/qsgimageparticle.cpp @@ -305,6 +305,62 @@ QSGImageParticle::QSGImageParticle(QSGItem* parent) , m_lastLevel(Unknown) { setFlag(ItemHasContents); + //TODO: Clean up defaults here and in custom + m_defaultUltra = new UltraVertices; + m_defaultSimple = new SimpleVertices; + UltraVertex *vertices = (UltraVertex *) m_defaultUltra; + SimpleVertex *vertices2 = (SimpleVertex *) m_defaultSimple; + for (int i=0; i<4; ++i) { + vertices2[i].x = vertices[i].x = 0; + vertices2[i].y = vertices[i].y = 0; + vertices2[i].t = vertices[i].t = -1; + vertices2[i].lifeSpan = vertices[i].lifeSpan = 0; + vertices2[i].size = vertices[i].size = 0; + vertices2[i].endSize = vertices[i].endSize = 0; + vertices2[i].sx = vertices[i].sx = 0; + vertices2[i].sy = vertices[i].sy = 0; + vertices2[i].ax = vertices[i].ax = 0; + vertices2[i].ay = vertices[i].ay = 0; + vertices[i].xx = 1; + vertices[i].xy = 0; + vertices[i].yx = 0; + vertices[i].yy = 1; + vertices[i].rotation = 0; + vertices[i].rotationSpeed = 0; + vertices[i].autoRotate = 0; + vertices[i].animIdx = -1; + vertices[i].frameDuration = 1; + vertices[i].frameCount = 0; + vertices[i].animT = -1; + vertices[i].color.r = 255; + vertices[i].color.g = 255; + vertices[i].color.b = 255; + vertices[i].color.a = 255; + } + + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; + + vertices2[0].tx = 0; + vertices2[0].ty = 0; + + vertices2[1].tx = 1; + vertices2[1].ty = 0; + + vertices2[2].tx = 0; + vertices2[2].ty = 1; + + vertices2[3].tx = 1; + vertices2[3].ty = 1; } QDeclarativeListProperty QSGImageParticle::sprites() @@ -498,11 +554,6 @@ void QSGImageParticle::setBloat(bool arg) if(perfLevel < 9999) reset(); } -void QSGImageParticle::setCount(int c) -{ - QSGParticlePainter::setCount(c); - m_pleaseReset = true; -} void QSGImageParticle::reset() { @@ -568,35 +619,9 @@ QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode() QSGGeometry *g = new QSGGeometry(SimpleParticle_AttributeSet, vCount, iCount); g->setDrawingMode(GL_TRIANGLES); - SimpleVertex *vertices = (SimpleVertex *) g->vertexData(); - for (int p=0; pvertexData(); + for (int p=0; pindexDataAsUShort(); for (int i=0; i p) {//Transplant/IntermediateVertices? for (int i=0; i<4; ++i) { vertices[i].x = oldSimple[i].x; vertices[i].y = oldSimple[i].y; @@ -698,64 +724,14 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode() vertices[i].sy = oldSimple[i].sy; vertices[i].ax = oldSimple[i].ax; vertices[i].ay = oldSimple[i].ay; - vertices[i].xx = 1; - vertices[i].xy = 0; - vertices[i].yx = 0; - vertices[i].yy = 1; - vertices[i].rotation = 0; - vertices[i].rotationSpeed = 0; - vertices[i].autoRotate = 0; - vertices[i].animIdx = 0; + /* vertices[i].frameDuration = oldSimple[i].lifeSpan; vertices[i].frameCount = 1; vertices[i].animT = oldSimple[i].t; - vertices[i].color.r = 255; - vertices[i].color.g = 255; - vertices[i].color.b = 255; - vertices[i].color.a = 255; - } - } else { - for (int i=0; i<4; ++i) { - vertices[i].x = 0; - vertices[i].y = 0; - vertices[i].t = -1; - vertices[i].lifeSpan = 0; - vertices[i].size = 0; - vertices[i].endSize = 0; - vertices[i].sx = 0; - vertices[i].sy = 0; - vertices[i].ax = 0; - vertices[i].ay = 0; - vertices[i].xx = 1; - vertices[i].xy = 0; - vertices[i].yx = 0; - vertices[i].yy = 1; - vertices[i].rotation = 0; - vertices[i].rotationSpeed = 0; - vertices[i].autoRotate = 0; - vertices[i].animIdx = -1; - vertices[i].frameDuration = 1; - vertices[i].frameCount = 0; - vertices[i].animT = -1; - vertices[i].color.r = 0;//TODO:Some things never get used uninitialized. Consider dropping them here? - vertices[i].color.g = 0; - vertices[i].color.b = 0; - vertices[i].color.a = 0; + */ } } - vertices[0].tx = 0; - vertices[0].ty = 0; - - vertices[1].tx = 1; - vertices[1].ty = 0; - - vertices[2].tx = 0; - vertices[2].ty = 1; - - vertices[3].tx = 1; - vertices[3].ty = 1; - vertices += 4; oldSimple += 4; } @@ -813,14 +789,95 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode() return m_node; } +void QSGImageParticle::resize(int oldCount, int newCount) +{ + //If perf level changes at the same time as a resize, we reset instead of doing pending resize + if(!m_node) + return; + switch(perfLevel){ + default: + case Sprites: + if(m_spriteEngine) + reset();//TODO: Handle sprite resizeing (have to shuffle the engine too...) + case Coloured: + case Deformable: + case Tabled: + if(!m_resizePending){ + m_resizePendingUltra.resize(oldCount); + UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData(); + for(int i=0; igeometry()->vertexData(); + for(int i=0; isetFlag(QSGNode::OwnsGeometry, false); + m_node->geometry()->allocate(m_count*4, m_count*6); + memcpy(m_node->geometry()->vertexData(), tmp1, sizeof(UltraVertices) * m_count); + m_node->setFlag(QSGNode::OwnsGeometry, true); + break; + case Simple: + Q_ASSERT(m_resizePendingSimple.size() == m_count);//XXX + for(int i=0; isetFlag(QSGNode::OwnsGeometry, false); + m_node->geometry()->allocate(m_count*4, m_count*6); + memcpy(m_node->geometry()->vertexData(), tmp2, sizeof(SimpleVertices) * m_count); + m_node->setFlag(QSGNode::OwnsGeometry, true); + break; + } + quint16 *indices = m_node->geometry()->indexDataAsUShort(); + for (int i=0; isetFlag(QSGNode::OwnsGeometry, true); +} + QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) { if(m_pleaseReset){ if(m_node){ if(perfLevel == 1){ qDebug() << "Beta"; - m_lastData = qMalloc(m_count*sizeof(SimpleVertices));//TODO: Account for count_changed possibility - memcpy(m_lastData, m_node->geometry()->vertexData(), m_count * sizeof(SimpleVertices));//TODO: Multiple levels + m_lastCount = m_node->geometry()->vertexCount() / 4; + m_lastData = qMalloc(m_lastCount*sizeof(SimpleVertices)); + memcpy(m_lastData, m_node->geometry()->vertexData(), m_lastCount * sizeof(SimpleVertices));//TODO: Multiple levels } m_lastLevel = perfLevel; delete m_node; @@ -832,6 +889,8 @@ QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) m_material = 0; m_pleaseReset = false; } + if(m_resizePending) + performPendingResize(); if(m_system && m_system->isRunning()) prepareNextFrame(); @@ -990,4 +1049,20 @@ void QSGImageParticle::load(QSGParticleData *d) vertexCopy(*p->v4, d->pv); } +/* +void QSGImageParticle::verticesUpgrade(void *prev, void *next) +{ + PerformanceLevel copyLevel = qMin(perfLevel, m_lastLevel); + switch(perfLevel){//Intentional fall-through + case Sprites: + if(copyLevel >= Sprites) + case Tabled: + case Deformable: + case Coloured: + } + +} +*/ + + QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h index c6ec4c2c6a..a644dd4216 100644 --- a/src/declarative/particles/qsgimageparticle_p.h +++ b/src/declarative/particles/qsgimageparticle_p.h @@ -167,7 +167,6 @@ public: virtual void load(QSGParticleData*); virtual void reload(QSGParticleData*); - virtual void setCount(int c); QDeclarativeListProperty sprites(); QSGSpriteEngine* spriteEngine() {return m_spriteEngine;} @@ -298,12 +297,14 @@ protected: void prepareNextFrame(); QSGGeometryNode* buildParticleNode(); QSGGeometryNode* buildSimpleParticleNode(); + void resize(int oldCount, int newCount); + void performPendingResize(); private slots: void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty private: - //void vertexCopy(UltraVertex &b,const ParticleVertex& a); + //template void verticesUpgrade(IntermediateVertices* prev, T* next);//### Loses typessafety again... IntermediateVertices* fetchIntermediateVertices(int pos); bool m_do_reset; @@ -345,6 +346,14 @@ private: PerformanceLevel m_lastLevel; void* m_lastData; + int m_lastCount; + + //TODO: Some smart method that scales to multiple types better + bool m_resizePending; + QVector m_resizePendingUltra; + QVector m_resizePendingSimple; + UltraVertices* m_defaultUltra; + SimpleVertices* m_defaultSimple; }; QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgitemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp index 42f0062148..1c6a8c4db5 100644 --- a/src/declarative/particles/qsgitemparticle.cpp +++ b/src/declarative/particles/qsgitemparticle.cpp @@ -87,6 +87,7 @@ void QSGItemParticle::give(QSGItem *item) void QSGItemParticle::load(QSGParticleData* d) { + Q_ASSERT(d); int pos = particleTypeIndex(d); m_data[pos] = d; m_loadables << pos; @@ -120,7 +121,7 @@ void QSGItemParticle::tick() }else if(m_delegate){ m_items[pos] = qobject_cast(m_delegate->create(qmlContext(this))); } - if(m_items[pos]){ + if(m_items[pos] && m_data[pos]){//###Data can be zero if creating an item leads to a reset - this screws things up. m_items[pos]->setX(m_data[pos]->curX() - m_items[pos]->width()/2);//TODO: adjust for system? m_items[pos]->setY(m_data[pos]->curY() - m_items[pos]->height()/2); QSGItemParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); @@ -142,27 +143,22 @@ void QSGItemParticle::reload(QSGParticleData* d) //No-op unless we start copying the data. } -void QSGItemParticle::setCount(int c) +void QSGItemParticle::resize(int oldCount, int newCount) { - QSGParticlePainter::setCount(c);//###Do we need our own? - m_particleCount = c; - reset(); -} - -int QSGItemParticle::count() -{ - return m_particleCount; + if(!m_system) + return; + groupShuffle(m_items, (QSGItem*)0); + groupShuffle(m_data, (QSGParticleData*)0); } void QSGItemParticle::reset() { QSGParticlePainter::reset(); //TODO: Cleanup items? - m_items.resize(m_particleCount); - m_data.resize(m_particleCount); m_items.fill(0); m_data.fill(0); - //m_pendingItems.clear();//TODO: Should this be done? If so, Emit signal? + m_loadables.clear(); + //deletables? } @@ -191,7 +187,7 @@ void QSGItemParticle::prepareNextFrame() return; //TODO: Size, better fade? - for(int i=0; i m_deletables; QList< int > m_loadables; - int m_particleCount; bool m_fade; QList m_pendingItems; diff --git a/src/declarative/particles/qsgmodelparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp index f87c0d31b9..aba68c6ce4 100644 --- a/src/declarative/particles/qsgmodelparticle.cpp +++ b/src/declarative/particles/qsgmodelparticle.cpp @@ -201,25 +201,17 @@ void QSGModelParticle::reload(QSGParticleData* d) //No-op unless we start copying the data. } -void QSGModelParticle::setCount(int c) +void QSGModelParticle::resize(int oldCount, int newCount) { - QSGParticlePainter::setCount(c);//###Do we need our own? - m_particleCount = c; - reset(); -} - -int QSGModelParticle::count() -{ - return m_particleCount; + groupShuffle(m_items, (QSGItem *) 0); + groupShuffle(m_data, (QSGParticleData*) 0); + groupShuffle(m_idx, -1); } void QSGModelParticle::reset() { QSGParticlePainter::reset(); //TODO: Cleanup items? - m_items.resize(m_particleCount); - m_data.resize(m_particleCount); - m_idx.resize(m_particleCount); m_items.fill(0); m_data.fill(0); m_idx.fill(-1); @@ -253,7 +245,7 @@ void QSGModelParticle::prepareNextFrame() return; //TODO: Size, better fade? - for(int i=0; i m_deletables; QList< int > m_requests; - int m_particleCount; bool m_fade; QList m_pendingItems; diff --git a/src/declarative/particles/qsgparticleaffector.cpp b/src/declarative/particles/qsgparticleaffector.cpp index 96c5cfb25b..5b0936cc40 100644 --- a/src/declarative/particles/qsgparticleaffector.cpp +++ b/src/declarative/particles/qsgparticleaffector.cpp @@ -79,22 +79,22 @@ void QSGParticleAffector::affectSystem(qreal dt) m_groups << m_system->m_groupIds[p];//###Can this occur before group ids are properly assigned? m_updateIntSet = false; } - //foreach(ParticleData* d, m_system->m_data){ - for(int i=0; im_particle_count; i++){ - QSGParticleData* d = m_system->m_data[i]; - if(!d || (m_onceOff && m_onceOffed.contains(d->systemIndex))) - continue; - if(m_groups.isEmpty() || m_groups.contains(d->group)){ - //Need to have previous location for affected. if signal || shape might be faster? - QPointF curPos = QPointF(d->curX(), d->curY()); - if(width() == 0 || height() == 0 - || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()),curPos)){ - if(affectParticle(d, dt)){ - m_system->m_needsReset << d; - if(m_onceOff) - m_onceOffed << d->systemIndex; - if(m_signal) - emit affected(curPos.x(), curPos.y()); + foreach(QSGParticleGroupData* gd, m_system->m_groupData){ + foreach(QSGParticleData* d, gd->data){ + if(!d || (m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index)))) + continue; + if(m_groups.isEmpty() || m_groups.contains(d->group)){ + //Need to have previous location for affected. if signal || shape might be faster? + QPointF curPos = QPointF(d->curX(), d->curY()); + if(width() == 0 || height() == 0 + || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()),curPos)){ + if(affectParticle(d, dt)){ + m_system->m_needsReset << d; + if(m_onceOff) + m_onceOffed << qMakePair(d->group, d->index); + if(m_signal) + emit affected(curPos.x(), curPos.y()); + } } } } @@ -108,10 +108,10 @@ bool QSGParticleAffector::affectParticle(QSGParticleData *d, qreal dt) return false; } -void QSGParticleAffector::reset(int idx) +void QSGParticleAffector::reset(QSGParticleData* pd) {//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass if(m_onceOff) - m_onceOffed.remove(idx); + m_onceOffed.remove(qMakePair(pd->group, pd->index)); } void QSGParticleAffector::updateOffsets() diff --git a/src/declarative/particles/qsgparticleaffector_p.h b/src/declarative/particles/qsgparticleaffector_p.h index b299158069..7418760941 100644 --- a/src/declarative/particles/qsgparticleaffector_p.h +++ b/src/declarative/particles/qsgparticleaffector_p.h @@ -65,7 +65,7 @@ class QSGParticleAffector : public QSGItem public: explicit QSGParticleAffector(QSGItem *parent = 0); virtual void affectSystem(qreal dt); - virtual void reset(int systemIdx);//As some store their own data per idx? + virtual void reset(QSGParticleData*);//As some store their own data per particle? QSGParticleSystem* system() const { return m_system; @@ -108,7 +108,7 @@ signals: void shapeChanged(QSGParticleExtruder* arg); - void affected(qreal x, qreal y);//###Idx too? + void affected(qreal x, qreal y); void signalChanged(bool arg); public slots: @@ -174,7 +174,7 @@ protected: QPointF m_offset; private: QSet m_groups; - QSet m_onceOffed; + QSet > m_onceOffed; bool m_updateIntSet; bool m_onceOff; diff --git a/src/declarative/particles/qsgparticlepainter.cpp b/src/declarative/particles/qsgparticlepainter.cpp index a5e8c0a3e6..0d369fd4d8 100644 --- a/src/declarative/particles/qsgparticlepainter.cpp +++ b/src/declarative/particles/qsgparticlepainter.cpp @@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE QSGParticlePainter::QSGParticlePainter(QSGItem *parent) : QSGItem(parent), - m_system(0) + m_system(0), m_count(0), m_lastStart(0) { connect(this, SIGNAL(xChanged()), this, SLOT(calcSystemOffset())); @@ -67,7 +67,7 @@ void QSGParticlePainter::setSystem(QSGParticleSystem *arg) if (m_system != arg) { m_system = arg; if(m_system){ - m_system->registerParticleType(this); + m_system->registerParticlePainter(this); connect(m_system, SIGNAL(xChanged()), this, SLOT(calcSystemOffset())); connect(m_system, SIGNAL(yChanged()), @@ -89,15 +89,24 @@ void QSGParticlePainter::reload(QSGParticleData*) void QSGParticlePainter::reset() { //Have to every time because what it's emitting may have changed and that affects particleTypeIndex - m_particleStarts.clear(); - m_lastStart = 0; + if(m_system) + updateParticleStarts(); } +void QSGParticlePainter::resize(int, int) +{ +} + + void QSGParticlePainter::setCount(int c) { + Q_ASSERT(c >= 0); //XXX if(c == m_count) return; + int lastCount = m_count; m_count = c; + resize(lastCount, m_count);//### is virtual needed? Or should I just use the signal? + updateParticleStarts(); emit countChanged(); } @@ -107,14 +116,27 @@ int QSGParticlePainter::count() } +void QSGParticlePainter::updateParticleStarts() +{ + m_particleStarts.clear(); + m_lastStart = 0; + QList particleList; + if(m_particles.isEmpty()) + particleList << 0; + foreach(const QString &s, m_particles) + particleList << m_system->m_groupIds[s]; + foreach(int gIdx, particleList){ + QSGParticleGroupData *gd = m_system->m_groupData[gIdx]; + m_particleStarts.insert(gIdx, qMakePair(gd->size, m_lastStart)); + m_lastStart += gd->size; + } +} + int QSGParticlePainter::particleTypeIndex(QSGParticleData* d) { - if(!m_particleStarts.contains(d->group)){ - m_particleStarts.insert(d->group, m_lastStart); - m_lastStart += m_system->m_groupData[d->group]->size; - } - int ret = m_particleStarts[d->group] + d->particleIndex; - Q_ASSERT(ret >=0 && ret < m_count);//XXX: Possibly shouldn't assert, but bugs here were hard to find in the past + Q_ASSERT(d && m_particleStarts.contains(d->group));//XXX + int ret = m_particleStarts[d->group].second + d->index; + Q_ASSERT(ret >=0 && ret < m_count);//XXX:shouldn't assert, but bugs here were hard to find in the past return ret; } @@ -129,8 +151,8 @@ void QSGParticlePainter::calcSystemOffset() //Reload all particles//TODO: Necessary? foreach(const QString &g, m_particles){ int gId = m_system->m_groupIds[g]; - for(int i=0; im_groupData[gId]->size; i++) - reload(m_system->m_data[m_system->m_groupData[gId]->start + i]); + foreach(QSGParticleData* d, m_system->m_groupData[gId]->data) + reload(d); } } } diff --git a/src/declarative/particles/qsgparticlepainter_p.h b/src/declarative/particles/qsgparticlepainter_p.h index 8f1e13b70f..6657d57501 100644 --- a/src/declarative/particles/qsgparticlepainter_p.h +++ b/src/declarative/particles/qsgparticlepainter_p.h @@ -63,8 +63,8 @@ public: explicit QSGParticlePainter(QSGItem *parent = 0); virtual void load(QSGParticleData*); virtual void reload(QSGParticleData*); - virtual void setCount(int c); - virtual int count(); + void setCount(int c); + int count(); QSGParticleSystem* system() const { return m_system; @@ -76,7 +76,6 @@ public: return m_particles; } - int particleTypeIndex(QSGParticleData*); signals: void countChanged(); void systemChanged(QSGParticleSystem* arg); @@ -95,25 +94,23 @@ void setParticles(QStringList arg) } private slots: void calcSystemOffset(); + void updateParticleStarts(); + protected: virtual void reset(); virtual void componentComplete(); -// virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *){ -// qDebug() << "Shouldn't be here..." << this; -// return 0; -// } QSGParticleSystem* m_system; friend class QSGParticleSystem; int m_count; bool m_pleaseReset; QStringList m_particles; - QHash m_particleStarts; + QHash > m_particleStarts; //Group, size, idx int m_lastStart; QPointF m_systemOffset; - template + template //just convenience void vertexCopy(VertexStruct &b, const ParticleVertex& a) { b.x = a.x - m_systemOffset.x(); @@ -128,6 +125,36 @@ protected: b.ay = a.ay; } + //###Abstracted primarily for code reuse. Demote to subclasses? + int particleTypeIndex(QSGParticleData*); + virtual void resize(int oldCount, int newCount); + template + void groupShuffle(QVector &v, const T& zero)//Must be called inside resize + { + //TODO: In place shuffling because it's faster + QVector v0(v); + v.clear(); + v.resize(m_count); + int lastStart = 0; + QList particleList; + if(m_particles.isEmpty()) + particleList << 0; + foreach(const QString &s, m_particles) + particleList << m_system->m_groupIds[s]; + + foreach(int gIdx, particleList){ + QSGParticleGroupData *gd = m_system->m_groupData[gIdx]; + for(int i=0; idata.size(); i++){//TODO: When group didn't exist before + int newIdx = lastStart + i;//Have to make the same way as in updateParticleStarts + if(i >= m_particleStarts[gIdx].first || v0.size() <= m_particleStarts[gIdx].second + i) + v[newIdx] = zero; + else + v[newIdx] = v0[m_particleStarts[gIdx].second + i]; + } + lastStart += gd->size; + } + } + private: }; diff --git a/src/declarative/particles/qsgparticlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp index 4a8f75bb04..a93f2a5eba 100644 --- a/src/declarative/particles/qsgparticlesystem.cpp +++ b/src/declarative/particles/qsgparticlesystem.cpp @@ -52,8 +52,7 @@ QT_BEGIN_NAMESPACE QSGParticleData::QSGParticleData() : group(0) , e(0) - , particleIndex(0) - , systemIndex(0) + , index(0) { pv.x = 0; pv.y = 0; @@ -71,34 +70,126 @@ QSGParticleSystem::QSGParticleSystem(QSGItem *parent) : , m_startTime(0), m_overwrite(false) , m_componentComplete(false) { - m_groupIds = QHash(); + QSGParticleGroupData* gd = new QSGParticleGroupData;//Default group + m_groupData.insert(0,gd); + m_groupIds.insert("",0); + m_nextGroupId = 1; + + connect(&m_painterMapper, SIGNAL(mapped(QObject*)), + this, SLOT(loadPainter(QObject*))); } -void QSGParticleSystem::registerParticleType(QSGParticlePainter* p) +void QSGParticleSystem::registerParticlePainter(QSGParticlePainter* p) { - m_particles << QPointer(p);//###Set or uniqueness checking? - reset(); + //TODO: a way to Unregister emitters, painters and affectors + m_particlePainters << QPointer(p);//###Set or uniqueness checking? + connect(p, SIGNAL(particlesChanged(QStringList)), + &m_painterMapper, SLOT(map())); + loadPainter(p); + p->update();//###Initial update here? } void QSGParticleSystem::registerParticleEmitter(QSGParticleEmitter* e) { m_emitters << QPointer(e);//###How to get them out? connect(e, SIGNAL(particleCountChanged()), - this, SLOT(countChanged())); + this, SLOT(emittersChanged())); connect(e, SIGNAL(particleChanged(QString)), - this, SLOT(countChanged())); - reset(); + this, SLOT(emittersChanged())); + emittersChanged(); + e->reset();//Start, so that starttime factors appropriately } void QSGParticleSystem::registerParticleAffector(QSGParticleAffector* a) { m_affectors << QPointer(a); - //reset();//TODO: Slim down the huge batch of resets at the start } -void QSGParticleSystem::countChanged() +void QSGParticleSystem::loadPainter(QObject *p) { - reset();//Need to give Particles new Count + if(!m_componentComplete) + return; + + QSGParticlePainter* painter = qobject_cast(p); + Q_ASSERT(painter);//XXX + foreach(QSGParticleGroupData* sg, m_groupData) + sg->painters.remove(painter); + int particleCount = 0; + if(painter->particles().isEmpty()){//Uses default particle + particleCount += m_groupData[0]->size; + m_groupData[0]->painters << painter; + }else{ + foreach(const QString &group, painter->particles()){ + particleCount += m_groupData[m_groupIds[group]]->size; + m_groupData[m_groupIds[group]]->painters << painter; + } + } + painter->setCount(particleCount); + painter->update();//###Initial update here? + return; +} + +void QSGParticleSystem::emittersChanged() +{ + if(!m_componentComplete) + return; + + m_emitters.removeAll(0); + + //Recalculate all counts, as emitter 'particle' may have changed as well + //### Worth tracking previous 'particle' per emitter to do partial recalculations? + m_particle_count = 0; + + int previousGroups = m_nextGroupId; + QVector previousSizes; + previousSizes.resize(previousGroups); + for(int i=0; isize; + for(int i=0; isize = 0; + + foreach(QSGParticleEmitter* e, m_emitters){//Populate groups and set sizes. + if(!m_groupIds.contains(e->particle()) + || (!e->particle().isEmpty() && !m_groupIds[e->particle()])){//or it was accidentally inserted by a failed lookup earlier + QSGParticleGroupData* gd = new QSGParticleGroupData; + int id = m_nextGroupId++; + m_groupIds.insert(e->particle(), id); + m_groupData.insert(id, gd); + } + m_groupData[m_groupIds[e->particle()]]->size += e->particleCount(); + m_particle_count += e->particleCount(); + //###: Cull emptied groups? + } + + foreach(QSGParticleGroupData* gd, m_groupData){//resize groups and update painters + int id = m_groupData.key(gd); + + //TODO: Shrink back down! (but it has the problem of trying to remove the dead particles while maintaining integrity) + gd->size = qMax(gd->size, id < previousGroups?previousSizes[id]:0); + + gd->data.resize(gd->size); + if(id < previousGroups){ + for(int i=previousSizes[id]; isize; i++) + gd->data[i] = 0; + /*TODO:Consider salvaging partial updates, but have to batch changes to a single painter + int delta = 0; + delta = gd->size - previousSizes[id]; + foreach(QSGParticlePainter* painter, gd->painters){ + if(!painter->count() && delta){ + painter->reset(); + painter->update(); + } + qDebug() << "Phi" << painter << painter->count() << delta; + painter->setCount(painter->count() + delta); + } + */ + } + } + foreach(QSGParticlePainter *p, m_particlePainters) + loadPainter(p); + + if(m_particle_count > 16000)//###Investigate if these limits are worth warning about? + qWarning() << "Particle system arbitarily believes it has a vast number of particles (>16000). Expect poor performance"; } void QSGParticleSystem::setRunning(bool arg) @@ -114,131 +205,67 @@ void QSGParticleSystem::componentComplete() { QSGItem::componentComplete(); m_componentComplete = true; - if(!m_emitters.isEmpty() && !m_particles.isEmpty()) - reset(); + //if(!m_emitters.isEmpty() && !m_particlePainters.isEmpty()) + reset(); } -void QSGParticleSystem::initializeSystem() -{ - int oldCount = m_particle_count; - m_particle_count = 0;//TODO: Only when changed? - - //### Reset the data too? - for(int i=0; i::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++) - delete (*iter); - m_groupData.clear(); - m_groupIds.clear(); - - GroupData* gd = new GroupData;//Default group - gd->size = 0; - gd->start = -1; - gd->nextIdx = 0; - m_groupData.insert(0,gd); - m_groupIds.insert("",0); - m_nextGroupId = 1; - - if(!m_emitters.count() || !m_particles.count()) - return; - - foreach(QSGParticleEmitter* e, m_emitters){ - if(!m_groupIds.contains(e->particle()) - || (!e->particle().isEmpty() && !m_groupIds[e->particle()])){//or it was accidentally inserted by a failed lookup earlier - GroupData* gd = new GroupData; - gd->size = 0; - gd->start = -1; - gd->nextIdx = 0; - int id = m_nextGroupId++; - m_groupIds.insert(e->particle(), id); - m_groupData.insert(id, gd); - } - m_groupData[m_groupIds[e->particle()]]->size += e->particleCount(); - } - - for(QHash::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++){ - (*iter)->start = m_particle_count; - m_particle_count += (*iter)->size; - } - m_data.resize(m_particle_count); - for(int i=oldCount; i 16000) - qWarning() << "Particle system contains a vast number of particles (>16000). Expect poor performance"; - - foreach(QSGParticlePainter* particle, m_particles){ - int particleCount = 0; - if(particle->particles().isEmpty()){//Uses default particle - particleCount += m_groupData[0]->size; - m_groupData[0]->types << particle; - }else{ - foreach(const QString &group, particle->particles()){ - particleCount += m_groupData[m_groupIds[group]]->size; - m_groupData[m_groupIds[group]]->types << particle; - } - } - particle->setCount(particleCount); - particle->m_pleaseReset = true; - } - - m_timestamp.start(); - m_initialized = true; - emit systemInitialized(); - qDebug() << "System Initialized. Size:" << m_particle_count; -} - -void QSGParticleSystem::reset() +void QSGParticleSystem::reset()//TODO: Needed? { if(!m_componentComplete) - return;//Batch starting reset()s a little + return; + //Clear guarded pointers which have been deleted int cleared = 0; cleared += m_emitters.removeAll(0); - cleared += m_particles.removeAll(0); + cleared += m_particlePainters.removeAll(0); cleared += m_affectors.removeAll(0); //qDebug() << "Reset" << m_emitters.count() << m_particles.count() << "Cleared" << cleared; - foreach(QSGParticlePainter* p, m_particles) - p->reset(); - foreach(QSGParticleEmitter* e, m_emitters) - e->reset(); + + emittersChanged(); + + //TODO: Reset data +// foreach(QSGParticlePainter* p, m_particlePainters) +// p->reset(); +// foreach(QSGParticleEmitter* e, m_emitters) +// e->reset(); + //### Do affectors need reset too? + if(!m_running) return; - initializeSystem(); - foreach(QSGParticlePainter* p, m_particles) - p->update(); - foreach(QSGParticleEmitter* e, m_emitters) - e->emitWindow(0);//Start, so that starttime factors appropriately + + foreach(QSGParticlePainter *p, m_particlePainters){ + loadPainter(p); + p->reset(); + } + + m_timestamp.start();//TODO: Better placement + m_initialized = true; } QSGParticleData* QSGParticleSystem::newDatum(int groupId) { + Q_ASSERT(groupId < m_groupData.count());//XXX shouldn't really be an assert Q_ASSERT(m_groupData[groupId]->size); - int nextIdx = m_groupData[groupId]->start + m_groupData[groupId]->nextIdx++; + if( m_groupData[groupId]->nextIdx >= m_groupData[groupId]->size) m_groupData[groupId]->nextIdx = 0; + int nextIdx = m_groupData[groupId]->nextIdx++; - Q_ASSERT(nextIdx < m_data.size()); + Q_ASSERT(nextIdx < m_groupData[groupId]->size); QSGParticleData* ret; - if(m_data[nextIdx]){//Recycle, it's faster. - ret = m_data[nextIdx]; + if(m_groupData[groupId]->data[nextIdx]){//Recycle, it's faster. + ret = m_groupData[groupId]->data[nextIdx]; if(!m_overwrite && ret->stillAlive()){ return 0;//Artificial longevity (or too fast emission) means this guy hasn't died. To maintain count, don't emit a new one }//###Reset? }else{ ret = new QSGParticleData; - m_data[nextIdx] = ret; + m_groupData[groupId]->data[nextIdx] = ret; } ret->system = this; - ret->systemIndex = nextIdx; - ret->particleIndex = nextIdx - m_groupData[groupId]->start; + ret->index = nextIdx; ret->group = groupId; return ret; } @@ -254,8 +281,8 @@ void QSGParticleSystem::emitParticle(QSGParticleData* pd) foreach(QSGParticleAffector *a, m_affectors) if(a && a->m_needsReset) - a->reset(pd->systemIndex); - foreach(QSGParticlePainter* p, m_groupData[pd->group]->types) + a->reset(pd); + foreach(QSGParticlePainter* p, m_groupData[pd->group]->painters) if(p) p->load(pd); } @@ -285,7 +312,7 @@ qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p) if(a) a->affectSystem(dt); foreach(QSGParticleData* d, m_needsReset) - foreach(QSGParticlePainter* p, m_groupData[d->group]->types) + foreach(QSGParticlePainter* p, m_groupData[d->group]->painters) if(p && d) p->reload(d); } diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h index 9730ff35e8..4c690ff7a8 100644 --- a/src/declarative/particles/qsgparticlesystem_p.h +++ b/src/declarative/particles/qsgparticlesystem_p.h @@ -47,6 +47,7 @@ #include #include #include +#include QT_BEGIN_HEADER @@ -61,11 +62,14 @@ class QSGParticlePainter; class QSGParticleData; -struct GroupData{ +class QSGParticleGroupData{ +public: + QSGParticleGroupData():size(0),nextIdx(0) + {} int size; - int start; int nextIdx; - QList types; + QSet painters; + QVector data; }; class QSGParticleSystem : public QSGItem @@ -131,20 +135,20 @@ protected: void componentComplete(); private slots: - void countChanged(); + void emittersChanged(); + void loadPainter(QObject* p); public://but only really for related class usage. Perhaps we should all be friends? void emitParticle(QSGParticleData* p); QSGParticleData* newDatum(int groupId); qint64 systemSync(QSGParticlePainter* p); QElapsedTimer m_timestamp; - QVector m_data; QSet m_needsReset; QHash m_groupIds; - QHash m_groupData;//id, size, start + QHash m_groupData; qint64 m_timeInt; bool m_initialized; - void registerParticleType(QSGParticlePainter* p); + void registerParticlePainter(QSGParticlePainter* p); void registerParticleEmitter(QSGParticleEmitter* e); void registerParticleAffector(QSGParticleAffector* a); bool overwrite() const @@ -158,12 +162,15 @@ private: bool m_running; QList > m_emitters; QList > m_affectors; - QList > m_particles; + QList > m_particlePainters; QList > m_syncList; qint64 m_startTime; int m_nextGroupId; bool m_overwrite; bool m_componentComplete; + + QSignalMapper m_painterMapper; + QSignalMapper m_emitterMapper; }; //TODO: Clean up all this into ParticleData @@ -211,10 +218,9 @@ public: qreal curSY() const; int group; - QSGParticleEmitter* e; + QSGParticleEmitter* e;//### Needed? QSGParticleSystem* system; - int particleIndex; - int systemIndex; + int index; void debugDump(); bool stillAlive(); diff --git a/src/declarative/particles/qsgspritegoal.cpp b/src/declarative/particles/qsgspritegoal.cpp index 8dc98ae314..c97bfd1f26 100644 --- a/src/declarative/particles/qsgspritegoal.cpp +++ b/src/declarative/particles/qsgspritegoal.cpp @@ -81,7 +81,7 @@ bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt) Q_UNUSED(dt); //TODO: Affect all engines QSGSpriteEngine *engine = 0; - foreach(QSGParticlePainter *p, m_system->m_groupData[d->group]->types) + foreach(QSGParticlePainter *p, m_system->m_groupData[d->group]->painters) if(qobject_cast(p)) engine = qobject_cast(p)->spriteEngine(); if(!engine) @@ -89,8 +89,8 @@ bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt) if(m_goalIdx == -2 || engine != m_lastEngine) updateStateIndex(engine); - if(engine->spriteState(d->particleIndex) != m_goalIdx){ - engine->setGoal(m_goalIdx, d->particleIndex, m_jump); + if(engine->spriteState(d->index) != m_goalIdx){ + engine->setGoal(m_goalIdx, d->index, m_jump); emit affected(QPointF(d->curX(), d->curY()));//###Expensive if unconnected? Move to Affector? return true; //Doesn't affect particle data, but necessary for onceOff } diff --git a/src/declarative/particles/qsgturbulence.cpp b/src/declarative/particles/qsgturbulence.cpp index 476db9c4b0..bb46b99f0d 100644 --- a/src/declarative/particles/qsgturbulence.cpp +++ b/src/declarative/particles/qsgturbulence.cpp @@ -128,30 +128,34 @@ void QSGTurbulenceAffector::affectSystem(qreal dt) m_lastT += period; } - foreach(QSGParticleData *d, m_system->m_data){ - if(!d || !activeGroup(d->group)) + foreach(QSGParticleGroupData *gd, m_system->m_groupData){ + if(!activeGroup(m_system->m_groupData.key(gd)))//TODO: Surely this can be done better return; - qreal fx = 0.0; - qreal fy = 0.0; - QPointF pos = QPointF(d->curX() - x(), d->curY() - y());//TODO: Offset - QPointF nodePos = QPointF(pos.x() / m_spacing.x(), pos.y() / m_spacing.y()); - QSet > nodes; - nodes << qMakePair((int)ceil(nodePos.x()), (int)ceil(nodePos.y())); - nodes << qMakePair((int)ceil(nodePos.x()), (int)floor(nodePos.y())); - nodes << qMakePair((int)floor(nodePos.x()), (int)ceil(nodePos.y())); - nodes << qMakePair((int)floor(nodePos.x()), (int)floor(nodePos.y())); - typedef QPair intPair; - foreach(const intPair &p, nodes){ - if(!QRect(0,0,m_gridSize-1,m_gridSize-1).contains(QPoint(p.first, p.second))) - continue; - qreal dist = magnitude(pos.x() - p.first*m_spacing.x(), pos.y() - p.second*m_spacing.y());//TODO: Mathematically valid - fx += m_field[p.first][p.second].x() * ((m_magSum - dist)/m_magSum);//Proportionally weight nodes - fy += m_field[p.first][p.second].y() * ((m_magSum - dist)/m_magSum); - } - if(fx || fy){ - d->setInstantaneousSX(d->curSX()+ fx * dt); - d->setInstantaneousSY(d->curSY()+ fy * dt); - m_system->m_needsReset << d; + foreach(QSGParticleData *d, gd->data){ + if(!d || !activeGroup(d->group)) + return; + qreal fx = 0.0; + qreal fy = 0.0; + QPointF pos = QPointF(d->curX() - x(), d->curY() - y());//TODO: Offset + QPointF nodePos = QPointF(pos.x() / m_spacing.x(), pos.y() / m_spacing.y()); + QSet > nodes; + nodes << qMakePair((int)ceil(nodePos.x()), (int)ceil(nodePos.y())); + nodes << qMakePair((int)ceil(nodePos.x()), (int)floor(nodePos.y())); + nodes << qMakePair((int)floor(nodePos.x()), (int)ceil(nodePos.y())); + nodes << qMakePair((int)floor(nodePos.x()), (int)floor(nodePos.y())); + typedef QPair intPair; + foreach(const intPair &p, nodes){ + if(!QRect(0,0,m_gridSize-1,m_gridSize-1).contains(QPoint(p.first, p.second))) + continue; + qreal dist = magnitude(pos.x() - p.first*m_spacing.x(), pos.y() - p.second*m_spacing.y());//TODO: Mathematically valid + fx += m_field[p.first][p.second].x() * ((m_magSum - dist)/m_magSum);//Proportionally weight nodes + fy += m_field[p.first][p.second].y() * ((m_magSum - dist)/m_magSum); + } + if(fx || fy){ + d->setInstantaneousSX(d->curSX()+ fx * dt); + d->setInstantaneousSY(d->curSY()+ fy * dt); + m_system->m_needsReset << d; + } } } } From b0183beea565baca7f32f4f2f4aa579ae6901577 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 15 Jun 2011 11:32:49 +0200 Subject: [PATCH 29/48] Remove annoying warning --- src/declarative/items/qsgshadereffectmesh.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/declarative/items/qsgshadereffectmesh.cpp b/src/declarative/items/qsgshadereffectmesh.cpp index 4900755e46..dbc3472d09 100644 --- a/src/declarative/items/qsgshadereffectmesh.cpp +++ b/src/declarative/items/qsgshadereffectmesh.cpp @@ -203,7 +203,6 @@ void QSGGridMesh::setResolution(const QSize &res) if (res == m_resolution) return; if (res.width() < 1 || res.height() < 1) { - qWarning("QSGGridMesh: Resolution must be at least 1x1"); return; } m_resolution = res; From ca31cbf2ad5dc43d74a2220d929f45bfe10c806b Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 15 Jun 2011 11:51:15 +0200 Subject: [PATCH 30/48] Renamed QMLRenderer -> QSGDefaultRenderer Change-Id: Ib1f00d45cecd3b438148adce2f7cf247030b2dfb --- src/declarative/items/qsgshadereffectmesh.cpp | 5 ----- .../scenegraph/coreapi/qsgdefaultrenderer.cpp | 14 +++++++------- .../scenegraph/coreapi/qsgdefaultrenderer_p.h | 4 ++-- src/declarative/scenegraph/qsgcontext.cpp | 2 +- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/declarative/items/qsgshadereffectmesh.cpp b/src/declarative/items/qsgshadereffectmesh.cpp index 4900755e46..f4d8b468af 100644 --- a/src/declarative/items/qsgshadereffectmesh.cpp +++ b/src/declarative/items/qsgshadereffectmesh.cpp @@ -135,11 +135,6 @@ QSGGeometry *QSGGridMesh::updateGeometry(QSGGeometry *geometry, const QVector::pop() } -QMLRenderer::QMLRenderer(QSGContext *context) +QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context) : QSGRenderer(context) , m_opaqueNodes(64) , m_transparentNodes(64) @@ -176,7 +176,7 @@ QMLRenderer::QMLRenderer(QSGContext *context) #endif } -void QMLRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) +void QSGDefaultRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) { QSGRenderer::nodeChanged(node, flags); @@ -191,7 +191,7 @@ void QMLRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) m_needs_sorting = true; } -void QMLRenderer::render() +void QSGDefaultRenderer::render() { #if defined (QML_RUNTIME_TESTING) static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree")); @@ -348,18 +348,18 @@ public: bool operator < (const Foo &other) const { return nodeLessThan(second, other.second); } }; -void QMLRenderer::setSortFrontToBackEnabled(bool sort) +void QSGDefaultRenderer::setSortFrontToBackEnabled(bool sort) { printf("setting sorting to... %d\n", sort); m_sort_front_to_back = sort; } -bool QMLRenderer::isSortFrontToBackEnabled() const +bool QSGDefaultRenderer::isSortFrontToBackEnabled() const { return m_sort_front_to_back; } -void QMLRenderer::buildLists(QSGNode *node) +void QSGDefaultRenderer::buildLists(QSGNode *node) { if (node->isSubtreeBlocked()) return; @@ -432,7 +432,7 @@ void QMLRenderer::buildLists(QSGNode *node) } } -void QMLRenderer::renderNodes(const QDataBuffer &list) +void QSGDefaultRenderer::renderNodes(const QDataBuffer &list) { const float scale = 1.0f / m_currentRenderOrder; int count = list.size(); diff --git a/src/declarative/scenegraph/coreapi/qsgdefaultrenderer_p.h b/src/declarative/scenegraph/coreapi/qsgdefaultrenderer_p.h index ca1f5592cf..c855ed606a 100644 --- a/src/declarative/scenegraph/coreapi/qsgdefaultrenderer_p.h +++ b/src/declarative/scenegraph/coreapi/qsgdefaultrenderer_p.h @@ -52,11 +52,11 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class QMLRenderer : public QSGRenderer +class QSGDefaultRenderer : public QSGRenderer { Q_OBJECT public: - QMLRenderer(QSGContext *context); + QSGDefaultRenderer(QSGContext *context); void render(); diff --git a/src/declarative/scenegraph/qsgcontext.cpp b/src/declarative/scenegraph/qsgcontext.cpp index 55942bebe4..635b0ad091 100644 --- a/src/declarative/scenegraph/qsgcontext.cpp +++ b/src/declarative/scenegraph/qsgcontext.cpp @@ -308,7 +308,7 @@ QSGRenderer *QSGContext::createRenderer() { // ### Do something with this before release... static bool doFrontToBack = qApp->arguments().contains(QLatin1String("--opaque-front-to-back")); - QMLRenderer *renderer = new QMLRenderer(this); + QSGDefaultRenderer *renderer = new QSGDefaultRenderer(this); if (doFrontToBack) { printf("QSGContext: Sorting opaque nodes front to back...\n"); renderer->setSortFrontToBackEnabled(true); From 658ca667ad9a3dca6313886f8d49d254b5161c1b Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 17 Jun 2011 11:43:09 +1000 Subject: [PATCH 31/48] Don't leak memory in benchmark --- tests/benchmarks/declarative/binding/tst_binding.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/declarative/binding/tst_binding.cpp b/tests/benchmarks/declarative/binding/tst_binding.cpp index 5dce36d31d..70796f682a 100644 --- a/tests/benchmarks/declarative/binding/tst_binding.cpp +++ b/tests/benchmarks/declarative/binding/tst_binding.cpp @@ -186,7 +186,8 @@ void tst_binding::creation() COMPONENT(file, binding); QBENCHMARK { - c.create(); + QObject *o = c.create(); + delete o; } } From 7031ca0e1d896336334f5a788baac6c09a9ce703 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Mon, 20 Jun 2011 08:31:50 +1000 Subject: [PATCH 32/48] Add QDeclarativeChangeSet. Takes a sequence of model changes and reorders and compresses them so that like changes are grouped together and are ordered from start to end. The order is Removed, Inserted, Moved then Changed. --- .../util/qdeclarativechangeset.cpp | 440 +++++++++++++ .../util/qdeclarativechangeset_p.h | 160 +++++ src/declarative/util/util.pri | 2 + tests/auto/declarative/declarative.pro | 1 + .../qdeclarativechangeset.pro | 17 + .../tst_qdeclarativechangeset.cpp | 614 ++++++++++++++++++ 6 files changed, 1234 insertions(+) create mode 100644 src/declarative/util/qdeclarativechangeset.cpp create mode 100644 src/declarative/util/qdeclarativechangeset_p.h create mode 100644 tests/auto/declarative/qdeclarativechangeset/qdeclarativechangeset.pro create mode 100644 tests/auto/declarative/qdeclarativechangeset/tst_qdeclarativechangeset.cpp diff --git a/src/declarative/util/qdeclarativechangeset.cpp b/src/declarative/util/qdeclarativechangeset.cpp new file mode 100644 index 0000000000..d9a508ba13 --- /dev/null +++ b/src/declarative/util/qdeclarativechangeset.cpp @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativechangeset_p.h" + +void QDeclarativeChangeSet::insertInsert(int start, int end) +{ + const int count = end - start; + + // Moved signals. + QVector::iterator move = m_moves.begin(); + for (; move != m_moves.end() && start >= move->maximum(); ++move) {} + for (; move != m_moves.end() && end >= move->minimum(); ++move) { + if (start <= move->tstart) { + move->tstart += count; + move->tend += count; + } else if (start < move->tend) { + int relativeStart = start - move->tstart; + + move = m_moves.insert(move, Move( + move->fstart + count, move->fstart + count + relativeStart, move->tstart)); + ++move; + move->fstart += relativeStart; + move->tstart += count + relativeStart; + move->tend += count; + + start -= relativeStart; + end -= relativeStart; + } else { + start -= move->count(); + end -= move->count(); + } + + if (start <= move->fstart) { + move->fstart += count; + move->fend += count; + } else if (start < move->tstart) { + start += move->count(); + end += move->count(); + } + + } + for (; move != m_moves.end(); ++move) { + move->fstart += count; + move->fend += count; + move->tstart += count; + move->tend += count; + } + + // Inserted signals. + QVector::iterator insert = m_inserts.begin(); + for (; insert != m_inserts.end(); ++insert) { + if (start < insert->start) { + insert = m_inserts.insert(insert, Insert(start, end)); + break; + } else if (start <= insert->end) { + insert->end += count; + break; + } + } + if (insert == m_inserts.end()) { + m_inserts.append(Insert(start, end)); + } else for (++insert; insert != m_inserts.end(); ++insert) { + insert->start += count; + insert->end += count; + } + + + // Changed signals. + QVector::iterator change = m_changes.begin(); + for (; change != m_changes.end() && start != change->start && start < change->end; ++change) { + if (start > change->start) { + int relativeStart = start - change->start; + change = m_changes.insert(change, Change(change->start, change->start + relativeStart)); + ++change; + change->start += count + relativeStart; + change->end += count - relativeStart; + break; + } + } + for (; change != m_changes.end(); ++change) { + change->start += count; + change->end += count; + } +} + +void QDeclarativeChangeSet::insertRemove(int start, int end) +{ + // Changed Signals. + QVector::iterator change = m_changes.begin(); + for (; change != m_changes.end() && start >= change->end; ++change) {} + for (; change != m_changes.end() && end < change->start; ++change) { + const int removeCount = qMin(change->end, end) - qMax(change->start, start); + change->end -= removeCount; + if (change->start == change->end) { + change = m_changes.erase(change); + } else if (start < change->start) { + change->start = start; + } + } + const int count = end - start; + for (; change != m_changes.end(); ++change) { + change->start -= count; + change->end -= count; + } + + QVector removeChanges; + + // Moved signals. + QVector::iterator move = m_moves.begin(); + for (; move != m_moves.end() && start >= move->maximum(); ++move) {} + for (; move != m_moves.end() && end >= move->minimum(); ++move) { + if (move->fstart < move->tstart) { + if (start < move->fstart) { + const int difference = move->fstart - start; + move->fend -= difference; + move->fstart = start; + move->tstart -= difference; + move->tend -= difference; + + removeChanges.append(Remove(start, start + difference)); + end -= difference; + } + if (end < move->tstart) { + move->tstart -= end - start; + move->tend -= end - start; + } else if (start < move->tend) { + const int difference = qMin(move->tend, end) - move->tstart; + removeChanges.append(Remove( + move->fstart , move->fstart + difference)); + end -= difference; + + move->fend -= difference; + move->tstart -= end - start; + move->tend -= end - start + difference; + } + start += move->count(); + end += move->count(); + } else { + if (start < move->tend) { + const int offset = qMax(0, start - move->tstart); + const int difference = qMin(move->tend, end) - qMax(move->tstart, start); + + removeChanges.append(Remove( + move->fstart + offset, move->fstart + offset + difference)); + start -= offset; + end -= offset + difference; + + move->fend -= difference; + move->tstart = start; + move->tend = start + move->fend - move->fstart; + } else { + start -= move->count(); + end -= move->count(); + } + + move->fstart -= end - start; + move->fend -= end - start; + + if (start > move->fstart) { + const int offset = start - move->fstart; + const int difference = qMin(move->fend, end) - start; + removeChanges.append(Remove( + move->fstart + end - start + offset + difference , + move->fend + end - start + offset)); + end -= offset; + move->fstart += offset; + move->fend += offset; + } + } + + if (move->tstart == move->tend || move->fstart == move->tstart) { + move = m_moves.erase(move); + --move; + } + } + for (; move != m_moves.end(); ++move) { + move->fstart -= count; + move->fend -= count; + move->tstart -= count; + move->tend -= count; + } + + if (start != end) + removeChanges.append(Remove(start, end)); + + foreach (const Remove &r, removeChanges) { + int start = r.start; + int end = r.end; + + QVector::iterator insert = m_inserts.end() - 1; + for (const int count = end - start; insert != m_inserts.begin() - 1 && insert->start >= end; --insert) { + insert->start -= count; + insert->end -= count; + } + for (; insert != m_inserts.begin() - 1 && insert->end > start; --insert) { + const int removeCount = qMin(insert->end, end) - qMax(insert->start, start); + insert->end -= removeCount; + if (insert->start == insert->end) { + insert = m_inserts.erase(insert); + } else if (start < insert->start) { + insert->end -= insert->start - start; + insert->start = start; + } else { + start -= insert->count(); + end -= insert->count(); + } + end -= removeCount; + if (start == end) + return; + } + // Adjust the index to compensate for any inserts prior to the remove position.. + for (; insert != m_inserts.begin() - 1; --insert) { + start -= insert->count(); + end -= insert->count(); + } + + // Removed signals. + QVector::iterator remove = m_removes.begin(); + for (; remove != m_removes.end(); ++remove) { + if (end < remove->start) { + remove = m_removes.insert(remove, Remove(start, end)); + break; + } else if (start <= remove->start) { + remove->end += end - remove->start; + remove->start = start; + + QVector::iterator rbegin = remove; + QVector::iterator rend = ++rbegin; + for (; rend != m_removes.end() && rend->start <= remove->end; ++rend) + remove->end += rend->count(); + if (rbegin != rend) { + remove = m_removes.erase(rbegin, rend); + } + break; + } + } + if (remove != m_removes.end()) { + const int count = end - start; + for (++remove; remove != m_removes.end(); ++remove) { + remove->start -= count; + remove->end -= count; + } + } else { + m_removes.append(Remove(start, end)); + } + } +} + +void QDeclarativeChangeSet::insertMove(int start, int end, int to) +{ + QVector insertChanges; + QVector moveChanges; + + int fStart = start; + int fTo = to; + int fEnd = end; + int &bStart = fTo; + int bEnd = to + end - start; + + if (start > to) { + qSwap(fStart, bStart); + qSwap(fEnd, bEnd); + } + + // Inserted signals. + QVector::iterator insert = m_inserts.begin(); + if (start < to) { + for (; insert != m_inserts.end() && fStart >= insert->end; ++insert) {} + for (; insert != m_inserts.end() && fEnd > insert->start; ++insert) { + const int removeCount = qMin(insert->end, fEnd) - qMax(insert->start, fStart); + const int relativeStart = fStart - insert->start; + const int relativeEnd = qMax(0, fEnd - insert->end); + + insert->end -= removeCount; + if (insert->start == insert->end) { + insert = m_inserts.erase(insert); + --insert; + } + + if (relativeStart < 0) { + moveChanges.append(Move(fStart, fStart - relativeStart, fTo + relativeEnd)); + fTo -= relativeStart; + } + + fTo += removeCount; + insertChanges.append(Insert(bEnd - removeCount, bEnd)); + } + } else { + for (; insert != m_inserts.end() && bStart >= insert->end; ++insert) {} + for (; insert != m_inserts.end() && bEnd > insert->start; ++insert) { + const int removeCount = qMin(insert->end, bEnd) - qMax(insert->start, bStart); + const int relativeStart = bStart - insert->start; + + insert->start += removeCount; + if (insert->start == insert->end) { + insert->start = fStart; + insert->end = insert->start + removeCount; + } else { + insert = m_inserts.insert(insert, Insert(fStart, fStart + removeCount)); + ++insert; + } + if (relativeStart < 0) { + moveChanges.append(Move(fStart, fStart - relativeStart, fTo + removeCount)); + fStart -= relativeStart; + fTo -= relativeStart; + } + fStart += removeCount; + fTo += removeCount; + } + } + + if (fTo != bEnd) + moveChanges.append(Move(fStart, fStart + bEnd - fTo, fTo)); + + QVector::iterator it = insertChanges.begin(); + for (insert = m_inserts.begin(); it != insertChanges.end() && insert != m_inserts.end();++insert) { + if (it->start < insert->start) { + insert = m_inserts.insert(insert, *it); + ++it; + } else if (it->start <= insert->end) { + insert->end += it->count(); + ++it; + } + } + for (; it != insertChanges.end(); ++it) + m_inserts.append(*it); + + // Insert queued moved signals ordered by destination position. + QVector::iterator move = m_moves.begin(); + if (start > to) { + for (QVector::iterator it = moveChanges.begin(); it != moveChanges.end(); ++it) { + it->fend += it->tstart - it->fstart; + it->tend -=it->tstart - it->fstart; + qSwap(it->fstart, it->tstart); + for (; move != m_moves.end() && it->to >= qMin(move->fstart, move->tstart); ++move) {} + move = m_moves.insert(move, *it); + } + } else { + for (QVector::iterator it = moveChanges.begin(); it != moveChanges.end(); ++it) { + for (; move != m_moves.end() && it->start >= qMin(move->fstart, move->tstart); ++move) {} + move = m_moves.insert(move, *it); + } + } +} + +void QDeclarativeChangeSet::insertChange(int start, int end) +{ + QVector filteredChanges; + + // Inserted signals (don't emit change signals on new items). + QVector::iterator insert = m_inserts.begin(); + for (; insert != m_inserts.end() && start >= insert->end; ++insert) {} + for (; insert != m_inserts.end() && end > insert->start; ++insert) { + if (start < insert->start) + filteredChanges.append(Change(start, insert->start)); + start = insert->end; + } + if (start < end) + filteredChanges.append(Change(start, end)); + + // Find the union of the existing and filtered sets of change signals. + QVector::iterator change = m_changes.begin(); + for (QVector::iterator it = filteredChanges.begin(); it != filteredChanges.end(); ++it) { + for (; change != m_changes.end() && change->end < it->start; ++change) {} + if (change == m_changes.end() || change->start > it->end) { + change = m_changes.insert(change, *it); + } else { + if (it->start < change->start) + change->start = it->start; + + if (it->end > change->end) { + change->end = it->end; + QVector::iterator rbegin = change; + QVector::iterator rend = ++rbegin; + for (; rend != m_changes.end() && rend->start <= change->end; ++rend) { + if (rend->end > change->end) + change->end = rend->end; + } + if (rbegin != rend) { + change = m_changes.erase(rbegin, rend); + --change; + } + } + } + } + +} + +QDebug operator <<(QDebug debug, const QDeclarativeChangeSet &set) +{ + foreach (const QDeclarativeChangeSet::Remove &remove, set.removes()) + debug.nospace() << "QDeclarativeChangeSet::Remove(" << remove.start << "," << remove.end << ")"; + foreach (const QDeclarativeChangeSet::Insert &insert, set.inserts()) + debug.nospace() << "QDeclarativeChangeSet::Insert(" << insert.start << "," << insert.end << ")"; + foreach (const QDeclarativeChangeSet::Move &move, set.moves()) + debug.nospace() << "QDeclarativeChangeSet::Move(" << move.start << "," << move.end << "," << move.to << ")"; + foreach (const QDeclarativeChangeSet::Change &change, set.changes()) + debug.nospace() << "QDeclarativeChangeSet::Change(" << change.start << "," << change.end << ")"; + return debug; +} + diff --git a/src/declarative/util/qdeclarativechangeset_p.h b/src/declarative/util/qdeclarativechangeset_p.h new file mode 100644 index 0000000000..f911e658ea --- /dev/null +++ b/src/declarative/util/qdeclarativechangeset_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECHANGESET_P_H +#define QDECLARATIVECHANGESET_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QDeclarativeChangeSet +{ +public: + struct Insert + { + Insert() {} + Insert(int start, int end) : start(start), end(end) {} + + int count() const { return end - start; } + + int start; + int end; + }; + + struct Remove + { + Remove() {} + Remove(int start, int end) : start(start), end(end) {} + + int count() const { return end - start; } + + int start; + int end; + }; + + struct Move + { + Move() {} + Move(int start, int end, int to) : fstart(start), fend(end), tstart(to), tend(to + end - start) {} + + int minimum() const { return qMin(fstart, tstart); } + int maximum() const { return qMax(fend, tend); } + int count() const { return fend - fstart; } + + union { + int start; + int fstart; + }; + union { + int end; + int fend; + }; + union { + int to; + int tstart; + }; + int tend; + }; + + struct Change + { + Change() {} + Change(int start, int end) : start(start), end(end) {} + + int count() const { return end - start; } + + int start; + int end; + }; + + const QVector &removes() const { return m_removes; } + const QVector &inserts() const { return m_inserts; } + const QVector &moves() const { return m_moves; } + const QVector &changes() const {return m_changes; } + + void insertInsert(int start, int end); + void insertRemove(int start, int end); + void insertMove(int start, int end, int to); + void insertChange(int start, int end); + + void appendInsert(int start, int end) { m_inserts.append(Insert(start, end)); } + void appendRemove(int start, int end) { m_removes.append(Remove(start, end)); } + void appendMove(int start, int end, int to) { m_moves.append(Move(start, end, to)); } + void appendChange(int start, int end) { m_changes.append(Change(start, end)); } + + void append(const QVector &inserts) { m_inserts += inserts; } + void append(const QVector &removes) { m_removes += removes; } + void append(const QVector &moves) { m_moves += moves; } + void append(const QVector &changes) { m_changes += changes; } + + void clear() + { + m_removes.clear(); + m_inserts.clear(); + m_moves.clear(); + m_changes.clear(); + } + +private: + QVector m_removes; + QVector m_inserts; + QVector m_moves; + QVector m_changes; +}; + +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeChangeSet &change); + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri index 5bc8b117c0..4bd1f1bb1b 100644 --- a/src/declarative/util/util.pri +++ b/src/declarative/util/util.pri @@ -28,6 +28,7 @@ SOURCES += \ $$PWD/qdeclarativefontloader.cpp \ $$PWD/qdeclarativestyledtext.cpp \ $$PWD/qdeclarativelistmodelworkeragent.cpp \ + $$PWD/qdeclarativechangeset.cpp \ $$PWD/qlistmodelinterface.cpp HEADERS += \ @@ -63,6 +64,7 @@ HEADERS += \ $$PWD/qdeclarativefontloader_p.h \ $$PWD/qdeclarativestyledtext_p.h \ $$PWD/qdeclarativelistmodelworkeragent_p.h \ + $$PWD/qdeclarativechangeset_p.h \ $$PWD/qlistmodelinterface_p.h contains(QT_CONFIG, xmlpatterns) { diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index 20fa8548fd..9ef9ce8a16 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -34,6 +34,7 @@ contains(QT_CONFIG, private_tests) { qdeclarativebehaviors \ qdeclarativebinding \ qdeclarativeborderimage \ + qdeclarativechangeset \ qdeclarativeconnection \ qdeclarativedebug \ qdeclarativedebugclient \ diff --git a/tests/auto/declarative/qdeclarativechangeset/qdeclarativechangeset.pro b/tests/auto/declarative/qdeclarativechangeset/qdeclarativechangeset.pro new file mode 100644 index 0000000000..1c9a3c00f9 --- /dev/null +++ b/tests/auto/declarative/qdeclarativechangeset/qdeclarativechangeset.pro @@ -0,0 +1,17 @@ +load(qttest_p4) +contains(QT_CONFIG,declarative): QT += declarative gui +macx:CONFIG -= app_bundle + +SOURCES += tst_qdeclarativechangeset.cpp + +symbian: { + importFiles.files = data + importFiles.path = . + DEPLOYMENT += importFiles +} else { + DEFINES += SRCDIR=\\\"$$PWD\\\" +} + +CONFIG += parallel_test + +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativechangeset/tst_qdeclarativechangeset.cpp b/tests/auto/declarative/qdeclarativechangeset/tst_qdeclarativechangeset.cpp new file mode 100644 index 0000000000..018711ac64 --- /dev/null +++ b/tests/auto/declarative/qdeclarativechangeset/tst_qdeclarativechangeset.cpp @@ -0,0 +1,614 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +#define VERIFY_EXPECTED_OUTPUT + + + +class tst_qdeclarativemodelchange : public QObject +{ + Q_OBJECT +public: + struct Signal + { + int start; + int end; + int to; + + bool isInsert() const { return to == -1; } + bool isRemove() const { return to == -2; } + bool isMove() const { return to >= 0; } + bool isChange() const { return to == -3; } + }; + + static Signal Insert(int start, int end) { Signal signal = { start, end, -1 }; return signal; } + static Signal Remove(int start, int end) { Signal signal = { start, end, -2 }; return signal; } + static Signal Move(int start, int end, int to) { Signal signal = { start, end, to }; return signal; } + static Signal Change(int start, int end) { Signal signal = { start, end, -3 }; return signal; } + + typedef QVector SignalList; + + +#ifdef VERIFY_EXPECTED_OUTPUT + + template + void move(int from, int to, int n, T *items) + { + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + T replaced; + int i=0; + typename T::ConstIterator it=items->begin(); it += from+n; + for (; ibegin(); it += from; + for (; ibegin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } + + QVector applyChanges(const QVector &list, const QVector &changes) + { + QVector alteredList = list; + foreach (const Signal &signal, changes) { + if (signal.isInsert()) { + alteredList.insert(signal.start, signal.end - signal.start, 100); + } else if (signal.isRemove()) { + alteredList.erase(alteredList.begin() + signal.start, alteredList.begin() + signal.end); + } else if (signal.isMove()) { + move(signal.start, signal.to, signal.end - signal.start, &alteredList); + } else if (signal.isChange()) { + for (int i = signal.start; i < signal.end; ++i) { + if (alteredList[i] < 100) + alteredList[i] = 100; + } + } + } + return alteredList; + } + +#endif + +private slots: + void sequence_data(); + void sequence(); +}; + +bool operator ==(const tst_qdeclarativemodelchange::Signal &left, const tst_qdeclarativemodelchange::Signal &right) { + return left.start == right.start && left.end == right.end && left.to == right.to; } + + +QDebug operator <<(QDebug debug, const tst_qdeclarativemodelchange::Signal &signal) +{ + if (signal.isInsert()) + debug.nospace() << "Insert(" << signal.start << "," << signal.end << ")"; + else if (signal.isRemove()) + debug.nospace() << "Remove(" << signal.start << "," << signal.end << ")"; + else if (signal.isMove()) + debug.nospace() << "Move(" << signal.start << "," << signal.end << "," << signal.to << ")"; + else if (signal.isChange()) + debug.nospace() << "Change(" << signal.start << "," << signal.end << ")"; + return debug; +} + +Q_DECLARE_METATYPE(tst_qdeclarativemodelchange::SignalList) + +void tst_qdeclarativemodelchange::sequence_data() +{ + QTest::addColumn("input"); + QTest::addColumn("output"); + + // Insert + QTest::newRow("i(12-17)") + << (SignalList() << Insert(12, 17)) + << (SignalList() << Insert(12, 17)); + QTest::newRow("i(2-5),i(12-17)") + << (SignalList() << Insert(2, 5) << Insert(12, 17)) + << (SignalList() << Insert(2, 5) << Insert(12, 17)); + QTest::newRow("i(12-17),i(2-5)") + << (SignalList() << Insert(12, 17) << Insert(2, 5)) + << (SignalList() << Insert(2, 5) << Insert(15, 20)); + QTest::newRow("i(12-17),i(12-15)") + << (SignalList() << Insert(12, 17) << Insert(12, 15)) + << (SignalList() << Insert(12, 20)); + QTest::newRow("i(12-17),i(17-20)") + << (SignalList() << Insert(12, 17) << Insert(17, 20)) + << (SignalList() << Insert(12, 20)); + QTest::newRow("i(12-17),i(15-18)") + << (SignalList() << Insert(12, 17) << Insert(15, 18)) + << (SignalList() << Insert(12, 20)); + + // Remove + QTest::newRow("r(3-12)") + << (SignalList() << Remove(3, 12)) + << (SignalList() << Remove(3, 12)); + QTest::newRow("r(3-7),r(3-5)") + << (SignalList() << Remove(3, 7) << Remove(3, 5)) + << (SignalList() << Remove(3, 9)); + QTest::newRow("r(4-3),r(14-19)") + << (SignalList() << Remove(4, 7) << Remove(14, 19)) + << (SignalList() << Remove(4, 7) << Remove(14, 19)); + QTest::newRow("r(14-19),r(4-7)") + << (SignalList() << Remove(14, 19) << Remove(4, 7)) + << (SignalList() << Remove(4, 7) << Remove(11, 16)); + QTest::newRow("r(4-7),r(2-11)") + << (SignalList() << Remove(4, 7) << Remove(2, 11)) + << (SignalList() << Remove(2, 14)); + + // Move + QTest::newRow("m(8-10,10)") + << (SignalList() << Move(8, 10, 10)) + << (SignalList() << Move(8, 10, 10)); + // No merging of moves yet. +// QTest::newRow("m(5-7,13),m(5-8,12)") +// << (SignalList() << Move(5, 7, 13) << Move(5, 8, 12)) +// << (SignalList() << Move(5, 10, 10)); + + // Change + QTest::newRow("c(4-9)") + << (SignalList() << Change(4, 9)) + << (SignalList() << Change(4, 9)); + QTest::newRow("c(4-9),c(12-14)") + << (SignalList() << Change(4, 9) << Change(12, 14)) + << (SignalList() << Change(4, 9) << Change(12, 14)); + QTest::newRow("c(12-14),c(4-9)") + << (SignalList() << Change(12, 14) << Change(4, 9)) + << (SignalList() << Change(4, 9) << Change(12, 14)); + QTest::newRow("c(4-9),c(2-4)") + << (SignalList() << Change(4, 9) << Change(2, 4)) + << (SignalList() << Change(2, 9)); + QTest::newRow("c(4-9),c(9-11)") + << (SignalList() << Change(4, 9) << Change(9, 11)) + << (SignalList() << Change(4, 11)); + QTest::newRow("c(4-9),c(3-5)") + << (SignalList() << Change(4, 9) << Change(3, 5)) + << (SignalList() << Change(3, 9)); + QTest::newRow("c(4-9),c(8-10)") + << (SignalList() << Change(4, 9) << Change(8, 10)) + << (SignalList() << Change(4, 10)); + QTest::newRow("c(4-9),c(3-5)") + << (SignalList() << Change(4, 9) << Change(3, 5)) + << (SignalList() << Change(3, 9)); + QTest::newRow("c(4-9),c(2,11)") + << (SignalList() << Change(4, 9) << Change(2, 11)) + << (SignalList() << Change(2, 11)); + QTest::newRow("c(4-9),c(12-15),c(8-14)") + << (SignalList() << Change(4, 9) << Change(12, 15) << Change(8, 14)) + << (SignalList() << Change(4, 15)); + + // Insert, then remove. + QTest::newRow("i(12-18),r(12-18)") + << (SignalList() << Insert(12, 18) << Remove(12, 18)) + << (SignalList()); + QTest::newRow("i(12-18),r(10-14)") + << (SignalList() << Insert(12, 18) << Remove(10, 14)) + << (SignalList() << Remove(10, 12) << Insert(10, 14)); + QTest::newRow("i(12-18),r(16-20)") + << (SignalList() << Insert(12, 18) << Remove(16, 20)) + << (SignalList() << Remove(12, 14) << Insert(12, 16)); + QTest::newRow("i(12-18),r(13-17)") + << (SignalList() << Insert(12, 18) << Remove(13, 17)) + << (SignalList() << Insert(12, 14)); + QTest::newRow("i(12-18),r(14,18)") + << (SignalList() << Insert(12, 18) << Remove(14, 18)) + << (SignalList() << Insert(12, 14)); + QTest::newRow("i(12-18),r(12-16)") + << (SignalList() << Insert(12, 18) << Remove(12, 16)) + << (SignalList() << Insert(12, 14)); + QTest::newRow("i(12-18),r(11-19)") + << (SignalList() << Insert(12, 18) << Remove(11, 19)) + << (SignalList() << Remove(11, 13)); + QTest::newRow("i(12-18),r(8-12)") + << (SignalList() << Insert(12, 18) << Remove(8, 12)) + << (SignalList() << Remove(8, 12) << Insert(8, 14)); + QTest::newRow("i(12-18),r(2-6)") + << (SignalList() << Insert(12, 18) << Remove(2, 6)) + << (SignalList() << Remove(2, 6) << Insert(8, 14)); + QTest::newRow("i(12-18),r(18-22)") + << (SignalList() << Insert(12, 18) << Remove(18, 22)) + << (SignalList() << Remove(12, 16) << Insert(12, 18)); + QTest::newRow("i(12-18),r(20-24)") + << (SignalList() << Insert(12, 18) << Remove(20, 24)) + << (SignalList() << Remove(14, 18) << Insert(12, 18)); + + // Insert, then move + QTest::newRow("i(12-18),m(12-18,5)") + << (SignalList() << Insert(12, 18) << Move(12, 18, 5)) + << (SignalList() << Insert(5, 11)); + QTest::newRow("i(12-18),m(10-14,5)") + << (SignalList() << Insert(12, 18) << Move(10, 14, 5)) + << (SignalList() << Insert(5, 7) << Insert(14, 18) << Move(12, 14, 5)); + QTest::newRow("i(12-18),m(16-20,5)") + << (SignalList() << Insert(12, 18) << Move(16, 20, 5)) + << (SignalList() << Insert(5, 7) << Insert(14, 18) << Move(18, 20, 7)); + QTest::newRow("i(12-18),m(13-17,5)") + << (SignalList() << Insert(12, 18) << Move(13, 17, 5)) + << (SignalList() << Insert(5, 9) << Insert(16, 18)); + QTest::newRow("i(12-18),m(14-18,5)") + << (SignalList() << Insert(12, 18) << Move(14, 18, 5)) + << (SignalList() << Insert(5, 9) << Insert(16, 18)); + QTest::newRow("i(12-18),m(12-16,5)") + << (SignalList() << Insert(12, 18) << Move(12, 16, 5)) + << (SignalList() << Insert(5, 9) << Insert(16, 18)); + QTest::newRow("i(12-18),m(11-19,5)") + << (SignalList() << Insert(12, 18) << Move(11, 19, 5)) + << (SignalList() << Insert(5, 11) << Move(17, 18, 5) << Move(18, 19, 12)); + QTest::newRow("i(12-18),m(8-12,5)") + << (SignalList() << Insert(12, 18) << Move(8, 12, 5)) + << (SignalList() << Insert(12, 18) << Move(8, 12, 5)); + QTest::newRow("i(12-18),m(2-6,5)") + << (SignalList() << Insert(12, 18) << Move(2, 6, 5)) + << (SignalList() << Insert(12, 18) << Move(2, 6, 5)); + QTest::newRow("i(12-18),m(18-22,5)") + << (SignalList() << Insert(12, 18) << Move(18, 22, 5)) + << (SignalList() << Insert(12, 18) << Move(18, 22, 5)); + QTest::newRow("i(12-18),m(20-24,5)") + << (SignalList() << Insert(12, 18) << Move(20, 24, 5)) + << (SignalList() << Insert(12, 18) << Move(20, 24, 5)); + + QTest::newRow("i(12-18),m(5-13,11)") + << (SignalList() << Insert(12, 18) << Move(5, 13, 11)) + << (SignalList() << Insert(12, 17) << Insert(18, 19) << Move(5, 12, 11)); + + QTest::newRow("i(12-18),m(12-18,23)") + << (SignalList() << Insert(12, 18) << Move(12, 18, 23)) + << (SignalList() << Insert(23, 29)); + QTest::newRow("i(12-18),m(10-14,23)") + << (SignalList() << Insert(12, 18) << Move(10, 14, 23)) + << (SignalList() << Insert(12, 16) << Insert(25, 27) << Move(10, 12, 23)); + QTest::newRow("i(12-18),m(16-20,23)") + << (SignalList() << Insert(12, 18) << Move(16, 20, 23)) + << (SignalList() << Insert(12, 16) << Insert(25, 27) << Move(16, 18, 25)); + QTest::newRow("i(12-18),m(13-17,23)") + << (SignalList() << Insert(12, 18) << Move(13, 17, 23)) + << (SignalList() << Insert(12, 14) << Insert(23, 27)); + QTest::newRow("i(12-18),m(14-18,23)") + << (SignalList() << Insert(12, 18) << Move(14, 18, 23)) + << (SignalList() << Insert(12, 14) << Insert(23, 27)); + QTest::newRow("i(12-18),m(12-16,23)") + << (SignalList() << Insert(12, 18) << Move(12, 16, 23)) + << (SignalList() << Insert(12, 14) << Insert(23, 27)); + QTest::newRow("i(12-18),m(11-19,23)") + << (SignalList() << Insert(12, 18) << Move(11, 19, 23)) + << (SignalList() << Insert(25, 31) << Move(11, 12, 24) << Move(11, 12, 30)); + QTest::newRow("i(12-18),m(8-12,23)") + << (SignalList() << Insert(12, 18) << Move(8, 12, 23)) + << (SignalList() << Insert(12, 18) << Move(8, 12, 23)); + QTest::newRow("i(12-18),m(2-6,23)") + << (SignalList() << Insert(12, 18) << Move(2, 6, 23)) + << (SignalList() << Insert(12, 18) << Move(2, 6, 23)); + QTest::newRow("i(12-18),m(18-22,23)") + << (SignalList() << Insert(12, 18) << Move(18, 22, 23)) + << (SignalList() << Insert(12, 18) << Move(18, 22, 23)); + QTest::newRow("i(12-18),m(20-24,23)") + << (SignalList() << Insert(12, 18) << Move(20, 24, 23)) + << (SignalList() << Insert(12, 18) << Move(20, 24, 23)); + + QTest::newRow("i(12-18),m(11-21,23)") + << (SignalList() << Insert(12, 18) << Move(11, 21, 23)) + << (SignalList() << Insert(27, 33) << Move(11, 12, 26) << Move(11, 14, 30)); + + // Insert, then change + QTest::newRow("i(12-18),c(12-16)") + << (SignalList() << Insert(12, 18) << Change(12, 6)) + << (SignalList() << Insert(12, 18)); + QTest::newRow("i(12-18),c(10-14)") + << (SignalList() << Insert(12, 18) << Change(10, 16)) + << (SignalList() << Insert(12, 18) << Change(10, 12)); + QTest::newRow("i(12-18),c(16-20)") + << (SignalList() << Insert(12, 18) << Change(16, 20)) + << (SignalList() << Insert(12, 18) << Change(18, 20)); + QTest::newRow("i(12-18),c(13-17)") + << (SignalList() << Insert(12, 18) << Change(13, 17)) + << (SignalList() << Insert(12, 18)); + QTest::newRow("i(12-18),c(14-18)") + << (SignalList() << Insert(12, 18) << Change(14, 18)) + << (SignalList() << Insert(12, 18)); + QTest::newRow("i(12-18),c(12-16)") + << (SignalList() << Insert(12, 18) << Change(12, 16)) + << (SignalList() << Insert(12, 18)); + QTest::newRow("i(12-18),c(11-19)") + << (SignalList() << Insert(12, 18) << Change(11, 19)) + << (SignalList() << Insert(12, 18) << Change(11, 12) << Change(18, 19)); + QTest::newRow("i(12-18),c(8-12)") + << (SignalList() << Insert(12, 18) << Change(8, 12)) + << (SignalList() << Insert(12, 18) << Change(8, 12)); + QTest::newRow("i(12-18),c(2-6)") + << (SignalList() << Insert(12, 18) << Change(2, 6)) + << (SignalList() << Insert(12, 18) << Change(2, 6)); + QTest::newRow("i(12-18),c(18-22)") + << (SignalList() << Insert(12, 18) << Change(18, 22)) + << (SignalList() << Insert(12, 18) << Change(18, 22)); + QTest::newRow("i(12-18),c(20-24)") + << (SignalList() << Insert(12, 18) << Change(20, 24)) + << (SignalList() << Insert(12, 18) << Change(20, 24)); + + // Remove, then insert + QTest::newRow("r(12-18),i(12-18)") + << (SignalList() << Remove(12, 18) << Insert(12, 18)) + << (SignalList() << Remove(12, 18) << Insert(12, 18)); + QTest::newRow("r(12-18),i(10-14)") + << (SignalList() << Remove(12, 18) << Insert(10, 14)) + << (SignalList() << Remove(12, 18) << Insert(10, 14)); + QTest::newRow("r(12-18),i(16-20)") + << (SignalList() << Remove(12, 18) << Insert(16, 20)) + << (SignalList() << Remove(12, 18) << Insert(16, 20)); + QTest::newRow("r(12-18),i(13-17)") + << (SignalList() << Remove(12, 18) << Insert(13, 17)) + << (SignalList() << Remove(12, 18) << Insert(13, 17)); + QTest::newRow("r(12-18),i(14-18)") + << (SignalList() << Remove(12, 18) << Insert(14, 18)) + << (SignalList() << Remove(12, 18) << Insert(14, 18)); + QTest::newRow("r(12-18),i(12-16)") + << (SignalList() << Remove(12, 18) << Insert(12, 16)) + << (SignalList() << Remove(12, 18) << Insert(12, 16)); + QTest::newRow("r(12-18),i(11-19)") + << (SignalList() << Remove(12, 18) << Insert(11, 19)) + << (SignalList() << Remove(12, 18) << Insert(11, 19)); + QTest::newRow("i(12-18),r(8-12)") + << (SignalList() << Remove(12, 18) << Insert(8, 12)) + << (SignalList() << Remove(12, 18) << Insert(8, 12)); + QTest::newRow("i(12-18),r(2-6)") + << (SignalList() << Remove(12, 18) << Insert(2, 6)) + << (SignalList() << Remove(12, 18) << Insert(2, 6)); + QTest::newRow("i(12-18),r(18-22)") + << (SignalList() << Remove(12, 18) << Insert(18, 22)) + << (SignalList() << Remove(12, 18) << Insert(18, 22)); + QTest::newRow("i(12-18),r(20-24)") + << (SignalList() << Remove(12, 18) << Insert(20, 24)) + << (SignalList() << Remove(12, 18) << Insert(20, 24)); + + // Move, then insert + QTest::newRow("m(12-18,5),i(12-18)") + << (SignalList() << Move(12, 18, 5) << Insert(12, 18)) + << (SignalList() << Insert(6, 12) << Move(18, 24, 5)); + QTest::newRow("m(12-18,5),i(10-14)") + << (SignalList() << Move(12, 18, 5) << Insert(10, 14)) + << (SignalList() << Insert(5, 9) << Move(16, 21, 5) << Move(21, 22, 14)); + QTest::newRow("m(12-18,5),i(16-20)") + << (SignalList() << Move(12, 18, 5) << Insert(16, 20)) + << (SignalList() << Insert(10, 14) << Move(16, 22, 5)); + QTest::newRow("m(12-18,5),i(13-17)") + << (SignalList() << Move(12, 18, 5) << Insert(13, 17)) + << (SignalList() << Insert(7, 11) << Move(16, 22, 5)); + QTest::newRow("m(12-18,5),i(14-18)") + << (SignalList() << Move(12, 18, 5) << Insert(14, 18)) + << (SignalList() << Insert(8, 12) << Move(16, 22, 5)); + QTest::newRow("m(12-18,5),i(12-16)") + << (SignalList() << Move(12, 18, 5) << Insert(12, 16)) + << (SignalList() << Insert(6, 10) << Move(16, 22, 5)); + QTest::newRow("m(12-18,5),i(11-19)") + << (SignalList() << Move(12, 18, 5) << Insert(11, 19)) + << (SignalList() << Insert(5, 13) << Move(20, 26, 5)); + QTest::newRow("m(12-18,5),i(8-12)") + << (SignalList() << Move(12, 18, 5) << Insert(8, 12)) + << (SignalList() << Insert(5, 9) << Move(16, 19, 5) << Move(19, 22, 12)); + QTest::newRow("m(12-18,5),i(2-6)") + << (SignalList() << Move(12, 18, 5) << Insert(2, 6)) + << (SignalList() << Insert(2, 6) << Move(16, 22, 9)); + QTest::newRow("m(12-18,5),i(18-22)") + << (SignalList() << Move(12, 18, 5) << Insert(18, 22)) + << (SignalList() << Insert(18, 22) << Move(12, 18, 5)); + QTest::newRow("m(12-18,5),i(20-24)") + << (SignalList() << Move(12, 18, 5) << Insert(20, 24)) + << (SignalList() << Insert(20, 24) << Move(12, 18, 5)); + + QTest::newRow("m(12-18,23),i(12-18)") + << (SignalList() << Move(12, 18, 23) << Insert(12, 18)) + << (SignalList() << Insert(12, 18) << Move(18, 24, 29)); + QTest::newRow("m(12-18,23),i(10-14)") + << (SignalList() << Move(12, 18, 23) << Insert(10, 14)) + << (SignalList() << Insert(10, 14) << Move(16, 22, 27)); + QTest::newRow("m(12-18,23),i(16-20)") + << (SignalList() << Move(12, 18, 23) << Insert(16, 20)) + << (SignalList() << Insert(22, 26) << Move(12, 18, 27)); + QTest::newRow("m(12-18,23),i(13-17)") + << (SignalList() << Move(12, 18, 23) << Insert(13, 17)) + << (SignalList() << Insert(19, 23) << Move(12, 18, 27)); + QTest::newRow("m(12-18,23),i(14-18)") + << (SignalList() << Move(12, 18, 23) << Insert(14, 18)) + << (SignalList() << Insert(20, 24) << Move(12, 18, 27)); + QTest::newRow("m(12-18,23),i(12-16)") + << (SignalList() << Move(12, 18, 23) << Insert(12, 16)) + << (SignalList() << Insert(12, 16) << Move(16, 22, 27)); + QTest::newRow("m(12-18,23),i(11-19)") + << (SignalList() << Move(12, 18, 23) << Insert(11, 19)) + << (SignalList() << Insert(11, 19) << Move(20, 26, 31)); + QTest::newRow("m(12-18,23),i(8-12)") + << (SignalList() << Move(12, 18, 23) << Insert(8, 12)) + << (SignalList() << Insert(8, 12) << Move(16, 22, 27)); + QTest::newRow("m(12-18,23),i(2-6)") + << (SignalList() << Move(12, 18, 23) << Insert(2, 6)) + << (SignalList() << Insert(2, 6) << Move(16, 22, 27)); + QTest::newRow("m(12-18,23),i(18-22)") + << (SignalList() << Move(12, 18, 23) << Insert(18, 22)) + << (SignalList() << Insert(24, 28) << Move(12, 18, 27)); + QTest::newRow("m(12-18,23),i(20-24)") + << (SignalList() << Move(12, 18, 23) << Insert(20, 24)) + << (SignalList() << Insert(26, 30) << Move(12, 18, 27)); + + // Move, then remove + QTest::newRow("m(12-18,5),r(12-18)") + << (SignalList() << Move(12, 18, 5) << Remove(12, 18)) + << (SignalList() << Remove(6, 12) << Move(6, 12, 5)); + QTest::newRow("m(12-18,5),r(10-14)") + << (SignalList() << Move(12, 18, 5) << Remove(10, 14)) + << (SignalList() << Remove(5, 8) << Remove(14, 15) << Move(9, 14, 5)); + QTest::newRow("m(12-18,5),r(16-20)") + << (SignalList() << Move(12, 18, 5) << Remove(16, 20)) + << (SignalList() << Remove(10, 12) << Remove(16, 18) << Move(10, 16, 5)); + QTest::newRow("m(12-18,5),r(13-17)") + << (SignalList() << Move(12, 18, 5) << Remove(13, 17)) + << (SignalList() << Remove(7, 11) << Move(8, 14, 5)); + QTest::newRow("m(12-18,5),r(14-18)") + << (SignalList() << Move(12, 18, 5) << Remove(14, 18)) + << (SignalList() << Remove(8, 12) << Move(8, 14, 5)); + QTest::newRow("m(12-18,5),r(12-16)") + << (SignalList() << Move(12, 18, 5) << Remove(12, 16)) + << (SignalList() << Remove(6, 10) << Move(8, 14, 5)); + QTest::newRow("m(12-18,5),r(11-19)") + << (SignalList() << Move(12, 18, 5) << Remove(11, 19)) + << (SignalList() << Remove(5, 12) << Remove(11, 12)); + QTest::newRow("m(12-18,5),r(8-12)") + << (SignalList() << Move(12, 18, 5) << Remove(8, 12)) + << (SignalList() << Remove(5, 6) << Remove(14, 17) << Move(11, 14, 5)); + QTest::newRow("m(12-18,5),r(2-6)") + << (SignalList() << Move(12, 18, 5) << Remove(2, 6)) + << (SignalList() << Remove(2, 5) << Remove(9, 10) << Move(9, 14, 2)); + QTest::newRow("m(12-18,5),r(6-10)") + << (SignalList() << Move(12, 18, 5) << Remove(6, 10)) + << (SignalList() << Remove(13, 17) << Move(12, 14, 5)); + QTest::newRow("m(12-18,5),r(18-22)") + << (SignalList() << Move(12, 18, 5) << Remove(18, 22)) + << (SignalList() << Remove(18, 22) << Move(12, 18, 5)); + QTest::newRow("m(12-18,5),r(20-24)") + << (SignalList() << Move(12, 18, 5) << Remove(20, 24)) + << (SignalList() << Remove(20, 24) << Move(12, 18, 5)); + + QTest::newRow("m(12-18,23),r(12-18)") + << (SignalList() << Move(12, 18, 23) << Remove(12, 18)) + << (SignalList() << Remove(18, 24) << Move(12, 18, 17)); + QTest::newRow("m(12-18,23),r(10-14)") + << (SignalList() << Move(12, 18, 23) << Remove(10, 14)) + << (SignalList() << Remove(10, 12) << Remove(16, 18) << Move(10, 16, 19)); + QTest::newRow("m(12-18,23),r(16-20)") + << (SignalList() << Move(12, 18, 23) << Remove(16, 20)) + << (SignalList() << Remove(22, 26) << Move(12, 18, 19)); + QTest::newRow("m(12-18,23),r(13-17)") + << (SignalList() << Move(12, 18, 23) << Remove(13, 17)) + << (SignalList() << Remove(19, 23) << Move(12, 18, 19)); + QTest::newRow("m(12-18,23),r(14-18)") + << (SignalList() << Move(12, 18, 23) << Remove(14, 18)) + << (SignalList() << Remove(20, 24) << Move(12, 18, 19)); + QTest::newRow("m(12-18,23),r(12-16)") + << (SignalList() << Move(12, 18, 23) << Remove(12, 16)) + << (SignalList() << Remove(18, 22) << Move(12, 18, 19)); + QTest::newRow("m(12-18,23),r(11-19)") + << (SignalList() << Move(12, 18, 23) << Remove(11, 19)) + << (SignalList() << Remove(11, 12) << Remove(17, 24) << Move(11, 17, 15)); + QTest::newRow("m(12-18,23),r(8-12)") + << (SignalList() << Move(12, 18, 23) << Remove(8, 12)) + << (SignalList() << Remove(8, 12) << Move(8, 14, 19)); + QTest::newRow("m(12-18,23),r(2-6)") + << (SignalList() << Move(12, 18, 23) << Remove(2, 6)) + << (SignalList() << Remove(2, 6) << Move(8, 14, 19)); + QTest::newRow("m(12-18,23),r(18-22)") + << (SignalList() << Move(12, 18, 23) << Remove(18, 22)) + << (SignalList() << Remove(24, 28) << Move(12, 18, 19)); + QTest::newRow("m(12-18,23),r(20-24)") + << (SignalList() << Move(12, 18, 23) << Remove(20, 24)) + << (SignalList() << Remove(12, 13) << Remove(25, 28) << Move(12, 17, 20)); +} + +void tst_qdeclarativemodelchange::sequence() +{ + QFETCH(SignalList, input); + QFETCH(SignalList, output); + + QDeclarativeChangeSet set; + + foreach (const Signal &signal, input) { + if (signal.isRemove()) + set.insertRemove(signal.start, signal.end); + else if (signal.isInsert()) + set.insertInsert(signal.start, signal.end); + else if (signal.isMove()) + set.insertMove(signal.start, signal.end, signal.to); + else if (signal.isChange()) + set.insertChange(signal.start, signal.end); + } + + SignalList changes; + foreach (const QDeclarativeChangeSet::Remove &remove, set.removes()) + changes << Remove(remove.start, remove.end); + foreach (const QDeclarativeChangeSet::Insert &insert, set.inserts()) + changes << Insert(insert.start, insert.end); + foreach (const QDeclarativeChangeSet::Move &move, set.moves()) + changes << Move(move.start, move.end, move.to); + foreach (const QDeclarativeChangeSet::Change &change, set.changes()) + changes << Change(change.start, change.end); + +#ifdef VERIFY_EXPECTED_OUTPUT + QVector list; + for (int i = 0; i < 40; ++i) + list.append(i); + QVector inputList = applyChanges(list, input); + QVector outputList = applyChanges(list, output); + if (outputList != inputList /* || changes != output*/) { + qDebug() << input; + qDebug() << output; + qDebug() << changes; + qDebug() << inputList; + qDebug() << outputList; + } else if (changes != output) { + qDebug() << output; + qDebug() << changes; + } + QCOMPARE(outputList, inputList); +#else + + if (changes != output) { + qDebug() << output; + qDebug() << changes; + } + +#endif + + QCOMPARE(changes, output); +} + + +QTEST_MAIN(tst_qdeclarativemodelchange) + +#include "tst_qdeclarativechangeset.moc" From 0eeb925aa5039ffddf8d623f250980fc4c97712e Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Mon, 20 Jun 2011 15:22:08 +1000 Subject: [PATCH 33/48] Add "DELETE" support to XMLHttpRequest --- .../qml/qdeclarativexmlhttprequest.cpp | 9 +++++--- .../data/send_ignoreData.qml | 2 +- .../data/send_ignoreData_DELETE.expect | 7 ++++++ ...PUT.expect => send_ignoreData_HEAD.expect} | 0 .../tst_qdeclarativexmlhttprequest.cpp | 23 +++++++++++++++++-- 5 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData_DELETE.expect rename tests/auto/declarative/qdeclarativexmlhttprequest/data/{send_ignoreData_PUT.expect => send_ignoreData_HEAD.expect} (100%) diff --git a/src/declarative/qml/qdeclarativexmlhttprequest.cpp b/src/declarative/qml/qdeclarativexmlhttprequest.cpp index 83b7d170a4..f74995ba0a 100644 --- a/src/declarative/qml/qdeclarativexmlhttprequest.cpp +++ b/src/declarative/qml/qdeclarativexmlhttprequest.cpp @@ -1163,10 +1163,12 @@ void QDeclarativeXMLHttpRequest::requestFromUrl(const QUrl &url) m_network = networkAccessManager()->get(request); else if (m_method == QLatin1String("HEAD")) m_network = networkAccessManager()->head(request); - else if(m_method == QLatin1String("POST")) + else if (m_method == QLatin1String("POST")) m_network = networkAccessManager()->post(request, m_data); - else if(m_method == QLatin1String("PUT")) + else if (m_method == QLatin1String("PUT")) m_network = networkAccessManager()->put(request, m_data); + else if (m_method == QLatin1String("DELETE")) + m_network = networkAccessManager()->deleteResource(request); QObject::connect(m_network, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64))); @@ -1447,7 +1449,8 @@ static QScriptValue qmlxmlhttprequest_open(QScriptContext *context, QScriptEngin if (method != QLatin1String("GET") && method != QLatin1String("PUT") && method != QLatin1String("HEAD") && - method != QLatin1String("POST")) + method != QLatin1String("POST") && + method != QLatin1String("DELETE")) THROW_DOM(SYNTAX_ERR, "Unsupported HTTP method type"); diff --git a/tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData.qml b/tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData.qml index c98555cd2f..442932be26 100644 --- a/tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData.qml +++ b/tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData.qml @@ -14,7 +14,7 @@ QtObject { // Test to the end x.onreadystatechange = function() { if (x.readyState == XMLHttpRequest.DONE) { - if (reqType == "HEAD") + if (reqType == "HEAD" || reqType == "DELETE") dataOK = (x.responseText == ""); else dataOK = (x.responseText == "QML Rocks!\n"); diff --git a/tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData_DELETE.expect b/tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData_DELETE.expect new file mode 100644 index 0000000000..b2d177aa20 --- /dev/null +++ b/tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData_DELETE.expect @@ -0,0 +1,7 @@ +DELETE /testdocument.html HTTP/1.1 +ACCEPT-LANGUAGE: en-US +Connection: Keep-Alive +Accept-Encoding: gzip +User-Agent: Mozilla/5.0 +Host: 127.0.0.1:14445 + diff --git a/tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData_PUT.expect b/tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData_HEAD.expect similarity index 100% rename from tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData_PUT.expect rename to tests/auto/declarative/qdeclarativexmlhttprequest/data/send_ignoreData_HEAD.expect diff --git a/tests/auto/declarative/qdeclarativexmlhttprequest/tst_qdeclarativexmlhttprequest.cpp b/tests/auto/declarative/qdeclarativexmlhttprequest/tst_qdeclarativexmlhttprequest.cpp index de16cf41f5..aad68c5926 100644 --- a/tests/auto/declarative/qdeclarativexmlhttprequest/tst_qdeclarativexmlhttprequest.cpp +++ b/tests/auto/declarative/qdeclarativexmlhttprequest/tst_qdeclarativexmlhttprequest.cpp @@ -497,7 +497,7 @@ void tst_qdeclarativexmlhttprequest::send_alreadySent() delete object; } -// Test that send for a GET or HEAD ignores data +// Test that sends for GET, HEAD and DELETE ignore data void tst_qdeclarativexmlhttprequest::send_ignoreData() { { @@ -522,7 +522,7 @@ void tst_qdeclarativexmlhttprequest::send_ignoreData() { TestHTTPServer server(SERVER_PORT); QVERIFY(server.isValid()); - QVERIFY(server.wait(TEST_FILE("send_ignoreData_PUT.expect"), + QVERIFY(server.wait(TEST_FILE("send_ignoreData_HEAD.expect"), TEST_FILE("send_ignoreData.reply"), QUrl())); @@ -537,6 +537,25 @@ void tst_qdeclarativexmlhttprequest::send_ignoreData() delete object; } + + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + QVERIFY(server.wait(TEST_FILE("send_ignoreData_DELETE.expect"), + TEST_FILE("send_ignoreData.reply"), + QUrl())); + + QDeclarativeComponent component(&engine, TEST_FILE("send_ignoreData.qml")); + QObject *object = component.beginCreate(engine.rootContext()); + QVERIFY(object != 0); + object->setProperty("reqType", "DELETE"); + object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool() == true); + + delete object; + } } // Test that send()'ing data works From 2329582577aab8ee35f93c193f3584e49eaeba34 Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Mon, 20 Jun 2011 10:53:34 +0200 Subject: [PATCH 34/48] Removed unnecessary calls to QSGNode::destroy(). --- src/declarative/scenegraph/coreapi/qsgnode.cpp | 18 ++++++++++++------ src/declarative/scenegraph/coreapi/qsgnode.h | 11 +++-------- .../scenegraph/coreapi/qsgrenderer.cpp | 8 ++++++++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/declarative/scenegraph/coreapi/qsgnode.cpp b/src/declarative/scenegraph/coreapi/qsgnode.cpp index fa720a3b72..bfe17ca72b 100644 --- a/src/declarative/scenegraph/coreapi/qsgnode.cpp +++ b/src/declarative/scenegraph/coreapi/qsgnode.cpp @@ -174,6 +174,17 @@ bool QSGNode::isSubtreeBlocked() const return m_subtreeGeometryCount == 0; } +/*! + \internal + Detaches the node from the scene graph and deletes any children it owns. + + This function is called from QSGNode's and QSGRootNode's destructor. It + should not be called explicitly in user code. QSGRootNode needs to call + destroy() because destroy() calls removeChildNode() which in turn calls + markDirty() which type-casts the node to QSGRootNode. This type-cast is not + valid at the time QSGNode's destructor is called because the node will + already be partially destroyed at that point. +*/ void QSGNode::destroy() { @@ -493,7 +504,6 @@ QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type) QSGBasicGeometryNode::~QSGBasicGeometryNode() { - destroy(); if (flags() & OwnsGeometry) delete m_geometry; } @@ -569,7 +579,6 @@ QSGGeometryNode::QSGGeometryNode() QSGGeometryNode::~QSGGeometryNode() { - destroy(); if (flags() & OwnsMaterial) delete m_material; if (flags() & OwnsOpaqueMaterial) @@ -731,7 +740,6 @@ QSGClipNode::QSGClipNode() QSGClipNode::~QSGClipNode() { - destroy(); } @@ -807,7 +815,6 @@ QSGTransformNode::QSGTransformNode() QSGTransformNode::~QSGTransformNode() { - destroy(); } @@ -881,7 +888,7 @@ QSGRootNode::~QSGRootNode() { while (!m_renderers.isEmpty()) m_renderers.last()->setRootNode(0); - destroy(); + destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode. } @@ -940,7 +947,6 @@ QSGOpacityNode::QSGOpacityNode() QSGOpacityNode::~QSGOpacityNode() { - destroy(); } diff --git a/src/declarative/scenegraph/coreapi/qsgnode.h b/src/declarative/scenegraph/coreapi/qsgnode.h index 2705958e04..cee6b76869 100644 --- a/src/declarative/scenegraph/coreapi/qsgnode.h +++ b/src/declarative/scenegraph/coreapi/qsgnode.h @@ -152,16 +152,11 @@ public: protected: QSGNode(NodeType type); - // When a node is destroyed, it will detach from the scene graph and the renderer will be - // notified about the change. If the node is detached in the base node's destructor, the - // renderer can't safely cast the node to its original type, since at this point it has been - // partly destroyed already. To solve this problem, all the node destructors must call a common - // destroy method. - - void destroy(); - private: + friend class QSGRootNode; + void init(); + void destroy(); QSGNode *m_parent; NodeType m_type; diff --git a/src/declarative/scenegraph/coreapi/qsgrenderer.cpp b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp index f56bf9a918..48c34d39dd 100644 --- a/src/declarative/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp @@ -293,6 +293,14 @@ void QSGRenderer::setClearColor(const QColor &color) m_clear_color = color; } +/*! + Updates internal data structures and emits the sceneGraphChanged() signal. + + If \a flags contains the QSGNode::DirtyNodeRemoved flag, the node might be + in the process of being destroyed. It is then not safe to downcast the node + pointer. +*/ + void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) { Q_UNUSED(node); From 447453f2dbe476964af423f35bb1876366923a9b Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Mon, 20 Jun 2011 11:55:18 +0200 Subject: [PATCH 35/48] Compile fix for MSVC. --- src/declarative/items/qsgcontext2d.cpp | 15 +++++++++++++-- .../scenegraph/qsgdistancefieldglyphcache.cpp | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/declarative/items/qsgcontext2d.cpp b/src/declarative/items/qsgcontext2d.cpp index 2e60de189f..9dddc99557 100644 --- a/src/declarative/items/qsgcontext2d.cpp +++ b/src/declarative/items/qsgcontext2d.cpp @@ -786,12 +786,15 @@ static QScriptValue ctx2d_isPointInPath(QScriptContext *c, QScriptEngine *e) // text static QScriptValue ctx2d_font(QScriptContext *c, QScriptEngine *e) { + return QScriptValue(); } static QScriptValue ctx2d_textAlign(QScriptContext *c, QScriptEngine *e) { + return QScriptValue(); } static QScriptValue ctx2d_textBaseline(QScriptContext *c, QScriptEngine *e) { + return QScriptValue(); } static QScriptValue ctx2d_fillText(QScriptContext *c, QScriptEngine *e) { @@ -846,53 +849,61 @@ static QScriptValue ctx2d_drawImage(QScriptContext *c, QScriptEngine *e) static QScriptValue ctx2d_createImageData(QScriptContext *c, QScriptEngine *e) { //#TODO + return QScriptValue(); } static QScriptValue ctx2d_getImageData(QScriptContext *c, QScriptEngine *e) { //#TODO + return QScriptValue(); } static QScriptValue ctx2d_putImageData(QScriptContext *c, QScriptEngine *e) { //#TODO + return QScriptValue(); } //Image Data Interface static QScriptValue ctx2d_imageData_data(QScriptContext *c, QScriptEngine *e) { //#TODO + return QScriptValue(); } static QScriptValue ctx2d_imageData_height(QScriptContext *c, QScriptEngine *e) { //#TODO + return QScriptValue(); } static QScriptValue ctx2d_imageData_width(QScriptContext *c, QScriptEngine *e) { //#TODO + return QScriptValue(); } //CanvasPixelArray interface static QScriptValue ctx2d_pixelArray_length(QScriptContext *c, QScriptEngine *e) { //#TODO + return QScriptValue(); } //getter/setter by index how to? static QScriptValue ctx2d_pixelArray(QScriptContext *c, QScriptEngine *e) { //#TODO + return QScriptValue(); } //CanvasGradient interface static QScriptValue ctx2d_gradient_addColorStop(QScriptContext *c, QScriptEngine *e) { //#TODO - + return QScriptValue(); } //TextMetrics static QScriptValue ctx2d_textMetrics_width(QScriptContext *c, QScriptEngine *e) { //#TODO - + return QScriptValue(); } diff --git a/src/declarative/scenegraph/qsgdistancefieldglyphcache.cpp b/src/declarative/scenegraph/qsgdistancefieldglyphcache.cpp index 0011492053..e216dd4699 100644 --- a/src/declarative/scenegraph/qsgdistancefieldglyphcache.cpp +++ b/src/declarative/scenegraph/qsgdistancefieldglyphcache.cpp @@ -941,7 +941,7 @@ void QSGDistanceFieldGlyphCache::updateCache() QFile file(key); if (file.open(QFile::ReadOnly)) { int fileSize = file.size(); - int dim = sqrt(fileSize); + int dim = sqrt(float(fileSize)); QByteArray blob = file.readAll(); glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, dim, dim, GL_ALPHA, GL_UNSIGNED_BYTE, blob.constData()); continue; From 4b9f5c7600772ddd1e091c8780b1fd70e9ba5001 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Mon, 20 Jun 2011 12:21:33 +0200 Subject: [PATCH 36/48] Compile when qreal is not a double --- src/declarative/particles/qsgpointattractor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/declarative/particles/qsgpointattractor.cpp b/src/declarative/particles/qsgpointattractor.cpp index 4c675237ba..0a893f7743 100644 --- a/src/declarative/particles/qsgpointattractor.cpp +++ b/src/declarative/particles/qsgpointattractor.cpp @@ -60,11 +60,11 @@ bool QSGPointAttractorAffector::affectParticle(QSGParticleData *d, qreal dt) qreal ds = 0; switch(m_proportionalToDistance){ case Quadratic: - ds = (m_strength / qMax(1.,r*r)) * dt; + ds = (m_strength / qMax(1.,r*r)) * dt; break; case Linear://also default default: - ds = (m_strength / qMax(1.,r)) * dt; + ds = (m_strength / qMax(1.,r)) * dt; } dx = ds * cos(theta); dy = ds * sin(theta); From 0990f073953a499b1413b049c1909d09fec5a815 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Mon, 20 Jun 2011 14:18:46 +0200 Subject: [PATCH 37/48] Start out with a distance field cache that fits more characters --- src/declarative/scenegraph/qsgdistancefieldglyphcache.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/declarative/scenegraph/qsgdistancefieldglyphcache.cpp b/src/declarative/scenegraph/qsgdistancefieldglyphcache.cpp index e216dd4699..c63ff9d4cc 100644 --- a/src/declarative/scenegraph/qsgdistancefieldglyphcache.cpp +++ b/src/declarative/scenegraph/qsgdistancefieldglyphcache.cpp @@ -912,8 +912,9 @@ void QSGDistanceFieldGlyphCache::updateCache() if (m_textureData->pendingGlyphs.isEmpty()) return; - int requiredWidth = m_textureData->currY == 0 ? m_textureData->currX : maxTextureSize(); - int requiredHeight = qMin(maxTextureSize(), m_textureData->currY + QT_DISTANCEFIELD_TILESIZE); + int requiredWidth = maxTextureSize(); + int rows = 128 / (requiredWidth / QT_DISTANCEFIELD_TILESIZE); // Enough rows to fill the latin1 set by default.. + int requiredHeight = qMin(maxTextureSize(), qMax(m_textureData->currY + QT_DISTANCEFIELD_TILESIZE, QT_DISTANCEFIELD_TILESIZE * rows)); resizeTexture((requiredWidth), (requiredHeight)); glBindTexture(GL_TEXTURE_2D, m_textureData->texture); From f0ab52c313076fce7e854fd494b88c5dd0f71af5 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Tue, 21 Jun 2011 12:02:08 +1000 Subject: [PATCH 38/48] Rewrite last rewrite Also rewrite to have all particle state shared, not just common elements. --- .../particles/qsgcustomparticle.cpp | 118 ++--- .../particles/qsgcustomparticle_p.h | 8 +- src/declarative/particles/qsgemitter.cpp | 21 +- .../particles/qsgfollowemitter.cpp | 37 +- src/declarative/particles/qsggravity.cpp | 4 +- .../particles/qsgimageparticle.cpp | 410 ++++++------------ .../particles/qsgimageparticle_p.h | 27 +- src/declarative/particles/qsgitemparticle.cpp | 65 ++- src/declarative/particles/qsgitemparticle_p.h | 8 +- src/declarative/particles/qsgkill.cpp | 2 +- .../particles/qsgmodelparticle.cpp | 74 ++-- .../particles/qsgmodelparticle_p.h | 8 +- .../particles/qsgparticlepainter.cpp | 83 ++-- .../particles/qsgparticlepainter_p.h | 71 +-- .../particles/qsgparticlesystem.cpp | 126 +++--- .../particles/qsgparticlesystem_p.h | 51 ++- .../particles/qsgpointattractor.cpp | 12 +- src/declarative/particles/qsgwander.cpp | 8 +- 18 files changed, 460 insertions(+), 673 deletions(-) diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp index f91307ed5c..09cfdda143 100644 --- a/src/declarative/particles/qsgcustomparticle.cpp +++ b/src/declarative/particles/qsgcustomparticle.cpp @@ -123,36 +123,8 @@ QSGCustomParticle::QSGCustomParticle(QSGItem* parent) : QSGParticlePainter(parent) , m_pleaseReset(true) , m_dirtyData(true) - , m_resizePending(false) { setFlag(QSGItem::ItemHasContents); - m_defaultVertices = new PlainVertices; - PlainVertex* vertices = (PlainVertex*) m_defaultVertices; - for (int i=0; i<4; ++i) { - vertices[i].x = 0; - vertices[i].y = 0; - vertices[i].t = -1; - vertices[i].lifeSpan = 0; - vertices[i].size = 0; - vertices[i].endSize = 0; - vertices[i].sx = 0; - vertices[i].sy = 0; - vertices[i].ax = 0; - vertices[i].ay = 0; - vertices[i].r = 0; - } - - vertices[0].tx = 0; - vertices[0].ty = 0; - - vertices[1].tx = 1; - vertices[1].ty = 0; - - vertices[2].tx = 0; - vertices[2].ty = 1; - - vertices[3].tx = 1; - vertices[3].ty = 1; } void QSGCustomParticle::componentComplete() @@ -202,19 +174,6 @@ void QSGCustomParticle::setVertexShader(const QByteArray &code) emit vertexShaderChanged(); } -void QSGCustomParticle::resize(int oldCount, int newCount) -{ - if(!m_node) - return; - if(!m_resizePending){ - m_pendingResizeVector.resize(oldCount); - PlainVertices *particles = (PlainVertices *) m_node->geometry()->vertexData(); - for(int i=0; iisRunning()) prepareNextFrame(); @@ -437,32 +394,6 @@ void QSGCustomParticle::prepareNextFrame(){ buildData(); } -void QSGCustomParticle::performPendingResize() -{ - m_resizePending = false; - if(!m_node) - return; - Q_ASSERT(m_pendingResizeVector.size() == m_count);//XXX - PlainVertices tmp[m_count];//###More vast memcpys that will decrease performance - for(int i=0; isetFlag(QSGNode::OwnsGeometry, false); - m_node->geometry()->allocate(m_count*4, m_count*6); - memcpy(m_node->geometry()->vertexData(), tmp, sizeof(PlainVertices) * m_count); - quint16 *indices = m_node->geometry()->indexDataAsUShort(); - for (int i=0; isetFlag(QSGNode::OwnsGeometry, true); -} - QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() { if (m_count * 4 > 0xffff) { @@ -475,8 +406,6 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() return 0; } - m_resizePending = false;//reset resizes as well. - //Create Particle Geometry int vCount = m_count * 4; int iCount = m_count * 6; @@ -484,10 +413,18 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() g->setDrawingMode(GL_TRIANGLES); PlainVertex *vertices = (PlainVertex *) g->vertexData(); for (int p=0; pindexDataAsUShort(); @@ -553,28 +490,31 @@ void QSGCustomParticle::buildData() m_dirtyData = false; } -void QSGCustomParticle::load(QSGParticleData *d) +void QSGCustomParticle::initialize(int idx) { - reload(d);//We don't do anything special in C++ here. + m_data[idx]->r = rand()/(qreal)RAND_MAX; } -void QSGCustomParticle::reload(QSGParticleData *d) +void QSGCustomParticle::reload(int idx) { if (m_node == 0) return; PlainVertices *particles = (PlainVertices *) m_node->geometry()->vertexData(); - - int pos = particleTypeIndex(d); - - PlainVertices &p = particles[pos]; - - //Perhaps we could be more efficient? - vertexCopy(p.v1, d->pv); - vertexCopy(p.v2, d->pv); - vertexCopy(p.v3, d->pv); - vertexCopy(p.v4, d->pv); - + PlainVertex *vertices = (PlainVertex *)&particles[idx]; + for (int i=0; i<4; ++i) { + vertices[i].x = m_data[idx]->x - m_systemOffset.x(); + vertices[i].y = m_data[idx]->y - m_systemOffset.y(); + vertices[i].t = m_data[idx]->t; + vertices[i].lifeSpan = m_data[idx]->lifeSpan; + vertices[i].size = m_data[idx]->size; + vertices[i].endSize = m_data[idx]->endSize; + vertices[i].sx = m_data[idx]->sx; + vertices[i].sy = m_data[idx]->sy; + vertices[i].ax = m_data[idx]->ax; + vertices[i].ay = m_data[idx]->ay; + vertices[i].r = m_data[idx]->r; + } } QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgcustomparticle_p.h b/src/declarative/particles/qsgcustomparticle_p.h index 863da052f3..50ff37a2a0 100644 --- a/src/declarative/particles/qsgcustomparticle_p.h +++ b/src/declarative/particles/qsgcustomparticle_p.h @@ -62,8 +62,6 @@ class QSGCustomParticle : public QSGParticlePainter public: explicit QSGCustomParticle(QSGItem* parent=0); - virtual void load(QSGParticleData*); - virtual void reload(QSGParticleData*); QByteArray fragmentShader() const { return m_source.fragmentCode; } void setFragmentShader(const QByteArray &code); @@ -77,6 +75,9 @@ Q_SIGNALS: void fragmentShaderChanged(); void vertexShaderChanged(); protected: + virtual void initialize(int idx); + virtual void reload(int idx); + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); void prepareNextFrame(); void setSource(const QVariant &var, int index); @@ -108,9 +109,6 @@ private: QSGShaderEffectNode* m_node; qreal m_lastTime; - bool m_resizePending; - QVector m_pendingResizeVector; - PlainVertices* m_defaultVertices; }; QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgemitter.cpp b/src/declarative/particles/qsgemitter.cpp index 081dd8dec5..c3ec3ffc9e 100644 --- a/src/declarative/particles/qsgemitter.cpp +++ b/src/declarative/particles/qsgemitter.cpp @@ -124,7 +124,6 @@ void QSGBasicEmitter::emitWindow(int timeStamp) QSGParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_particle]); if(datum){//actually emit(otherwise we've been asked to skip this one) datum->e = this;//###useful? - ParticleVertex &p = datum->pv; qreal t = 1 - (pt - opt) / dt; qreal vx = - 2 * ax * (1 - t) @@ -137,8 +136,8 @@ void QSGBasicEmitter::emitWindow(int timeStamp) // Particle timestamp - p.t = pt; - p.lifeSpan = //TODO:Promote to base class? + datum->t = pt; + datum->lifeSpan = //TODO:Promote to base class? (m_particleDuration + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation)) / 1000.0; @@ -153,20 +152,20 @@ void QSGBasicEmitter::emitWindow(int timeStamp) , width(), height()); } QPointF newPos = effectiveExtruder()->extrude(boundsRect); - p.x = newPos.x(); - p.y = newPos.y(); + datum->x = newPos.x(); + datum->y = newPos.y(); // Particle speed const QPointF &speed = m_speed->sample(newPos); - p.sx = speed.x() + datum->sx = speed.x() + m_speed_from_movement * vx; - p.sy = speed.y() + datum->sy = speed.y() + m_speed_from_movement * vy; // Particle acceleration const QPointF &accel = m_acceleration->sample(newPos); - p.ax = accel.x(); - p.ay = accel.y(); + datum->ax = accel.x(); + datum->ay = accel.y(); // Particle size float sizeVariation = -m_particleSizeVariation @@ -175,8 +174,8 @@ void QSGBasicEmitter::emitWindow(int timeStamp) float size = qMax((qreal)0.0 , m_particleSize + sizeVariation); float endSize = qMax((qreal)0.0 , sizeAtEnd + sizeVariation); - p.size = size;// * float(m_emitting); - p.endSize = endSize;// * float(m_emitting); + datum->size = size;// * float(m_emitting); + datum->endSize = endSize;// * float(m_emitting); m_system->emitParticle(datum); } diff --git a/src/declarative/particles/qsgfollowemitter.cpp b/src/declarative/particles/qsgfollowemitter.cpp index 28a082f776..ba31e8f834 100644 --- a/src/declarative/particles/qsgfollowemitter.cpp +++ b/src/declarative/particles/qsgfollowemitter.cpp @@ -116,8 +116,8 @@ void QSGFollowEmitter::emitWindow(int timeStamp) if(!d || !d->stillAlive()) continue; pt = m_lastEmission[d->index]; - if(pt < d->pv.t) - pt = d->pv.t; + if(pt < d->t) + pt = d->t; if(!effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){ m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside @@ -127,45 +127,44 @@ void QSGFollowEmitter::emitWindow(int timeStamp) QSGParticleData* datum = m_system->newDatum(gId2); if(datum){//else, skip this emission datum->e = this;//###useful? - ParticleVertex &p = datum->pv; // Particle timestamp - p.t = pt; - p.lifeSpan = + datum->t = pt; + datum->lifeSpan = (m_particleDuration + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation)) / 1000.0; // Particle position // Note that burst location doesn't get used for follow emitter - qreal followT = pt - d->pv.t; + qreal followT = pt - d->t; qreal followT2 = followT * followT * 0.5; - qreal sizeOffset = d->pv.size/2;//TODO: Current size? As an option + qreal sizeOffset = d->size/2;//TODO: Current size? As an option //TODO: Set variations //Subtract offset, because PS expects this in emitter coordinates - QRectF boundsRect(d->pv.x - offset.x() + d->pv.sx * followT + d->pv.ax * followT2 - m_emitterXVariation/2, - d->pv.y - offset.y() + d->pv.sy * followT + d->pv.ay * followT2 - m_emitterYVariation/2, + QRectF boundsRect(d->x - offset.x() + d->sx * followT + d->ax * followT2 - m_emitterXVariation/2, + d->y - offset.y() + d->sy * followT + d->ay * followT2 - m_emitterYVariation/2, m_emitterXVariation, m_emitterYVariation); - // QRectF boundsRect(d->pv.x + d->pv.sx * followT + d->pv.ax * followT2 + offset.x() - sizeOffset, - // d->pv.y + d->pv.sy * followT + d->pv.ay * followT2 + offset.y() - sizeOffset, + // QRectF boundsRect(d->x + d->sx * followT + d->ax * followT2 + offset.x() - sizeOffset, + // d->y + d->sy * followT + d->ay * followT2 + offset.y() - sizeOffset, // sizeOffset*2, // sizeOffset*2); QSGParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder; const QPointF &newPos = effectiveEmissionExtruder->extrude(boundsRect); - p.x = newPos.x(); - p.y = newPos.y(); + datum->x = newPos.x(); + datum->y = newPos.y(); // Particle speed const QPointF &speed = m_speed->sample(newPos); - p.sx = speed.x(); - p.sy = speed.y(); + datum->sx = speed.x(); + datum->sy = speed.y(); // Particle acceleration const QPointF &accel = m_acceleration->sample(newPos); - p.ax = accel.x(); - p.ay = accel.y(); + datum->ax = accel.x(); + datum->ay = accel.y(); // Particle size float sizeVariation = -m_particleSizeVariation @@ -174,8 +173,8 @@ void QSGFollowEmitter::emitWindow(int timeStamp) float size = qMax((qreal)0.0, m_particleSize + sizeVariation); float endSize = qMax((qreal)0.0, sizeAtEnd + sizeVariation); - p.size = size * float(m_emitting); - p.endSize = endSize * float(m_emitting); + datum->size = size * float(m_emitting); + datum->endSize = endSize * float(m_emitting); m_system->emitParticle(datum); } diff --git a/src/declarative/particles/qsggravity.cpp b/src/declarative/particles/qsggravity.cpp index de735da5ad..b1cf3e9481 100644 --- a/src/declarative/particles/qsggravity.cpp +++ b/src/declarative/particles/qsggravity.cpp @@ -64,11 +64,11 @@ bool QSGGravityAffector::affectParticle(QSGParticleData *d, qreal dt) { Q_UNUSED(dt); bool changed = false; - if(d->pv.ax != m_xAcc){ + if(d->ax != m_xAcc){ d->setInstantaneousAX(m_xAcc); changed = true; } - if(d->pv.ay != m_yAcc){ + if(d->ay != m_yAcc){ d->setInstantaneousAY(m_yAcc); changed = true; } diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp index 15bc88b4c3..836236c13e 100644 --- a/src/declarative/particles/qsgimageparticle.cpp +++ b/src/declarative/particles/qsgimageparticle.cpp @@ -305,62 +305,6 @@ QSGImageParticle::QSGImageParticle(QSGItem* parent) , m_lastLevel(Unknown) { setFlag(ItemHasContents); - //TODO: Clean up defaults here and in custom - m_defaultUltra = new UltraVertices; - m_defaultSimple = new SimpleVertices; - UltraVertex *vertices = (UltraVertex *) m_defaultUltra; - SimpleVertex *vertices2 = (SimpleVertex *) m_defaultSimple; - for (int i=0; i<4; ++i) { - vertices2[i].x = vertices[i].x = 0; - vertices2[i].y = vertices[i].y = 0; - vertices2[i].t = vertices[i].t = -1; - vertices2[i].lifeSpan = vertices[i].lifeSpan = 0; - vertices2[i].size = vertices[i].size = 0; - vertices2[i].endSize = vertices[i].endSize = 0; - vertices2[i].sx = vertices[i].sx = 0; - vertices2[i].sy = vertices[i].sy = 0; - vertices2[i].ax = vertices[i].ax = 0; - vertices2[i].ay = vertices[i].ay = 0; - vertices[i].xx = 1; - vertices[i].xy = 0; - vertices[i].yx = 0; - vertices[i].yy = 1; - vertices[i].rotation = 0; - vertices[i].rotationSpeed = 0; - vertices[i].autoRotate = 0; - vertices[i].animIdx = -1; - vertices[i].frameDuration = 1; - vertices[i].frameCount = 0; - vertices[i].animT = -1; - vertices[i].color.r = 255; - vertices[i].color.g = 255; - vertices[i].color.b = 255; - vertices[i].color.a = 255; - } - - vertices[0].tx = 0; - vertices[0].ty = 0; - - vertices[1].tx = 1; - vertices[1].ty = 0; - - vertices[2].tx = 0; - vertices[2].ty = 1; - - vertices[3].tx = 1; - vertices[3].ty = 1; - - vertices2[0].tx = 0; - vertices2[0].ty = 0; - - vertices2[1].tx = 1; - vertices2[1].ty = 0; - - vertices2[2].tx = 0; - vertices2[2].ty = 1; - - vertices2[3].tx = 1; - vertices2[3].ty = 1; } QDeclarativeListProperty QSGImageParticle::sprites() @@ -411,7 +355,7 @@ void QSGImageParticle::setColor(const QColor &color) return; m_color = color; emit colorChanged(); - if(perfLevel < Coloured) + if(perfLevel < Colored) reset(); } @@ -421,7 +365,7 @@ void QSGImageParticle::setColorVariation(qreal var) return; m_color_variation = var; emit colorVariationChanged(); - if(perfLevel < Coloured) + if(perfLevel < Colored) reset(); } @@ -431,7 +375,7 @@ void QSGImageParticle::setAlphaVariation(qreal arg) m_alphaVariation = arg; emit alphaVariationChanged(arg); } - if(perfLevel < Coloured) + if(perfLevel < Colored) reset(); } @@ -441,7 +385,7 @@ void QSGImageParticle::setAlpha(qreal arg) m_alpha = arg; emit alphaChanged(arg); } - if(perfLevel < Coloured) + if(perfLevel < Colored) reset(); } @@ -451,7 +395,7 @@ void QSGImageParticle::setRedVariation(qreal arg) m_redVariation = arg; emit redVariationChanged(arg); } - if(perfLevel < Coloured) + if(perfLevel < Colored) reset(); } @@ -461,7 +405,7 @@ void QSGImageParticle::setGreenVariation(qreal arg) m_greenVariation = arg; emit greenVariationChanged(arg); } - if(perfLevel < Coloured) + if(perfLevel < Colored) reset(); } @@ -471,7 +415,7 @@ void QSGImageParticle::setBlueVariation(qreal arg) m_blueVariation = arg; emit blueVariationChanged(arg); } - if(perfLevel < Coloured) + if(perfLevel < Colored) reset(); } @@ -619,9 +563,34 @@ QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode() QSGGeometry *g = new QSGGeometry(SimpleParticle_AttributeSet, vCount, iCount); g->setDrawingMode(GL_TRIANGLES); - SimpleVertices *vertices = (SimpleVertices *) g->vertexData(); - for (int p=0; pvertexData(); + for (int p=0; px; + vertices[i].y = m_data[p]->y; + vertices[i].t = m_data[p]->t; + vertices[i].size = m_data[p]->size; + vertices[i].endSize = m_data[p]->endSize; + vertices[i].sx = m_data[p]->sx; + vertices[i].sy = m_data[p]->sy; + vertices[i].ax = m_data[p]->ax; + vertices[i].ay = m_data[p]->ay; + } + //reload(p); + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; + + vertices += 4; + } quint16 *indices = g->indexDataAsUShort(); for (int i=0; isetGeometry(g); + if (m_material) { delete m_material; m_material = 0; @@ -644,28 +616,24 @@ QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode() m_material->texture = sceneGraphEngine()->createTextureFromImage(image); m_material->texture->setFiltering(QSGTexture::Linear); m_material->framecount = 1; - m_node = new QSGGeometryNode(); - m_node->setGeometry(g); m_node->setMaterial(m_material); m_last_particle = 0; - return m_node; } QSGGeometryNode* QSGImageParticle::buildParticleNode() { if (m_count * 4 > 0xffff) { - printf("UltraParticle: Too many particles... \n");//####Why is this here? + printf("UltraParticle: Too many particles... \n");//### Why is this here? return 0; } if(m_count <= 0) { - printf("UltraParticle: Too few particles... \n"); + qDebug() << "UltraParticle: Too few particles... \n";//XXX: Is now a vaild intermediate state... return 0; } - m_resizePending = false; if(!m_sprites.count() && !m_bloat && m_colortable_name.isEmpty() && m_sizetable_name.isEmpty() @@ -705,38 +673,29 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode() QSGGeometry *g = new QSGGeometry(UltraParticle_AttributeSet, vCount, iCount); g->setDrawingMode(GL_TRIANGLES); + m_node = new QSGGeometryNode(); + m_node->setGeometry(g); UltraVertex *vertices = (UltraVertex *) g->vertexData(); - SimpleVertex *oldSimple = (SimpleVertex *) m_lastData;//TODO: Other levels - if(m_lastLevel == 1) - qDebug() << "Theta" << m_lastLevel << oldSimple[0].x << oldSimple[0].y << oldSimple[0].t; for (int p=0; p p) {//Transplant/IntermediateVertices? - for (int i=0; i<4; ++i) { - vertices[i].x = oldSimple[i].x; - vertices[i].y = oldSimple[i].y; - vertices[i].t = oldSimple[i].t; - vertices[i].lifeSpan = oldSimple[i].lifeSpan; - vertices[i].size = oldSimple[i].size; - vertices[i].endSize = oldSimple[i].endSize; - vertices[i].sx = oldSimple[i].sx; - vertices[i].sy = oldSimple[i].sy; - vertices[i].ax = oldSimple[i].ax; - vertices[i].ay = oldSimple[i].ay; - /* - vertices[i].frameDuration = oldSimple[i].lifeSpan; - vertices[i].frameCount = 1; - vertices[i].animT = oldSimple[i].t; - */ - } - } + reload(p);//reload gets geometry from node + + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; vertices += 4; - oldSimple += 4; } - quint16 *indices = g->indexDataAsUShort();//TODO: Speed gains by copying this over if count unchanged? + quint16 *indices = g->indexDataAsUShort(); for (int i=0; isetCount(m_count); } - m_node = new QSGGeometryNode(); - m_node->setGeometry(g); m_node->setMaterial(m_material); m_last_particle = 0; @@ -789,92 +746,11 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode() return m_node; } -void QSGImageParticle::resize(int oldCount, int newCount) -{ - //If perf level changes at the same time as a resize, we reset instead of doing pending resize - if(!m_node) - return; - switch(perfLevel){ - default: - case Sprites: - if(m_spriteEngine) - reset();//TODO: Handle sprite resizeing (have to shuffle the engine too...) - case Coloured: - case Deformable: - case Tabled: - if(!m_resizePending){ - m_resizePendingUltra.resize(oldCount); - UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData(); - for(int i=0; igeometry()->vertexData(); - for(int i=0; isetFlag(QSGNode::OwnsGeometry, false); - m_node->geometry()->allocate(m_count*4, m_count*6); - memcpy(m_node->geometry()->vertexData(), tmp1, sizeof(UltraVertices) * m_count); - m_node->setFlag(QSGNode::OwnsGeometry, true); - break; - case Simple: - Q_ASSERT(m_resizePendingSimple.size() == m_count);//XXX - for(int i=0; isetFlag(QSGNode::OwnsGeometry, false); - m_node->geometry()->allocate(m_count*4, m_count*6); - memcpy(m_node->geometry()->vertexData(), tmp2, sizeof(SimpleVertices) * m_count); - m_node->setFlag(QSGNode::OwnsGeometry, true); - break; - } - quint16 *indices = m_node->geometry()->indexDataAsUShort(); - for (int i=0; isetFlag(QSGNode::OwnsGeometry, true); -} - QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) { if(m_pleaseReset){ if(m_node){ if(perfLevel == 1){ - qDebug() << "Beta"; m_lastCount = m_node->geometry()->vertexCount() / 4; m_lastData = qMalloc(m_lastCount*sizeof(SimpleVertices)); memcpy(m_lastData, m_node->geometry()->vertexData(), m_lastCount * sizeof(SimpleVertices));//TODO: Multiple levels @@ -889,8 +765,6 @@ QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) m_material = 0; m_pleaseReset = false; } - if(m_resizePending) - performPendingResize(); if(m_system && m_system->isRunning()) prepareNextFrame(); @@ -904,7 +778,7 @@ QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) void QSGImageParticle::prepareNextFrame() { - if (m_node == 0){ //TODO: Staggered loading (as emitted) + if (m_node == 0){//TODO: Staggered loading (as emitted) m_node = buildParticleNode(); if(m_node == 0) return; @@ -935,67 +809,14 @@ void QSGImageParticle::prepareNextFrame() } } -template -IntermediateVertices* transplant(IntermediateVertices* iv, VT &v) -{//Deliberate typemangling cast - iv->v1 = (UltraVertex*)&(v.v1); - iv->v2 = (UltraVertex*)&(v.v2); - iv->v3 = (UltraVertex*)&(v.v3); - iv->v4 = (UltraVertex*)&(v.v4); - return iv; -} - -IntermediateVertices* QSGImageParticle::fetchIntermediateVertices(int pos) -{ - //Note that this class ruins typesafety for you. Maybe even thread safety. - //TODO: Something better, possibly with templates or inheritance - static IntermediateVertices iv; - SimpleVertices *sv; - UltraVertices *uv; - switch(perfLevel){ - case Simple: - sv = (SimpleVertices *) m_node->geometry()->vertexData(); - return transplant(&iv, sv[pos]); - case Coloured: - case Deformable: - case Tabled: - case Sprites: - default: - uv = (UltraVertices *) m_node->geometry()->vertexData(); - return transplant(&iv,uv[pos]); - } -} - void QSGImageParticle::reloadColor(const Color4ub &c, QSGParticleData* d) { - UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData(); - int pos = particleTypeIndex(d); - UltraVertices &p = particles[pos]; - p.v1.color = p.v2.color = p.v3.color = p.v4.color = c; + d->color = c; + //TODO: get index for reload - or make function take an index } -void QSGImageParticle::reload(QSGParticleData *d) +void QSGImageParticle::initialize(int idx) { - if (m_node == 0) - return; - - int pos = particleTypeIndex(d); - IntermediateVertices* p = fetchIntermediateVertices(pos); - - //Perhaps we could be more efficient? - vertexCopy(*p->v1, d->pv); - vertexCopy(*p->v2, d->pv); - vertexCopy(*p->v3, d->pv); - vertexCopy(*p->v4, d->pv); -} - -void QSGImageParticle::load(QSGParticleData *d) -{ - if (m_node == 0) - return; - - int pos = particleTypeIndex(d); - IntermediateVertices* p = fetchIntermediateVertices(pos);//Remember this removes typesafety! Color4ub color; qreal redVariation = m_color_variation + m_redVariation; qreal greenVariation = m_color_variation + m_greenVariation; @@ -1003,66 +824,111 @@ void QSGImageParticle::load(QSGParticleData *d) switch(perfLevel){//Fall-through is intended on all of them case Sprites: // Initial Sprite State - p->v1->animT = p->v2->animT = p->v3->animT = p->v4->animT = p->v1->t; - p->v1->animIdx = p->v2->animIdx = p->v3->animIdx = p->v4->animIdx = 0; + m_data[idx]->animT = m_data[idx]->t; + m_data[idx]->animIdx = 0; if(m_spriteEngine){ - m_spriteEngine->startSprite(pos); - p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = m_spriteEngine->spriteFrames(pos); - p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = m_spriteEngine->spriteDuration(pos); + m_spriteEngine->startSprite(idx); + m_data[idx]->frameCount = m_spriteEngine->spriteFrames(idx); + m_data[idx]->frameDuration = m_spriteEngine->spriteDuration(idx); }else{ - p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = 1; - p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = 9999; + m_data[idx]->frameCount = 1; + m_data[idx]->frameDuration = 9999; } case Tabled: case Deformable: //Initial Rotation if(m_xVector){ - const QPointF &ret = m_xVector->sample(QPointF(d->pv.x, d->pv.y)); - p->v1->xx = p->v2->xx = p->v3->xx = p->v4->xx = ret.x(); - p->v1->xy = p->v2->xy = p->v3->xy = p->v4->xy = ret.y(); + const QPointF &ret = m_xVector->sample(QPointF(m_data[idx]->x, m_data[idx]->y)); + m_data[idx]->xx = ret.x(); + m_data[idx]->xy = ret.y(); } if(m_yVector){ - const QPointF &ret = m_yVector->sample(QPointF(d->pv.x, d->pv.y)); - p->v1->yx = p->v2->yx = p->v3->yx = p->v4->yx = ret.x(); - p->v1->yy = p->v2->yy = p->v3->yy = p->v4->yy = ret.y(); + const QPointF &ret = m_yVector->sample(QPointF(m_data[idx]->x, m_data[idx]->y)); + m_data[idx]->yx = ret.x(); + m_data[idx]->yy = ret.y(); } - p->v1->rotation = p->v2->rotation = p->v3->rotation = p->v4->rotation = + m_data[idx]->rotation = (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV; - p->v1->rotationSpeed = p->v2->rotationSpeed = p->v3->rotationSpeed = p->v4->rotationSpeed = + m_data[idx]->rotationSpeed = (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV; - p->v1->autoRotate = p->v2->autoRotate = p->v3->autoRotate = p->v4->autoRotate = m_autoRotation?1.0:0.0; - case Coloured: + m_data[idx]->autoRotate = m_autoRotation?1.0:0.0; + case Colored: //Color initialization // Particle color color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation; color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation; color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation; color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation; - p->v1->color = p->v2->color = p->v3->color = p->v4->color = color; + m_data[idx]->color = color; default: break; } - - vertexCopy(*p->v1, d->pv); - vertexCopy(*p->v2, d->pv); - vertexCopy(*p->v3, d->pv); - vertexCopy(*p->v4, d->pv); } -/* -void QSGImageParticle::verticesUpgrade(void *prev, void *next) +void QSGImageParticle::reload(int idx) { - PerformanceLevel copyLevel = qMin(perfLevel, m_lastLevel); - switch(perfLevel){//Intentional fall-through - case Sprites: - if(copyLevel >= Sprites) - case Tabled: - case Deformable: - case Coloured: + if(!m_node) + return; + + m_node->setFlag(QSGNode::OwnsGeometry, false); + UltraVertex *ultraVertices = (UltraVertex *) m_node->geometry()->vertexData(); + SimpleVertex *simpleVertices = (SimpleVertex *) m_node->geometry()->vertexData(); + switch(perfLevel){ + case Sprites: + ultraVertices += idx*4; + for(int i=0; i<4; i++){ + ultraVertices[i].x = m_data[idx]->x - m_systemOffset.x(); + ultraVertices[i].y = m_data[idx]->y - m_systemOffset.y(); + ultraVertices[i].t = m_data[idx]->t; + ultraVertices[i].lifeSpan = m_data[idx]->lifeSpan; + ultraVertices[i].size = m_data[idx]->size; + ultraVertices[i].endSize = m_data[idx]->endSize; + ultraVertices[i].sx = m_data[idx]->sx; + ultraVertices[i].sy = m_data[idx]->sy; + ultraVertices[i].ax = m_data[idx]->ax; + ultraVertices[i].ay = m_data[idx]->ay; + ultraVertices[i].xx = m_data[idx]->xx; + ultraVertices[i].xy = m_data[idx]->xy; + ultraVertices[i].yx = m_data[idx]->yx; + ultraVertices[i].yy = m_data[idx]->yy; + ultraVertices[i].rotation = m_data[idx]->rotation; + ultraVertices[i].rotationSpeed = m_data[idx]->rotationSpeed; + ultraVertices[i].autoRotate = m_data[idx]->autoRotate; + ultraVertices[i].animIdx = m_data[idx]->animIdx; + ultraVertices[i].frameDuration = m_data[idx]->frameDuration; + ultraVertices[i].frameCount = m_data[idx]->frameCount; + ultraVertices[i].animT = m_data[idx]->animT; + ultraVertices[i].color.r = m_data[idx]->color.r; + ultraVertices[i].color.g = m_data[idx]->color.g; + ultraVertices[i].color.b = m_data[idx]->color.b; + ultraVertices[i].color.a = m_data[idx]->color.a; + } + break; + case Tabled://TODO: Us + case Deformable: + case Colored: + case Simple: + simpleVertices += idx*4; + for(int i=0; i<4; i++){ + simpleVertices[i].x = m_data[idx]->x - m_systemOffset.x(); + simpleVertices[i].y = m_data[idx]->y - m_systemOffset.y(); + simpleVertices[i].t = m_data[idx]->t; + simpleVertices[i].lifeSpan = m_data[idx]->lifeSpan; + simpleVertices[i].size = m_data[idx]->size; + simpleVertices[i].endSize = m_data[idx]->endSize; + simpleVertices[i].sx = m_data[idx]->sx; + simpleVertices[i].sy = m_data[idx]->sy; + simpleVertices[i].ax = m_data[idx]->ax; + simpleVertices[i].ay = m_data[idx]->ay; + } + break; + default: + break; } + m_node->setFlag(QSGNode::OwnsGeometry, true); } -*/ + QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h index a644dd4216..dc79c5910e 100644 --- a/src/declarative/particles/qsgimageparticle_p.h +++ b/src/declarative/particles/qsgimageparticle_p.h @@ -57,13 +57,6 @@ class QSGGeometryNode; class QSGSprite; class QSGSpriteEngine; -struct Color4ub { - uchar r; - uchar g; - uchar b; - uchar a; -}; - struct SimpleVertex { float x; float y; @@ -165,8 +158,6 @@ public: explicit QSGImageParticle(QSGItem *parent = 0); virtual ~QSGImageParticle(){} - virtual void load(QSGParticleData*); - virtual void reload(QSGParticleData*); QDeclarativeListProperty sprites(); QSGSpriteEngine* spriteEngine() {return m_spriteEngine;} @@ -174,7 +165,7 @@ public: enum PerformanceLevel{//TODO: Expose? Unknown = 0, Simple, - Coloured, + Colored, Deformable, Tabled, Sprites @@ -292,20 +283,19 @@ public slots: void setBloat(bool arg); protected: - QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); void reset(); + virtual void initialize(int idx); + virtual void reload(int idx); + + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); void prepareNextFrame(); QSGGeometryNode* buildParticleNode(); QSGGeometryNode* buildSimpleParticleNode(); - void resize(int oldCount, int newCount); - void performPendingResize(); private slots: void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty private: - //template void verticesUpgrade(IntermediateVertices* prev, T* next);//### Loses typessafety again... - IntermediateVertices* fetchIntermediateVertices(int pos); bool m_do_reset; QUrl m_image_name; @@ -347,13 +337,6 @@ private: PerformanceLevel m_lastLevel; void* m_lastData; int m_lastCount; - - //TODO: Some smart method that scales to multiple types better - bool m_resizePending; - QVector m_resizePendingUltra; - QVector m_resizePendingSimple; - UltraVertices* m_defaultUltra; - SimpleVertices* m_defaultSimple; }; QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgitemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp index 1c6a8c4db5..5e324cd43d 100644 --- a/src/declarative/particles/qsgitemparticle.cpp +++ b/src/declarative/particles/qsgitemparticle.cpp @@ -85,12 +85,13 @@ void QSGItemParticle::give(QSGItem *item) //TODO: This } -void QSGItemParticle::load(QSGParticleData* d) +void QSGItemParticle::initialize(int idx) +{ + m_loadables << idx;//defer to other thread +} + +void QSGItemParticle::reload(int idx) { - Q_ASSERT(d); - int pos = particleTypeIndex(d); - m_data[pos] = d; - m_loadables << pos; } void QSGItemParticle::tick() @@ -107,56 +108,41 @@ void QSGItemParticle::tick() m_deletables.clear(); foreach(int pos, m_loadables){ - if(m_stasis.contains(m_items[pos])) + if(m_stasis.contains(m_data[pos]->delegate)) qWarning() << "Current model particles prefers overwrite:false"; //remove old item from the particle that is dying to make room for this one - if(m_items[pos]){ - m_deletables << m_items[pos]; + if(m_data[pos]->delegate){ + m_deletables << m_data[pos]->delegate; m_activeCount--; } - m_items[pos] = 0; + m_data[pos]->delegate = 0; if(!m_pendingItems.isEmpty()){ - m_items[pos] = m_pendingItems.front(); + m_data[pos]->delegate = m_pendingItems.front(); m_pendingItems.pop_front(); }else if(m_delegate){ - m_items[pos] = qobject_cast(m_delegate->create(qmlContext(this))); + m_data[pos]->delegate = qobject_cast(m_delegate->create(qmlContext(this))); } - if(m_items[pos] && m_data[pos]){//###Data can be zero if creating an item leads to a reset - this screws things up. - m_items[pos]->setX(m_data[pos]->curX() - m_items[pos]->width()/2);//TODO: adjust for system? - m_items[pos]->setY(m_data[pos]->curY() - m_items[pos]->height()/2); - QSGItemParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); + if(m_data[pos]->delegate && m_data[pos]){//###Data can be zero if creating an item leads to a reset - this screws things up. + m_data[pos]->delegate->setX(m_data[pos]->curX() - m_data[pos]->delegate->width()/2);//TODO: adjust for system? + m_data[pos]->delegate->setY(m_data[pos]->curY() - m_data[pos]->delegate->height()/2); + QSGItemParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_data[pos]->delegate)); if(mpa){ mpa->m_mp = this; mpa->attach(); } - m_items[pos]->setParentItem(this); + m_data[pos]->delegate->setParentItem(this); if(m_fade) - m_items[pos]->setOpacity(0.); + m_data[pos]->delegate->setOpacity(0.); m_activeCount++; } } m_loadables.clear(); } -void QSGItemParticle::reload(QSGParticleData* d) -{ - //No-op unless we start copying the data. -} - -void QSGItemParticle::resize(int oldCount, int newCount) -{ - if(!m_system) - return; - groupShuffle(m_items, (QSGItem*)0); - groupShuffle(m_data, (QSGParticleData*)0); -} - void QSGItemParticle::reset() { QSGParticlePainter::reset(); //TODO: Cleanup items? - m_items.fill(0); - m_data.fill(0); m_loadables.clear(); //deletables? } @@ -188,19 +174,18 @@ void QSGItemParticle::prepareNextFrame() //TODO: Size, better fade? for(int i=0; idelegate; + if(!item) continue; - qreal t = ((timeStamp/1000.0) - data->pv.t) / data->pv.lifeSpan; + qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan; if(m_stasis.contains(item)) { - m_data[i]->pv.t += dt;//Stasis effect + m_data[i]->t += dt;//Stasis effect continue; } if(t >= 1.0){//Usually happens from load m_deletables << item; - m_items[i] = 0; - m_data[i] = 0; + m_data[i]->delegate = 0; m_activeCount--; }else{//Fade if(m_fade){ @@ -214,8 +199,8 @@ void QSGItemParticle::prepareNextFrame() item->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on } } - item->setX(data->curX() - item->width()/2); - item->setY(data->curY() - item->height()/2); + item->setX(data->curX() - item->width()/2 - m_systemOffset.x()); + item->setY(data->curY() - item->height()/2 - m_systemOffset.y()); } } diff --git a/src/declarative/particles/qsgitemparticle_p.h b/src/declarative/particles/qsgitemparticle_p.h index 7cea63baa1..24bbcf97d8 100644 --- a/src/declarative/particles/qsgitemparticle_p.h +++ b/src/declarative/particles/qsgitemparticle_p.h @@ -63,8 +63,6 @@ public: bool fade() const { return m_fade; } virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - virtual void load(QSGParticleData*); - virtual void reload(QSGParticleData*); static QSGItemParticleAttached *qmlAttachedProperties(QObject *object); QDeclarativeComponent* delegate() const @@ -95,7 +93,8 @@ public slots: protected: virtual void reset(); - virtual void resize(int oldCount, int newCount); + virtual void reload(int idx); + virtual void initialize(int idx); void prepareNextFrame(); private slots: void tick(); @@ -105,9 +104,6 @@ private: bool m_fade; QList m_pendingItems; - QVector m_items; - QVector m_data; - QVector m_idx; QList m_available; QSet m_stasis; qreal m_lastT; diff --git a/src/declarative/particles/qsgkill.cpp b/src/declarative/particles/qsgkill.cpp index 1321898dc9..eb3a55d402 100644 --- a/src/declarative/particles/qsgkill.cpp +++ b/src/declarative/particles/qsgkill.cpp @@ -52,7 +52,7 @@ bool QSGKillAffector::affectParticle(QSGParticleData *d, qreal dt) { Q_UNUSED(dt); if(d->stillAlive()){ - d->pv.t -= d->pv.lifeSpan + 1; + d->t -= d->lifeSpan + 1; return true; } } diff --git a/src/declarative/particles/qsgmodelparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp index aba68c6ce4..47b8844ad5 100644 --- a/src/declarative/particles/qsgmodelparticle.cpp +++ b/src/declarative/particles/qsgmodelparticle.cpp @@ -149,26 +149,13 @@ void QSGModelParticle::unfreeze(QSGItem* item) m_stasis.remove(item); } -void QSGModelParticle::load(QSGParticleData* d) +void QSGModelParticle::initialize(int idx) { if(!m_model || !m_model->count()) return; - int pos = particleTypeIndex(d); if(m_available.isEmpty()) return; - if(m_items[pos]){ - if(m_stasis.contains(m_items[pos])) - qWarning() << "Current model particles prefers overwrite:false"; - //remove old item from the particle that is dying to make room for this one - m_deletables << m_items[pos]; - m_available << m_idx[pos]; - m_idx[pos] = -1; - m_items[pos] = 0; - m_data[pos] = 0; - m_activeCount--; - } - m_requests << pos; - m_data[pos] = d; + m_requests << idx; m_activeCount++; } @@ -181,40 +168,42 @@ void QSGModelParticle::processPending() m_deletables.clear(); foreach(int pos, m_requests){ + if(m_data[pos]->delegate){ + if(m_stasis.contains(m_data[pos]->delegate)) + qWarning() << "Current model particles prefers overwrite:false"; + //remove old item from the particle that is dying to make room for this one + m_deletables << m_data[pos]->delegate; + m_available << m_data[pos]->modelIndex; + m_data[pos]->modelIndex = -1; + m_data[pos]->delegate = 0; + m_data[pos] = 0; + m_activeCount--; + } + if(!m_available.isEmpty()){ - m_items[pos] = m_model->item(m_available.first()); - m_idx[pos] = m_available.first(); + m_data[pos]->delegate = m_model->item(m_available.first()); + m_data[pos]->modelIndex = m_available.first(); m_available.pop_front(); - QSGModelParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_items[pos])); + QSGModelParticleAttached* mpa = qobject_cast(qmlAttachedPropertiesObject(m_data[pos]->delegate)); if(mpa){ mpa->m_mp = this; mpa->attach(); } - m_items[pos]->setParentItem(this); + m_data[pos]->delegate->setParentItem(this); } } m_requests.clear(); } -void QSGModelParticle::reload(QSGParticleData* d) +void QSGModelParticle::reload(int idx) { //No-op unless we start copying the data. } -void QSGModelParticle::resize(int oldCount, int newCount) -{ - groupShuffle(m_items, (QSGItem *) 0); - groupShuffle(m_data, (QSGParticleData*) 0); - groupShuffle(m_idx, -1); -} - void QSGModelParticle::reset() { QSGParticlePainter::reset(); //TODO: Cleanup items? - m_items.fill(0); - m_data.fill(0); - m_idx.fill(-1); //m_available.clear();//Should this be reset too? //m_pendingItems.clear();//TODO: Should this be done? If so, Emit signal? } @@ -246,20 +235,19 @@ void QSGModelParticle::prepareNextFrame() //TODO: Size, better fade? for(int i=0; idelegate) continue; - qreal t = ((timeStamp/1000.0) - data->pv.t) / data->pv.lifeSpan; - if(m_stasis.contains(item)) { - m_data[i]->pv.t += dt;//Stasis effect + qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan; + if(m_stasis.contains(m_data[i]->delegate)) { + m_data[i]->t += dt;//Stasis effect continue; } if(t >= 1.0){//Usually happens from load - m_available << m_idx[i]; - m_deletables << item; - m_idx[i] = -1; - m_items[i] = 0; + m_available << m_data[i]->modelIndex; + m_deletables << m_data[i]->delegate; + m_data[i]->modelIndex = -1; + m_data[i]->delegate = 0; m_data[i] = 0; m_activeCount--; }else{//Fade @@ -269,13 +257,13 @@ void QSGModelParticle::prepareNextFrame() o = t*5; if(t>0.8) o = (1-t)*5; - item->setOpacity(o); + m_data[i]->delegate->setOpacity(o); }else{ - item->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on + m_data[i]->delegate->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on } } - item->setX(data->curX() - item->width()/2); - item->setY(data->curY() - item->height()/2); + m_data[i]->delegate->setX(data->curX() - m_data[i]->delegate->width()/2 - m_systemOffset.x()); + m_data[i]->delegate->setY(data->curY() - m_data[i]->delegate->height()/2 - m_systemOffset.y()); } } diff --git a/src/declarative/particles/qsgmodelparticle_p.h b/src/declarative/particles/qsgmodelparticle_p.h index cef2008b1a..31e4025bb4 100644 --- a/src/declarative/particles/qsgmodelparticle_p.h +++ b/src/declarative/particles/qsgmodelparticle_p.h @@ -74,8 +74,6 @@ public: bool fade() const { return m_fade; } virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - virtual void load(QSGParticleData*); - virtual void reload(QSGParticleData*); static QSGModelParticleAttached *qmlAttachedProperties(QObject *object); signals: @@ -91,7 +89,8 @@ public slots: void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();} protected: virtual void reset(); - virtual void resize(int oldCount, int newCount); + virtual void reload(int idx); + virtual void initialize(int idx); void prepareNextFrame(); private slots: void updateCount(); @@ -106,9 +105,6 @@ private: bool m_fade; QList m_pendingItems; - QVector m_items; - QVector m_data; - QVector m_idx; QList m_available; QSet m_stasis; qreal m_lastT; diff --git a/src/declarative/particles/qsgparticlepainter.cpp b/src/declarative/particles/qsgparticlepainter.cpp index 0d369fd4d8..cf1d3c2894 100644 --- a/src/declarative/particles/qsgparticlepainter.cpp +++ b/src/declarative/particles/qsgparticlepainter.cpp @@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE QSGParticlePainter::QSGParticlePainter(QSGItem *parent) : QSGItem(parent), - m_system(0), m_count(0), m_lastStart(0) + m_system(0), m_count(0), m_lastStart(0), m_sentinel(new QSGParticleData) { connect(this, SIGNAL(xChanged()), this, SLOT(calcSystemOffset())); @@ -78,46 +78,34 @@ void QSGParticlePainter::setSystem(QSGParticleSystem *arg) } } -void QSGParticlePainter::load(QSGParticleData*) +void QSGParticlePainter::load(QSGParticleData* d) { + int idx = particleTypeIndex(d); + m_data[idx] = d; + initialize(idx); + reload(idx); } -void QSGParticlePainter::reload(QSGParticleData*) +void QSGParticlePainter::reload(QSGParticleData* d) { + reload(particleTypeIndex(d)); } void QSGParticlePainter::reset() { //Have to every time because what it's emitting may have changed and that affects particleTypeIndex - if(m_system) - updateParticleStarts(); + if(m_system && !m_inResize) + resize(0,1);//###Fix this by making resize take sensible arguments + //###This also means double resets. Make reset not virtual? } -void QSGParticlePainter::resize(int, int) +void QSGParticlePainter::resize(int oldSize, int newSize) { -} - - -void QSGParticlePainter::setCount(int c) -{ - Q_ASSERT(c >= 0); //XXX - if(c == m_count) + if(newSize == oldSize)//TODO: What if particles switched so indices change but total count is the same? return; - int lastCount = m_count; - m_count = c; - resize(lastCount, m_count);//### is virtual needed? Or should I just use the signal? - updateParticleStarts(); - emit countChanged(); -} -int QSGParticlePainter::count() -{ - return m_count; -} - - -void QSGParticlePainter::updateParticleStarts() -{ + QHash > oldStarts(m_particleStarts); + //Update particle starts datastore m_particleStarts.clear(); m_lastStart = 0; QList particleList; @@ -130,6 +118,47 @@ void QSGParticlePainter::updateParticleStarts() m_particleStarts.insert(gIdx, qMakePair(gd->size, m_lastStart)); m_lastStart += gd->size; } + + //Shuffle stuff around + //TODO: In place shuffling because it's faster + QVector oldData(m_data); + QVector oldAttached(m_attachedData); + m_data.clear(); + m_data.resize(m_count); + m_attachedData.resize(m_count); + foreach(int gIdx, particleList){ + QSGParticleGroupData *gd = m_system->m_groupData[gIdx]; + for(int i=0; idata.size(); i++){//TODO: When group didn't exist before + int newIdx = m_particleStarts[gIdx].second + i; + int oldIdx = oldStarts[gIdx].second + i; + if(i >= oldStarts[gIdx].first || oldData.size() <= oldIdx){ + m_data[newIdx] = m_sentinel; + }else{ + m_data[newIdx] = oldData[oldIdx]; + m_attachedData[newIdx] = oldAttached[oldIdx]; + } + } + } + m_inResize = true; + reset(); + m_inResize = false; +} + + +void QSGParticlePainter::setCount(int c) +{ + Q_ASSERT(c >= 0); //XXX + if(c == m_count) + return; + int lastCount = m_count; + m_count = c; + resize(lastCount, m_count); + emit countChanged(); +} + +int QSGParticlePainter::count() +{ + return m_count; } int QSGParticlePainter::particleTypeIndex(QSGParticleData* d) diff --git a/src/declarative/particles/qsgparticlepainter_p.h b/src/declarative/particles/qsgparticlepainter_p.h index 6657d57501..e506018f53 100644 --- a/src/declarative/particles/qsgparticlepainter_p.h +++ b/src/declarative/particles/qsgparticlepainter_p.h @@ -61,8 +61,9 @@ class QSGParticlePainter : public QSGItem public: explicit QSGParticlePainter(QSGItem *parent = 0); - virtual void load(QSGParticleData*); - virtual void reload(QSGParticleData*); + //Data Interface to system + void load(QSGParticleData*); + void reload(QSGParticleData*); void setCount(int c); int count(); QSGParticleSystem* system() const @@ -94,68 +95,38 @@ void setParticles(QStringList arg) } private slots: void calcSystemOffset(); - void updateParticleStarts(); protected: + /* Reset resets all your internal data structures. But anything attached to a particle should + be in attached data. So reset + reloads should have no visible effect. + ###Hunt down all cases where we do a complete reset for convenience and be more targeted + */ virtual void reset(); - virtual void componentComplete(); + virtual void componentComplete(); + //Data interface to painters + QVector m_data; //Actually stored in arbitrary order, + QVector m_attachedData; //This data will be moved along with m_data in resizes (but you own it) + virtual void initialize(int){} + virtual void reload(int){}//If you need to do something on size changed, check m_data size in this? Or we reset you every time? QSGParticleSystem* m_system; friend class QSGParticleSystem; int m_count; bool m_pleaseReset; QStringList m_particles; - QHash > m_particleStarts; //Group, size, idx - int m_lastStart; QPointF m_systemOffset; - template //just convenience - void vertexCopy(VertexStruct &b, const ParticleVertex& a) - { - b.x = a.x - m_systemOffset.x(); - b.y = a.y - m_systemOffset.y(); - b.t = a.t; - b.lifeSpan = a.lifeSpan; - b.size = a.size; - b.endSize = a.endSize; - b.sx = a.sx; - b.sy = a.sy; - b.ax = a.ax; - b.ay = a.ay; - } - - //###Abstracted primarily for code reuse. Demote to subclasses? - int particleTypeIndex(QSGParticleData*); - virtual void resize(int oldCount, int newCount); - template - void groupShuffle(QVector &v, const T& zero)//Must be called inside resize - { - //TODO: In place shuffling because it's faster - QVector v0(v); - v.clear(); - v.resize(m_count); - int lastStart = 0; - QList particleList; - if(m_particles.isEmpty()) - particleList << 0; - foreach(const QString &s, m_particles) - particleList << m_system->m_groupIds[s]; - - foreach(int gIdx, particleList){ - QSGParticleGroupData *gd = m_system->m_groupData[gIdx]; - for(int i=0; idata.size(); i++){//TODO: When group didn't exist before - int newIdx = lastStart + i;//Have to make the same way as in updateParticleStarts - if(i >= m_particleStarts[gIdx].first || v0.size() <= m_particleStarts[gIdx].second + i) - v[newIdx] = zero; - else - v[newIdx] = v0[m_particleStarts[gIdx].second + i]; - } - lastStart += gd->size; - } - } private: + int m_lastStart; + QHash > m_particleStarts; + int particleTypeIndex(QSGParticleData* d);//Now private + void resize(int, int); + + QSGParticleData* m_sentinel; + //QVector m_shadowData;//For when we implement overwrite: false + bool m_inResize; }; QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp index a93f2a5eba..2dc21299cf 100644 --- a/src/declarative/particles/qsgparticlesystem.cpp +++ b/src/declarative/particles/qsgparticlesystem.cpp @@ -54,15 +54,33 @@ QSGParticleData::QSGParticleData() , e(0) , index(0) { - pv.x = 0; - pv.y = 0; - pv.t = -1; - pv.size = 0; - pv.endSize = 0; - pv.sx = 0; - pv.sy = 0; - pv.ax = 0; - pv.ay = 0; + x = 0; + y = 0; + t = -1; + size = 0; + endSize = 0; + sx = 0; + sy = 0; + ax = 0; + ay = 0; + xx = 1; + xy = 0; + yx = 0; + yy = 1; + rotation = 0; + rotationSpeed = 0; + autoRotate = 0; + animIdx = -1; + frameDuration = 1; + frameCount = 0; + animT = -1; + color.r = 255; + color.g = 255; + color.b = 255; + color.a = 255; + r = 0; + delegate = 0; + modelIndex = -1; } QSGParticleSystem::QSGParticleSystem(QSGItem *parent) : @@ -275,8 +293,8 @@ void QSGParticleSystem::emitParticle(QSGParticleData* pd) //Account for relative emitter position QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0)); if(!offset.isNull()){ - pd->pv.x += offset.x(); - pd->pv.y += offset.y(); + pd->x += offset.x(); + pd->y += offset.y(); } foreach(QSGParticleAffector *a, m_affectors) @@ -323,107 +341,107 @@ qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p) //sets the x accleration without affecting the instantaneous x velocity or position void QSGParticleData::setInstantaneousAX(qreal ax) { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - qreal sx = (pv.sx + t*pv.ax) - t*ax; - qreal ex = pv.x + pv.sx * t + 0.5 * pv.ax * t * t; + qreal t = (system->m_timeInt / 1000.0) - this->t; + qreal sx = (this->sx + t*this->ax) - t*ax; + qreal ex = this->x + this->sx * t + 0.5 * this->ax * t * t; qreal x = ex - t*sx - 0.5 * t*t*ax; - pv.ax = ax; - pv.sx = sx; - pv.x = x; + this->ax = ax; + this->sx = sx; + this->x = x; } //sets the x velocity without affecting the instantaneous x postion void QSGParticleData::setInstantaneousSX(qreal vx) { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - qreal sx = vx - t*pv.ax; - qreal ex = pv.x + pv.sx * t + 0.5 * pv.ax * t * t; - qreal x = ex - t*sx - 0.5 * t*t*pv.ax; + qreal t = (system->m_timeInt / 1000.0) - this->t; + qreal sx = vx - t*this->ax; + qreal ex = this->x + this->sx * t + 0.5 * this->ax * t * t; + qreal x = ex - t*sx - 0.5 * t*t*this->ax; - pv.sx = sx; - pv.x = x; + this->sx = sx; + this->x = x; } //sets the instantaneous x postion void QSGParticleData::setInstantaneousX(qreal x) { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - pv.x = x - t*pv.sx - 0.5 * t*t*pv.ax; + qreal t = (system->m_timeInt / 1000.0) - this->t; + this->x = x - t*this->sx - 0.5 * t*t*this->ax; } //sets the y accleration without affecting the instantaneous y velocity or position void QSGParticleData::setInstantaneousAY(qreal ay) { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - qreal sy = (pv.sy + t*pv.ay) - t*ay; - qreal ey = pv.y + pv.sy * t + 0.5 * pv.ay * t * t; + qreal t = (system->m_timeInt / 1000.0) - this->t; + qreal sy = (this->sy + t*this->ay) - t*ay; + qreal ey = this->y + this->sy * t + 0.5 * this->ay * t * t; qreal y = ey - t*sy - 0.5 * t*t*ay; - pv.ay = ay; - pv.sy = sy; - pv.y = y; + this->ay = ay; + this->sy = sy; + this->y = y; } //sets the y velocity without affecting the instantaneous y position void QSGParticleData::setInstantaneousSY(qreal vy) { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - //qDebug() << t << (system->m_timeInt/1000.0) << pv.x << pv.sx << pv.ax << pv.x + pv.sx * t + 0.5 * pv.ax * t * t; - qreal sy = vy - t*pv.ay; - qreal ey = pv.y + pv.sy * t + 0.5 * pv.ay * t * t; - qreal y = ey - t*sy - 0.5 * t*t*pv.ay; + qreal t = (system->m_timeInt / 1000.0) - this->t; + //qDebug() << t << (system->m_timeInt/1000.0) << this->x << this->sx << this->ax << this->x + this->sx * t + 0.5 * this->ax * t * t; + qreal sy = vy - t*this->ay; + qreal ey = this->y + this->sy * t + 0.5 * this->ay * t * t; + qreal y = ey - t*sy - 0.5 * t*t*this->ay; - pv.sy = sy; - pv.y = y; + this->sy = sy; + this->y = y; } //sets the instantaneous Y position void QSGParticleData::setInstantaneousY(qreal y) { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - pv.y = y - t*pv.sy - 0.5 * t*t*pv.ay; + qreal t = (system->m_timeInt / 1000.0) - this->t; + this->y = y - t*this->sy - 0.5 * t*t*this->ay; } qreal QSGParticleData::curX() const { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - return pv.x + pv.sx * t + 0.5 * pv.ax * t * t; + qreal t = (system->m_timeInt / 1000.0) - this->t; + return this->x + this->sx * t + 0.5 * this->ax * t * t; } qreal QSGParticleData::curSX() const { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - return pv.sx + t*pv.ax; + qreal t = (system->m_timeInt / 1000.0) - this->t; + return this->sx + t*this->ax; } qreal QSGParticleData::curY() const { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - return pv.y + pv.sy * t + 0.5 * pv.ay * t * t; + qreal t = (system->m_timeInt / 1000.0) - this->t; + return y + sy * t + 0.5 * ay * t * t; } qreal QSGParticleData::curSY() const { - qreal t = (system->m_timeInt / 1000.0) - pv.t; - return pv.sy + t*pv.ay; + qreal t = (system->m_timeInt / 1000.0) - this->t; + return sy + t*ay; } void QSGParticleData::debugDump() { qDebug() << "Particle" << group - << "Pos: " << pv.x << "," << pv.y - << "Vel: " << pv.sx << "," << pv.sy - << "Acc: " << pv.ax << "," << pv.ay - << "Size: " << pv.size << "," << pv.endSize - << "Time: " << pv.t << "," < (system->m_timeInt/1000.0); + return (t + lifeSpan) > (system->m_timeInt/1000.0); } QT_END_NAMESPACE diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h index 4c690ff7a8..45b4e164f1 100644 --- a/src/declarative/particles/qsgparticlesystem_p.h +++ b/src/declarative/particles/qsgparticlesystem_p.h @@ -173,28 +173,18 @@ private: QSignalMapper m_emitterMapper; }; -//TODO: Clean up all this into ParticleData - -struct ParticleVertex { - float x; - float y; - float t; - float lifeSpan; - float size; - float endSize; - float sx; - float sy; - float ax; - float ay; - //TODO: Need opacity over life control. More variable size over life? +struct Color4ub { + uchar r; + uchar g; + uchar b; + uchar a; }; class QSGParticleData{ public: + //TODO: QObject like memory management (without the cost, just attached to system) QSGParticleData(); - ParticleVertex pv; - //Convenience functions for working backwards, because parameters are from the start of particle life //If setting multiple parameters at once, doing the conversion yourself will be faster. @@ -222,6 +212,35 @@ public: QSGParticleSystem* system; int index; + //General Position Stuff + float x; + float y; + float t; + float lifeSpan; + float size; + float endSize; + float sx; + float sy; + float ax; + float ay; + + //Other stuff, now universally shared + Color4ub color; + float xx; + float xy; + float yx; + float yy; + float rotation; + float rotationSpeed; + float autoRotate;//Assume that GPUs prefer floats to bools + float animIdx; + float frameDuration; + float frameCount; + float animT; + float r; + QSGItem* delegate; + int modelIndex; + void debugDump(); bool stillAlive(); }; diff --git a/src/declarative/particles/qsgpointattractor.cpp b/src/declarative/particles/qsgpointattractor.cpp index 4c675237ba..0ee2fa4c27 100644 --- a/src/declarative/particles/qsgpointattractor.cpp +++ b/src/declarative/particles/qsgpointattractor.cpp @@ -70,17 +70,17 @@ bool QSGPointAttractorAffector::affectParticle(QSGParticleData *d, qreal dt) dy = ds * sin(theta); switch(m_physics){ case Position: - d->pv.x = (d->pv.x + dx); - d->pv.y = (d->pv.y + dy); + d->x = (d->x + dx); + d->y = (d->y + dy); break; case Acceleration: - d->setInstantaneousAX(d->pv.ax + dx); - d->setInstantaneousAY(d->pv.ay + dy); + d->setInstantaneousAX(d->ax + dx); + d->setInstantaneousAY(d->ay + dy); break; case Velocity: //also default default: - d->setInstantaneousSX(d->pv.sx + dx); - d->setInstantaneousSY(d->pv.sy + dy); + d->setInstantaneousSX(d->sx + dx); + d->setInstantaneousSY(d->sy + dy); } return true; diff --git a/src/declarative/particles/qsgwander.cpp b/src/declarative/particles/qsgwander.cpp index 6e56d6a05b..26bea4ee04 100644 --- a/src/declarative/particles/qsgwander.cpp +++ b/src/declarative/particles/qsgwander.cpp @@ -116,10 +116,10 @@ bool QSGWanderAffector::affectParticle(QSGParticleData* data, qreal dt) case Position: newX = data->curX() + dx; if(m_xVariance > qAbs(newX) ) - data->pv.x += dx; + data->x += dx; newY = data->curY() + dy; if(m_yVariance > qAbs(newY) ) - data->pv.y += dy; + data->y += dy; break; default: case Velocity: @@ -131,10 +131,10 @@ bool QSGWanderAffector::affectParticle(QSGParticleData* data, qreal dt) data->setInstantaneousSY(newY); break; case Acceleration: - newX = data->pv.ax + dx; + newX = data->ax + dx; if(m_xVariance > qAbs(newX) ) data->setInstantaneousAX(newX); - newY = data->pv.ay + dy; + newY = data->ay + dy; if(m_yVariance > qAbs(newY) ) data->setInstantaneousAY(newY); break; From 275384fcb5da79742cd427f5ab5ae881cd7bb56a Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Tue, 21 Jun 2011 15:21:35 +1000 Subject: [PATCH 39/48] Only print debugging when THREAD_DEBUG is enabled --- src/declarative/items/qsgcanvas.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp index 30dc5cab02..a27b14bcbf 100644 --- a/src/declarative/items/qsgcanvas.cpp +++ b/src/declarative/items/qsgcanvas.cpp @@ -2001,7 +2001,9 @@ QImage QSGCanvas::grabFrameBuffer() void QSGCanvasRenderThread::run() { +#ifdef THREAD_DEBUG qDebug("QML Rendering Thread Started"); +#endif renderer->makeCurrent(); From fad0a10d48f6d8c2cc485d9265c49792a3893eb4 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Tue, 21 Jun 2011 17:00:46 +1000 Subject: [PATCH 40/48] Drag items relative to their scene position. This preserves the absolute position of a dragged item when its parent is changed. --- src/declarative/items/qsgmousearea.cpp | 14 ++++++++++---- src/declarative/items/qsgmousearea_p_p.h | 3 +-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/declarative/items/qsgmousearea.cpp b/src/declarative/items/qsgmousearea.cpp index 8ed72190f8..6886ccca35 100644 --- a/src/declarative/items/qsgmousearea.cpp +++ b/src/declarative/items/qsgmousearea.cpp @@ -501,9 +501,11 @@ void QSGMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event) setHovered(true); if (d->drag && d->drag->target()) { + if (!d->moved) { - d->startX = drag()->target()->x(); - d->startY = drag()->target()->y(); + d->targetStartPos = d->drag->target()->parentItem() + ? d->drag->target()->parentItem()->mapToScene(d->drag->target()->pos()) + : d->drag->target()->pos(); } QPointF startLocalPos; @@ -539,8 +541,12 @@ void QSGMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event) } } + QPointF startPos = d->drag->target()->parentItem() + ? d->drag->target()->parentItem()->mapFromScene(d->targetStartPos) + : d->targetStartPos; + if (d->dragX && d->drag->active()) { - qreal x = (curLocalPos.x() - startLocalPos.x()) + d->startX; + qreal x = (curLocalPos.x() - startLocalPos.x()) + startPos.x(); if (x < drag()->xmin()) x = drag()->xmin(); else if (x > drag()->xmax()) @@ -548,7 +554,7 @@ void QSGMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event) drag()->target()->setX(x); } if (d->dragY && d->drag->active()) { - qreal y = (curLocalPos.y() - startLocalPos.y()) + d->startY; + qreal y = (curLocalPos.y() - startLocalPos.y()) + startPos.y(); if (y < drag()->ymin()) y = drag()->ymin(); else if (y > drag()->ymax()) diff --git a/src/declarative/items/qsgmousearea_p_p.h b/src/declarative/items/qsgmousearea_p_p.h index 11f7089503..0eb0444224 100644 --- a/src/declarative/items/qsgmousearea_p_p.h +++ b/src/declarative/items/qsgmousearea_p_p.h @@ -99,8 +99,7 @@ public: bool dragRejected : 1; QSGDrag *drag; QPointF startScene; - qreal startX; - qreal startY; + QPointF targetStartPos; QPointF lastPos; QDeclarativeNullableValue lastScenePos; Qt::MouseButton lastButton; From 0a1e84d3a7c1da0a25414da579bba5154f190e79 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 22 Jun 2011 11:20:43 +1000 Subject: [PATCH 41/48] Make QSGCanvas::grabFrameBuffer() more reliable --- src/declarative/items/qsgcanvas.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp index a27b14bcbf..0500b752c7 100644 --- a/src/declarative/items/qsgcanvas.cpp +++ b/src/declarative/items/qsgcanvas.cpp @@ -2096,7 +2096,7 @@ void QSGCanvasRenderThread::run() // but we don't want to lock an extra time. wake(); - if (!d->animationRunning && !isExternalUpdatePending && !shouldExit) { + if (!d->animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) { #ifdef THREAD_DEBUG printf(" RenderThread: nothing to do, going to sleep...\n"); #endif @@ -2335,7 +2335,7 @@ QImage QSGCanvasRenderThread::grab() doGrab = true; isPaintCompleted = false; while (isRunning() && !isPaintCompleted) { - if (!isRenderBlocked) + if (isRenderBlocked) wake(); wait(); } From 9dd181877888c2842d3254bea9d217f4c6a45273 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 22 Jun 2011 12:10:47 +1000 Subject: [PATCH 42/48] QSKIP crashing tests until QTBUG-20017 is fixed --- tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp | 1 + tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp b/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp index 7d74c0add8..40920e3c24 100644 --- a/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp +++ b/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp @@ -458,6 +458,7 @@ void tst_qsgtextedit::hAlign() void tst_qsgtextedit::hAlign_RightToLeft() { + QSKIP("QTBUG-20017", SkipAll); QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/horizontalAlignment_RightToLeft.qml")); QSGTextEdit *textEdit = canvas.rootObject()->findChild("text"); QVERIFY(textEdit != 0); diff --git a/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp b/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp index cbe14a5563..72d5288824 100644 --- a/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp +++ b/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp @@ -1053,6 +1053,7 @@ void tst_qsgtextinput::horizontalAlignment() void tst_qsgtextinput::horizontalAlignment_RightToLeft() { + QSKIP("QTBUG-20017", SkipAll); QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/horizontalAlignment_RightToLeft.qml")); QSGTextInput *textInput = canvas.rootObject()->findChild("text"); QVERIFY(textInput != 0); @@ -1446,6 +1447,7 @@ void tst_qsgtextinput::navigation() void tst_qsgtextinput::navigation_RTL() { + QSKIP("QTBUG-20017", SkipAll); QSGView canvas(QUrl::fromLocalFile(SRCDIR "/data/navigation.qml")); canvas.show(); canvas.setFocus(); From 6938289ce3e1e29cc81f0e007390e0b5f6ef1a43 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 22 Jun 2011 13:04:30 +1000 Subject: [PATCH 43/48] Fix autotests --- .../tst_qdeclarativeimage.cpp | 1 - .../tst_qdeclarativeinstruction.cpp | 3 ++- .../qsgtextedit/tst_qsgtextedit.cpp | 5 +---- .../qsgtextinput/tst_qsgtextinput.cpp | 20 ++++++++++--------- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp index 8044b8097a..b5aaa007e9 100644 --- a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp +++ b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp @@ -328,7 +328,6 @@ void tst_qdeclarativeimage::mirror() p_e.drawPixmap(QRect(0, 0, width, height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height())); break; case QDeclarativeImage::PreserveAspectFit: - QEXPECT_FAIL("", "QTBUG-19538", Continue); p_e.drawPixmap(QRect(25, 0, width / (width/height), height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height())); break; case QDeclarativeImage::PreserveAspectCrop: diff --git a/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp b/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp index fda16409b7..1aa9a5d5b1 100644 --- a/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp +++ b/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp @@ -332,6 +332,7 @@ void tst_qdeclarativeinstruction::dump() i.storeScriptString.propertyIndex = 24; i.storeScriptString.value = 3; i.storeScriptString.scope = 1; + i.storeScriptString.bindingId = 3; data->addInstruction(i); } @@ -535,7 +536,7 @@ void tst_qdeclarativeinstruction::dump() << "25\t\tSTORE_VARIANT_OBJECT\t22" << "26\t\tSTORE_INTERFACE\t\t23" << "27\t\tSTORE_SIGNAL\t\t2\t3\t\t\"console.log(1921)\"" - << "28\t\tSTORE_SCRIPT_STRING\t24\t3\t1\t0" + << "28\t\tSTORE_SCRIPT_STRING\t24\t3\t1\t3" << "29\t\tASSIGN_SIGNAL_OBJECT\t0\t\t\t\"mySignal\"" << "30\t\tASSIGN_CUSTOMTYPE\t25\t6\t9" << "31\t\tSTORE_BINDING\t26\t3\t2" diff --git a/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp b/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp index 40920e3c24..ea9260772e 100644 --- a/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp +++ b/tests/auto/declarative/qsgtextedit/tst_qsgtextedit.cpp @@ -408,10 +408,7 @@ void tst_qsgtextedit::alignments() ob->setProperty("horizontalAlignment",hAlign); ob->setProperty("verticalAlignment",vAlign); QTRY_COMPARE(ob->property("running").toBool(),false); - QImage actual(canvas.width(), canvas.height(), QImage::Format_RGB32); - actual.fill(qRgb(255,255,255)); - QPainter p(&actual); - canvas.render(&p); + QImage actual = canvas.grabFrameBuffer(); expectfile = createExpectedFileIfNotFound(expectfile, actual); diff --git a/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp b/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp index 72d5288824..77427253a6 100644 --- a/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp +++ b/tests/auto/declarative/qsgtextinput/tst_qsgtextinput.cpp @@ -1037,12 +1037,7 @@ void tst_qsgtextinput::horizontalAlignment() QObject *ob = canvas.rootObject(); QVERIFY(ob != 0); ob->setProperty("horizontalAlignment",hAlign); - QImage actual(canvas.width(), canvas.height(), QImage::Format_RGB32); - actual.fill(qRgb(255,255,255)); - { - QPainter p(&actual); - canvas.render(&p); - } + QImage actual = canvas.grabFrameBuffer(); expectfile = createExpectedFileIfNotFound(expectfile, actual); @@ -1736,7 +1731,7 @@ void tst_qsgtextinput::cursorRectangle() // Check the cursor rectangle remains within the input bounding rect when auto scrolling. QVERIFY(r.left() < input.boundingRect().width()); - QVERIFY(r.right() >= input.width()); + QVERIFY(r.right() >= input.width() - error); for (int i = 6; i < text.length(); ++i) { input.setCursorPosition(i); @@ -2221,18 +2216,25 @@ void tst_qsgtextinput::preeditAutoScroll() QCOMPARE(input->positionAt(0), 0); QCOMPARE(input->positionAt(input->width()), 5); + // some tolerance for different fonts. +#ifdef Q_OS_LINUX + const int error = 2; +#else + const int error = 5; +#endif + // test if the preedit is larger than the text input that the // character preceding the cursor is still visible. qreal x = input->positionToRectangle(0).x(); for (int i = 0; i < 3; ++i) { ic.sendPreeditText(preeditText, i + 1); - QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i))); + QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error); QVERIFY(input->positionToRectangle(0).x() < x); x = input->positionToRectangle(0).x(); } for (int i = 1; i >= 0; --i) { ic.sendPreeditText(preeditText, i + 1); - QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i))); + QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error); QVERIFY(input->positionToRectangle(0).x() > x); x = input->positionToRectangle(0).x(); } From 26a18e5d03310dc69bfe97bac1ff512b3d62c475 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 22 Jun 2011 18:11:06 +1000 Subject: [PATCH 44/48] Correctly wait for render thread to exit --- src/declarative/items/qsgcanvas.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp index 0500b752c7..97d643dae6 100644 --- a/src/declarative/items/qsgcanvas.cpp +++ b/src/declarative/items/qsgcanvas.cpp @@ -2316,6 +2316,13 @@ void QSGCanvasRenderThread::stopRenderThread() } unlockInGui(); + +#ifdef THREAD_DEBUG + printf("GUI: waiting for render thread to terminate..\n"); +#endif + // Actually wait for the thread to terminate. Otherwise we can delete it + // too early and crash. + QThread::wait(); } From f153846b160fc6a18d513e492ed671aeb21a3061 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 22 Jun 2011 18:39:22 +1000 Subject: [PATCH 45/48] Skip unreliable test --- tests/auto/declarative/qsglistview/tst_qsglistview.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/declarative/qsglistview/tst_qsglistview.cpp b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp index e32353ad53..c2bcf6dd40 100644 --- a/tests/auto/declarative/qsglistview/tst_qsglistview.cpp +++ b/tests/auto/declarative/qsglistview/tst_qsglistview.cpp @@ -1186,6 +1186,8 @@ void tst_QSGListView::sectionsDelegate() void tst_QSGListView::currentIndex() { + QSKIP("QTBUG-20020", SkipAll); + TestModel model; for (int i = 0; i < 30; i++) model.addItem("Item" + QString::number(i), QString::number(i)); From b35d6b475333fc2fabd0e91f1bfcf2f65aa4d4f4 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Thu, 23 Jun 2011 11:20:00 +1000 Subject: [PATCH 46/48] Fix regression against 4.7 Task-number: QTBUG-19136 --- src/declarative/qml/qdeclarativevme.cpp | 1 + .../qdeclarativeecmascript/data/importScope.1.js | 1 + .../qdeclarativeecmascript/data/importScope.2.js | 3 +++ .../qdeclarativeecmascript/data/importScope.qml | 7 +++++++ .../tst_qdeclarativeecmascript.cpp | 14 ++++++++++++++ 5 files changed, 26 insertions(+) create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/importScope.1.js create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/importScope.2.js create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/importScope.qml diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp index a9b303c94f..8959f1775f 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -988,6 +988,7 @@ QScriptValue QDeclarativeVME::run(QDeclarativeContextData *parentCtxt, QDeclarat ctxt->imports = script->importCache; } else { ctxt->imports = parentCtxt->imports; + ctxt->importedScripts = parentCtxt->importedScripts; } if (ctxt->imports) { diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/importScope.1.js b/tests/auto/declarative/qdeclarativeecmascript/data/importScope.1.js new file mode 100644 index 0000000000..4c556f9e96 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/importScope.1.js @@ -0,0 +1 @@ +var value = 240 diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/importScope.2.js b/tests/auto/declarative/qdeclarativeecmascript/data/importScope.2.js new file mode 100644 index 0000000000..291fb9d2cc --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/importScope.2.js @@ -0,0 +1,3 @@ +function getValue() { + return ImportScope1.value +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/importScope.qml b/tests/auto/declarative/qdeclarativeecmascript/data/importScope.qml new file mode 100644 index 0000000000..a72847f0d7 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/importScope.qml @@ -0,0 +1,7 @@ +import QtQuick 1.0 +import "importScope.1.js" as ImportScope1 +import "importScope.2.js" as ImportScope2 + +QtObject { + property int test: ImportScope2.getValue() +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index cabaddeeb7..3c4c336822 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -108,6 +108,7 @@ private slots: void aliasPropertyAndBinding(); void nonExistentAttachedObject(); void scope(); + void importScope(); void signalParameterTypes(); void objectsCompareAsEqual(); void dynamicCreation_data(); @@ -959,6 +960,19 @@ void tst_qdeclarativeecmascript::scope() } } +// In 4.7, non-library javascript files that had no imports shared the imports of their +// importing context +void tst_qdeclarativeecmascript::importScope() +{ + QDeclarativeComponent component(&engine, TEST_FILE("importScope.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toInt(), 240); + + delete o; +} + /* Tests that "any" type passes through a synthesized signal parameter. This is essentially a test of QDeclarativeMetaType::copy() From 098b7fb0ac84dbe65a8861cce76e036f0b9198d6 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Thu, 23 Jun 2011 10:40:39 +0200 Subject: [PATCH 47/48] Merge multiple lines of text into a single geometry node Long term, we might want to have this kind of logic in the QSGGeometry class through a grow() function or in the QSGRenderer, but we only have this one usecase where it actually makes sense right now, so I'm keeping it local. Change-Id: Ibbb0dd4a6e4b587154e26ffc2a34375fbb4a571d --- src/declarative/items/qsgtextnode.cpp | 32 ++++++---- src/declarative/items/qsgtextnode_p.h | 3 +- .../scenegraph/qsgdistancefieldglyphnode.cpp | 58 ++++++++++++++++--- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/declarative/items/qsgtextnode.cpp b/src/declarative/items/qsgtextnode.cpp index 6909d8cfca..b8dee689d5 100644 --- a/src/declarative/items/qsgtextnode.cpp +++ b/src/declarative/items/qsgtextnode.cpp @@ -152,18 +152,25 @@ void QSGTextNode::addTextDecorations(const QPointF &position, const QRawFont &fo } QSGGlyphNode *QSGTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color, - QSGText::TextStyle style, const QColor &styleColor) + QSGText::TextStyle style, const QColor &styleColor, QSGGlyphNode *prevNode) { - QSGGlyphNode *node = m_context->createGlyphNode(); - if (QSGDistanceFieldGlyphCache::distanceFieldEnabled()) { - QSGDistanceFieldGlyphNode *dfNode = static_cast(node); - dfNode->setStyle(style); - dfNode->setStyleColor(styleColor); - } - node->setGlyphs(position, glyphs); - node->setColor(color); + QSGGlyphNode *node = prevNode; + + if (!node) + node = m_context->createGlyphNode(); + + node->setGlyphs(position, glyphs); + + if (node != prevNode) { + if (QSGDistanceFieldGlyphCache::distanceFieldEnabled()) { + QSGDistanceFieldGlyphNode *dfNode = static_cast(node); + dfNode->setStyle(style); + dfNode->setStyleColor(styleColor); + } + node->setColor(color); + appendChildNode(node); + } - appendChildNode(node); if (glyphs.overline() || glyphs.strikeOut() || glyphs.underline()) { QPointF baseLine = node->baseLine(); @@ -193,10 +200,13 @@ void QSGTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout QSGText::TextStyle style, const QColor &styleColor) { QList glyphsList(textLayout->glyphRuns()); + + QSGGlyphNode *prevNode = 0; + for (int i=0; ipopulate(glyphIndexes.count(), glyphIndexes.constData()); Q_ASSERT(g->indexType() == GL_UNSIGNED_SHORT); - g->allocate(glyphIndexes.size() * 4, glyphIndexes.size() * 6); - QVector4D *vp = (QVector4D *)g->vertexData(); - ushort *ip = g->indexDataAsUShort(); + + int oldVertexCount = g->vertexCount(); + int oldIndexCount = g->indexCount(); + + // We could potentially move the realloc part into the QSGGeometry object as a + // grow() function... + + void *data = 0; + if (oldVertexCount && oldIndexCount) { + int byteSize = oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D) + + oldIndexCount * sizeof(quint16); + data = qMalloc(byteSize); + memcpy(data, g->vertexData(), byteSize); + } + + g->allocate(oldVertexCount + glyphIndexes.size() * 4, oldIndexCount + glyphIndexes.size() * 6); + + if (data) { + memcpy(g->vertexData(), data, oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D)); + memcpy(g->indexData(), ((char *) data) + oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D), + oldIndexCount * sizeof(quint16)); + qFree(data); + } + + QSGGeometry::TexturedPoint2D *vp = g->vertexDataAsTexturedPoint2D() + oldVertexCount; + ushort *ip = g->indexDataAsUShort() + oldIndexCount; QPointF margins(2, 2); QPointF texMargins = margins / m_glyph_cache->fontScale(); @@ -167,12 +190,12 @@ void QSGDistanceFieldGlyphNode::updateGeometry() m_baseLine = glyphPosition; int vi = i & 1 ? (glyphIndexes.size() + 1) / 2 + i / 2 : i / 2; - vp[4 * vi + 0] = QVector4D(cx1, cy1, tx1, ty1); - vp[4 * vi + 1] = QVector4D(cx2, cy1, tx2, ty1); - vp[4 * vi + 2] = QVector4D(cx1, cy2, tx1, ty2); - vp[4 * vi + 3] = QVector4D(cx2, cy2, tx2, ty2); + vp[4 * vi + 0].set(cx1, cy1, tx1, ty1); + vp[4 * vi + 1].set(cx2, cy1, tx2, ty1); + vp[4 * vi + 2].set(cx1, cy2, tx1, ty2); + vp[4 * vi + 3].set(cx2, cy2, tx2, ty2); - int o = i * 4; + int o = i * 4 + oldVertexCount; ip[6 * i + 0] = o + 0; ip[6 * i + 1] = o + 2; ip[6 * i + 2] = o + 3; @@ -181,6 +204,25 @@ void QSGDistanceFieldGlyphNode::updateGeometry() ip[6 * i + 5] = o + 0; } +// printf("Vertices:\n"); +// for (int v=0; vvertexCount(); ++v) { +// QSGGeometry::TexturedPoint2D *t = g->vertexDataAsTexturedPoint2D() + v; +// printf(" - %d -- %f %f -- %.3f %.3f\n", v, t->x, t->y, t->tx, t->ty); +// } + +// printf("Indices:\n"); +// for (int i=0; iindexCount();) { + +// printf(" - %[ ", i); +// printf("%d, ", g->indexDataAsUShort()[i++]); +// printf("%d, ", g->indexDataAsUShort()[i++]); +// printf("%d, ", g->indexDataAsUShort()[i++]); +// printf("%d, ", g->indexDataAsUShort()[i++]); +// printf("%d, ", g->indexDataAsUShort()[i++]); +// printf("%d", g->indexDataAsUShort()[i++]); +// printf(" ]\n"); +// } + setBoundingRect(boundingRect); markDirty(DirtyGeometry); } From a9853d2ffe177bf98eb7610b58bb1bbb44743510 Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Thu, 23 Jun 2011 14:13:24 +0200 Subject: [PATCH 48/48] Updated tst_nodestest to use the DirtyForceUpdate flag. Changed test to use DirtyForceUpdate instead of DirtyAll, and removed DirtyRenderOrder and DirtyAll enums which should not be used. --- src/declarative/particles/qsgcustomparticle.cpp | 2 -- src/declarative/scenegraph/coreapi/qsgnode.cpp | 4 ++-- src/declarative/scenegraph/coreapi/qsgnode.h | 2 -- tests/auto/declarative/node/tst_nodestest.cpp | 4 ++-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp index 09cfdda143..46f160ddf4 100644 --- a/src/declarative/particles/qsgcustomparticle.cpp +++ b/src/declarative/particles/qsgcustomparticle.cpp @@ -452,8 +452,6 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode() if (s.vertexCode.isEmpty()) s.vertexCode = qt_particles_default_vertex_code; m_material.setProgramSource(s); - node->markDirty(QSGNode::DirtyMaterial); - node->markDirty(QSGNode::DirtyAll); return node; } diff --git a/src/declarative/scenegraph/coreapi/qsgnode.cpp b/src/declarative/scenegraph/coreapi/qsgnode.cpp index bfe17ca72b..b295d25b70 100644 --- a/src/declarative/scenegraph/coreapi/qsgnode.cpp +++ b/src/declarative/scenegraph/coreapi/qsgnode.cpp @@ -458,9 +458,9 @@ void QSGNode::markDirty(DirtyFlags flags) int geometryCountDiff = 0; if (flags & DirtyNodeAdded) - geometryCountDiff = m_subtreeGeometryCount; + geometryCountDiff += m_subtreeGeometryCount; if (flags & DirtyNodeRemoved) - geometryCountDiff = -m_subtreeGeometryCount; + geometryCountDiff -= m_subtreeGeometryCount; QSGNode *p = m_parent; while (p) { diff --git a/src/declarative/scenegraph/coreapi/qsgnode.h b/src/declarative/scenegraph/coreapi/qsgnode.h index cee6b76869..80def63f21 100644 --- a/src/declarative/scenegraph/coreapi/qsgnode.h +++ b/src/declarative/scenegraph/coreapi/qsgnode.h @@ -82,11 +82,9 @@ public: DirtyNodeAdded = 0x0004, DirtyNodeRemoved = 0x0008, DirtyGeometry = 0x0010, - DirtyRenderOrder = 0x0020, DirtyMaterial = 0x0040, DirtyOpacity = 0x0080, DirtyForceUpdate = 0x0100, - DirtyAll = 0xffff, DirtyPropagationMask = DirtyMatrix | DirtyClipList diff --git a/tests/auto/declarative/node/tst_nodestest.cpp b/tests/auto/declarative/node/tst_nodestest.cpp index a1b0a6ac91..fc159b98e9 100644 --- a/tests/auto/declarative/node/tst_nodestest.cpp +++ b/tests/auto/declarative/node/tst_nodestest.cpp @@ -196,14 +196,14 @@ public: if (renderer->rootNode()->parent()) { // Mark the root dirty to build a clean state from the root and down - renderer->rootNode()->markDirty(QSGNode::DirtyAll); + renderer->rootNode()->markDirty(QSGNode::DirtyForceUpdate); } renderer->renderScene(); if (renderer->rootNode()->parent()) { // Mark the parent of the root dirty to force the root and down to be updated. - renderer->rootNode()->parent()->markDirty(QSGNode::DirtyAll); + renderer->rootNode()->parent()->markDirty(QSGNode::DirtyForceUpdate); } }