diff options
author | Warwick Allison <warwick.allison@nokia.com> | 2009-12-02 05:47:29 (GMT) |
---|---|---|
committer | Warwick Allison <warwick.allison@nokia.com> | 2009-12-02 05:47:29 (GMT) |
commit | d9735849834a5efc638dd026d6fe51d15dc528ee (patch) | |
tree | 3313bf43000feffb4cffe4e1f119fda45c60877f | |
parent | 289645d2bf46c1d6eace7a0d60389a9c10dd0c34 (diff) | |
parent | dd0959ed872bf0cda7ad88382abce0c4b20bbdea (diff) | |
download | Qt-d9735849834a5efc638dd026d6fe51d15dc528ee.zip Qt-d9735849834a5efc638dd026d6fe51d15dc528ee.tar.gz Qt-d9735849834a5efc638dd026d6fe51d15dc528ee.tar.bz2 |
Merge branch 'kinetic-declarativeui' of git@scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
Conflicts:
src/declarative/qml/qmlmetatype.cpp
33 files changed, 641 insertions, 286 deletions
diff --git a/examples/declarative/loader/loader.pro b/examples/declarative/loader/loader.pro index edd6267..84b2d21 100644 --- a/examples/declarative/loader/loader.pro +++ b/examples/declarative/loader/loader.pro @@ -1,5 +1,5 @@ SOURCES = main.cpp qmlfolderlistmodel.cpp -HEADERS = qmlfolderlistmodel.h qlistmodelinterface.h +HEADERS = qmlfolderlistmodel.h RESOURCES = loader.qrc QT += script declarative network diff --git a/examples/declarative/loader/qlistmodelinterface.h b/examples/declarative/loader/qlistmodelinterface.h deleted file mode 100644 index 738378e..0000000 --- a/examples/declarative/loader/qlistmodelinterface.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (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 either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** 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.0, 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. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QLISTMODELINTERFACE_H -#define QLISTMODELINTERFACE_H - -#include <QtCore/QHash> -#include <QtCore/QVariant> - - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class Q_DECLARATIVE_EXPORT QListModelInterface : public QObject -{ - Q_OBJECT - public: - QListModelInterface(QObject *parent = 0) : QObject(parent) {} - virtual ~QListModelInterface() {} - - virtual int count() const = 0; - virtual QHash<int,QVariant> data(int index, const QList<int>& roles = QList<int>()) const = 0; - virtual bool setData(int index, const QHash<int,QVariant>& values) - { Q_UNUSED(index); Q_UNUSED(values); return false; } - - virtual QList<int> roles() const = 0; - virtual QString toString(int role) const = 0; - - Q_SIGNALS: - void itemsInserted(int index, int count); - void itemsRemoved(int index, int count); - void itemsMoved(int from, int to, int count); - void itemsChanged(int index, int count, const QList<int> &roles); - - protected: - QListModelInterface(QObjectPrivate &dd, QObject *parent) - : QObject(dd, parent) {} -}; - - -QT_END_NAMESPACE - -QT_END_HEADER -#endif //QTREEMODELINTERFACE_H diff --git a/examples/declarative/loader/qmlfolderlistmodel.h b/examples/declarative/loader/qmlfolderlistmodel.h index f50719a..ac9568f 100644 --- a/examples/declarative/loader/qmlfolderlistmodel.h +++ b/examples/declarative/loader/qmlfolderlistmodel.h @@ -43,7 +43,7 @@ #define QMLFOLDERLISTMODEL_H #include <qml.h> -#include "qlistmodelinterface.h" +#include "../../../src/declarative/3rdparty/qlistmodelinterface_p.h" class QmlContext; class QModelIndex; diff --git a/examples/declarative/workerscript/workerscript.js b/examples/declarative/workerscript/workerscript.js index 060c04a..f76471f 100644 --- a/examples/declarative/workerscript/workerscript.js +++ b/examples/declarative/workerscript/workerscript.js @@ -1,5 +1,15 @@ -onmessage = function(message) { - print ("You clicked the " + message.rectangle + " rectangle."); - print ("The coordinates were: " + message.x + "," + message.y); +var lastx = 0; +var lasty = 0; + +WorkerScript.onMessage = function(message) { + var ydiff = message.y - lasty; + var xdiff = message.x - lastx; + + var total = Math.sqrt(ydiff * ydiff + xdiff * xdiff); + + lastx = message.x; + lasty = message.y; + + WorkerScript.sendMessage( {xmove: xdiff, ymove: ydiff, move: total} ); } diff --git a/examples/declarative/workerscript/workerscript.qml b/examples/declarative/workerscript/workerscript.qml index 3729022..e36d4d4 100644 --- a/examples/declarative/workerscript/workerscript.qml +++ b/examples/declarative/workerscript/workerscript.qml @@ -6,6 +6,12 @@ Rectangle { WorkerScript { id: myWorker source: "workerscript.js" + + onMessage: { + print("Moved " + messageObject.xmove + " along the X axis."); + print("Moved " + messageObject.ymove + " along the Y axis."); + print("Moved " + messageObject.move + " pixels."); + } } Rectangle { diff --git a/src/declarative/graphicsitems/qmlgraphicsflickable.cpp b/src/declarative/graphicsitems/qmlgraphicsflickable.cpp index e010081..d827cda 100644 --- a/src/declarative/graphicsitems/qmlgraphicsflickable.cpp +++ b/src/declarative/graphicsitems/qmlgraphicsflickable.cpp @@ -162,6 +162,7 @@ QmlGraphicsFlickablePrivate::QmlGraphicsFlickablePrivate() , interactive(true), deceleration(500), maxVelocity(5000), reportedVelocitySmoothing(100) , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0) , horizontalVelocity(this), verticalVelocity(this), vTime(0), visibleArea(0) + , flickDirection(QmlGraphicsFlickable::AutoFlickDirection) { fixupXEvent = QmlTimeLineEvent::timeLineEvent<QmlGraphicsFlickablePrivate, &QmlGraphicsFlickablePrivate::fixupX>(&_moveX, this); fixupYEvent = QmlTimeLineEvent::timeLineEvent<QmlGraphicsFlickablePrivate, &QmlGraphicsFlickablePrivate::fixupY>(&_moveY, this); @@ -576,6 +577,36 @@ QmlGraphicsFlickableVisibleArea *QmlGraphicsFlickable::visibleArea() return d->visibleArea; } +/*! + \qmlproperty enumeration Flickable::flickDirection + + This property determines which directions the view can be flicked. + + \list + \o AutoFlickDirection (default) - allows flicking vertically if the + \e viewportHeight is not equal to the \e height of the Flickable. + Allows flicking horizontally if the \e viewportWidth is not equal + to the \e width of the Flickable. + \o HorizontalFlick - allows flicking horizontally. + \o VerticalFlick - allows flicking vertically. + \o HorizontalAndVerticalFlick - allows flicking in both directions. + \endlist +*/ +QmlGraphicsFlickable::FlickDirection QmlGraphicsFlickable::flickDirection() const +{ + Q_D(const QmlGraphicsFlickable); + return d->flickDirection; +} + +void QmlGraphicsFlickable::setFlickDirection(FlickDirection direction) +{ + Q_D(QmlGraphicsFlickable); + if (direction != d->flickDirection) { + d->flickDirection = direction; + emit flickDirectionChanged(); + } +} + void QmlGraphicsFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event) { if (interactive && timeline.isActive() && (qAbs(velocityX) > 10 || qAbs(velocityY) > 10)) @@ -1039,12 +1070,18 @@ qreal QmlGraphicsFlickable::vHeight() const bool QmlGraphicsFlickable::xflick() const { - return vWidth() != width(); + Q_D(const QmlGraphicsFlickable); + if (d->flickDirection == QmlGraphicsFlickable::AutoFlickDirection) + return vWidth() != width(); + return d->flickDirection & QmlGraphicsFlickable::HorizontalFlick; } bool QmlGraphicsFlickable::yflick() const { - return vHeight() != height(); + Q_D(const QmlGraphicsFlickable); + if (d->flickDirection == QmlGraphicsFlickable::AutoFlickDirection) + return vHeight() != height(); + return d->flickDirection & QmlGraphicsFlickable::VerticalFlick; } bool QmlGraphicsFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) diff --git a/src/declarative/graphicsitems/qmlgraphicsflickable_p.h b/src/declarative/graphicsitems/qmlgraphicsflickable_p.h index 50248e1..9610ac1 100644 --- a/src/declarative/graphicsitems/qmlgraphicsflickable_p.h +++ b/src/declarative/graphicsitems/qmlgraphicsflickable_p.h @@ -70,6 +70,7 @@ class Q_DECLARATIVE_EXPORT QmlGraphicsFlickable : public QmlGraphicsItem Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration) Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) + Q_PROPERTY(FlickDirection flickDirection READ flickDirection WRITE setFlickDirection NOTIFY flickDirectionChanged) Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive) Q_PROPERTY(int pressDelay READ pressDelay WRITE setPressDelay) @@ -85,6 +86,8 @@ class Q_DECLARATIVE_EXPORT QmlGraphicsFlickable : public QmlGraphicsItem Q_PROPERTY(QmlList<QmlGraphicsItem *>* flickableChildren READ flickableChildren) Q_CLASSINFO("DefaultProperty", "flickableData") + Q_ENUMS(FlickDirection) + public: QmlGraphicsFlickable(QmlGraphicsItem *parent=0); ~QmlGraphicsFlickable(); @@ -135,6 +138,10 @@ public: QmlGraphicsItem *viewport(); + enum FlickDirection { AutoFlickDirection=0x00, HorizontalFlick=0x01, VerticalFlick=0x02, HorizontalAndVerticalFlick=0x03 }; + FlickDirection flickDirection() const; + void setFlickDirection(FlickDirection); + Q_SIGNALS: void viewportWidthChanged(); void viewportHeightChanged(); @@ -151,6 +158,7 @@ Q_SIGNALS: void verticalVelocityChanged(); void isAtBoundaryChanged(); void pageChanged(); + void flickDirectionChanged(); protected: virtual bool sceneEventFilter(QGraphicsItem *, QEvent *); diff --git a/src/declarative/graphicsitems/qmlgraphicsflickable_p_p.h b/src/declarative/graphicsitems/qmlgraphicsflickable_p_p.h index 210052e..104769d 100644 --- a/src/declarative/graphicsitems/qmlgraphicsflickable_p_p.h +++ b/src/declarative/graphicsitems/qmlgraphicsflickable_p_p.h @@ -134,6 +134,7 @@ public: int vTime; QmlTimeLine velocityTimeline; QmlGraphicsFlickableVisibleArea *visibleArea; + QmlGraphicsFlickable::FlickDirection flickDirection; void handleMousePressEvent(QGraphicsSceneMouseEvent *); void handleMouseMoveEvent(QGraphicsSceneMouseEvent *); diff --git a/src/declarative/graphicsitems/qmlgraphicsgridview.cpp b/src/declarative/graphicsitems/qmlgraphicsgridview.cpp index f223413..cb6e675 100644 --- a/src/declarative/graphicsitems/qmlgraphicsgridview.cpp +++ b/src/declarative/graphicsitems/qmlgraphicsgridview.cpp @@ -327,6 +327,7 @@ void QmlGraphicsGridViewPrivate::init() q->setFlag(QGraphicsItem::ItemIsFocusScope); QObject::connect(q, SIGNAL(widthChanged()), q, SLOT(sizeChange())); QObject::connect(q, SIGNAL(heightChanged()), q, SLOT(sizeChange())); + q->setFlickDirection(QmlGraphicsFlickable::VerticalFlick); } void QmlGraphicsGridViewPrivate::clear() @@ -1014,10 +1015,13 @@ void QmlGraphicsGridView::setFlow(Flow flow) Q_D(QmlGraphicsGridView); if (d->flow != flow) { d->flow = flow; - if (d->flow == LeftToRight) + if (d->flow == LeftToRight) { setViewportWidth(-1); - else + setFlickDirection(QmlGraphicsFlickable::VerticalFlick); + } else { setViewportHeight(-1); + setFlickDirection(QmlGraphicsFlickable::HorizontalFlick); + } d->clear(); d->updateGrid(); refill(); diff --git a/src/declarative/graphicsitems/qmlgraphicslistview.cpp b/src/declarative/graphicsitems/qmlgraphicslistview.cpp index c4edbb7..eb7a026 100644 --- a/src/declarative/graphicsitems/qmlgraphicslistview.cpp +++ b/src/declarative/graphicsitems/qmlgraphicslistview.cpp @@ -490,6 +490,7 @@ void QmlGraphicsListViewPrivate::init() QObject::connect(q, SIGNAL(heightChanged()), q, SLOT(refill())); QObject::connect(q, SIGNAL(widthChanged()), q, SLOT(refill())); QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); + q->setFlickDirection(QmlGraphicsFlickable::VerticalFlick); } void QmlGraphicsListViewPrivate::clear() @@ -1667,10 +1668,13 @@ void QmlGraphicsListView::setOrientation(QmlGraphicsListView::Orientation orient Q_D(QmlGraphicsListView); if (d->orient != orientation) { d->orient = orientation; - if (d->orient == QmlGraphicsListView::Vertical) + if (d->orient == QmlGraphicsListView::Vertical) { setViewportWidth(-1); - else + setFlickDirection(VerticalFlick); + } else { setViewportHeight(-1); + setFlickDirection(HorizontalFlick); + } d->clear(); refill(); emit orientationChanged(); @@ -2131,6 +2135,8 @@ void QmlGraphicsListView::trackedPositionChanged() pos = d->startPosition(); } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + d->highlightRangeEnd) { pos = d->endPosition() - d->size(); + if (pos < d->startPosition()) + pos = d->startPosition(); } else { if (trackedPos < viewPos + d->highlightRangeStart) { pos = trackedPos - d->highlightRangeStart; diff --git a/src/declarative/qml/qmlboundsignal.cpp b/src/declarative/qml/qmlboundsignal.cpp index 4a0577a..78bcd49 100644 --- a/src/declarative/qml/qmlboundsignal.cpp +++ b/src/declarative/qml/qmlboundsignal.cpp @@ -93,7 +93,7 @@ QmlAbstractBoundSignal::~QmlAbstractBoundSignal() QmlBoundSignal::QmlBoundSignal(QObject *scope, const QMetaMethod &signal, QObject *parent) -: m_expression(0), m_idx(signal.methodIndex()), m_params(0) +: m_expression(0), m_signal(signal), m_paramsValid(false), m_params(0) { // A cached evaluation of the QmlExpression::value() slot index. // @@ -103,16 +103,13 @@ QmlBoundSignal::QmlBoundSignal(QObject *scope, const QMetaMethod &signal, if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); QmlGraphics_setParent_noEvent(this, parent); - QMetaObject::connect(scope, m_idx, this, evaluateIdx); - - if (!signal.parameterTypes().isEmpty()) - m_params = new QmlBoundSignalParameters(signal, this); + QMetaObject::connect(scope, m_signal.methodIndex(), this, evaluateIdx); } QmlBoundSignal::QmlBoundSignal(QmlContext *ctxt, const QString &val, QObject *scope, const QMetaMethod &signal, QObject *parent) -: m_expression(0), m_idx(signal.methodIndex()), m_params(0) +: m_expression(0), m_signal(signal), m_paramsValid(false), m_params(0) { // A cached evaluation of the QmlExpression::value() slot index. // @@ -122,13 +119,10 @@ QmlBoundSignal::QmlBoundSignal(QmlContext *ctxt, const QString &val, if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); QmlGraphics_setParent_noEvent(this, parent); - QMetaObject::connect(scope, m_idx, this, evaluateIdx); + QMetaObject::connect(scope, m_signal.methodIndex(), this, evaluateIdx); m_expression = new QmlExpression(ctxt, val, scope); m_expression->setTrackChange(false); - - if (!signal.parameterTypes().isEmpty()) - m_params = new QmlBoundSignalParameters(signal, this); } QmlBoundSignal::~QmlBoundSignal() @@ -139,7 +133,7 @@ QmlBoundSignal::~QmlBoundSignal() int QmlBoundSignal::index() const { - return m_idx; + return m_signal.methodIndex(); } /*! @@ -174,6 +168,12 @@ QmlBoundSignal *QmlBoundSignal::cast(QObject *o) int QmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a) { if (c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) { + if (!m_paramsValid) { + if (!m_signal.parameterTypes().isEmpty()) + m_params = new QmlBoundSignalParameters(m_signal, this); + m_paramsValid = true; + } + if (m_params) m_params->setValues(a); if (m_expression) { QmlExpressionPrivate::get(m_expression)->value(m_params); diff --git a/src/declarative/qml/qmlboundsignal_p.h b/src/declarative/qml/qmlboundsignal_p.h index e801892..53a5a6b 100644 --- a/src/declarative/qml/qmlboundsignal_p.h +++ b/src/declarative/qml/qmlboundsignal_p.h @@ -55,6 +55,7 @@ #include <qmlexpression.h> #include <private/qobject_p.h> +#include <QtCore/qmetaobject.h> QT_BEGIN_NAMESPACE @@ -87,7 +88,8 @@ protected: private: QmlExpression *m_expression; - int m_idx; + QMetaMethod m_signal; + bool m_paramsValid; QmlBoundSignalParameters *m_params; }; diff --git a/src/declarative/qml/qmlcompileddata.cpp b/src/declarative/qml/qmlcompileddata.cpp index 44d2745..9b931c5 100644 --- a/src/declarative/qml/qmlcompileddata.cpp +++ b/src/declarative/qml/qmlcompileddata.cpp @@ -167,7 +167,8 @@ QmlCompiledData::~QmlCompiledData() if (importCache) importCache->release(); - qDeleteAll(programs); + qDeleteAll(cachedPrograms); + qDeleteAll(cachedClosures); } QObject *QmlCompiledData::TypeReference::createInstance(QmlContext *ctxt, const QBitField &bindings) const diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index ee0c484..8edf023 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -2529,12 +2529,26 @@ bool QmlCompiler::completeComponentBuild() // Pre-rewrite the expression QString expression = binding.expression.asScript(); + + // ### Optimize + QmlRewrite::SharedBindingTester sharableTest; + bool isSharable = sharableTest.isSharable(expression); + QmlRewrite::RewriteBinding rewriteBinding; expression = rewriteBinding(expression); quint32 length = expression.length(); - quint32 pc = output->programs.length(); - output->programs.append(0); + quint32 pc; + + if (isSharable) { + pc = output->cachedClosures.count(); + pc |= 0x80000000; + output->cachedClosures.append(0); + } else { + pc = output->cachedPrograms.length(); + output->cachedPrograms.append(0); + } + binding.compiledData = QByteArray((const char *)&pc, sizeof(quint32)) + QByteArray((const char *)&length, sizeof(quint32)) + diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index 6b41a5f..991ae81 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -113,7 +113,8 @@ public: QList<QByteArray> datas; QList<QmlParser::Location> locations; QList<QmlInstruction> bytecode; - QList<QScriptProgram *> programs; + QList<QScriptProgram *> cachedPrograms; + QList<QScriptValue *> cachedClosures; QList<QmlPropertyCache *> propertyCaches; QList<QmlIntegerCache *> contextCaches; QList<QmlParser::Object::ScriptBlock> scripts; diff --git a/src/declarative/qml/qmlcomponent.cpp b/src/declarative/qml/qmlcomponent.cpp index 1e03557..2e8e4ce 100644 --- a/src/declarative/qml/qmlcomponent.cpp +++ b/src/declarative/qml/qmlcomponent.cpp @@ -705,7 +705,7 @@ void QmlComponentPrivate::completeCreate() QmlEnginePrivate::SimpleList<QmlParserStatus> ps = parserStatus.at(ii); - for (int jj = 0; jj < ps.count; ++jj) { + for (int jj = ps.count - 1; jj >= 0; --jj) { QmlParserStatus *status = ps.at(jj); if (status && status->d) { status->d = 0; diff --git a/src/declarative/qml/qmlcontextscriptclass.cpp b/src/declarative/qml/qmlcontextscriptclass.cpp index acf6203..c740ce7 100644 --- a/src/declarative/qml/qmlcontextscriptclass.cpp +++ b/src/declarative/qml/qmlcontextscriptclass.cpp @@ -47,9 +47,27 @@ QT_BEGIN_NAMESPACE struct ContextData : public QScriptDeclarativeClass::Object { - ContextData(QmlContext *c, QObject *o) : context(c), scopeObject(o) {} + ContextData() : isSharedContext(true) {} + ContextData(QmlContext *c, QObject *o) : context(c), scopeObject(o), isSharedContext(false) {} QGuard<QmlContext> context; QGuard<QObject> scopeObject; + bool isSharedContext; + + QmlContext *getContext(QmlEngine *engine) { + if (isSharedContext) { + return QmlEnginePrivate::get(engine)->sharedContext; + } else { + return context.data(); + } + } + + QObject *getScope(QmlEngine *engine) { + if (isSharedContext) { + return QmlEnginePrivate::get(engine)->sharedScope; + } else { + return scopeObject.data(); + } + } }; /* @@ -73,13 +91,20 @@ QScriptValue QmlContextScriptClass::newContext(QmlContext *context, QObject *sco return newObject(scriptEngine, this, new ContextData(context, scopeObject)); } +QScriptValue QmlContextScriptClass::newSharedContext() +{ + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + return newObject(scriptEngine, this, new ContextData()); +} + QmlContext *QmlContextScriptClass::contextFromValue(const QScriptValue &v) { if (scriptClass(v) != this) return 0; ContextData *data = (ContextData *)object(v); - return data->context; + return data->getContext(engine); } QScriptClass::QueryFlags @@ -94,8 +119,8 @@ QmlContextScriptClass::queryProperty(Object *object, const Identifier &name, lastPropertyIndex = -1; lastDefaultObject = -1; - QmlContext *bindContext = ((ContextData *)object)->context.data(); - QObject *scopeObject = ((ContextData *)object)->scopeObject.data(); + QmlContext *bindContext = ((ContextData *)object)->getContext(engine); + QObject *scopeObject = ((ContextData *)object)->getScope(engine); if (!bindContext) return 0; diff --git a/src/declarative/qml/qmlcontextscriptclass_p.h b/src/declarative/qml/qmlcontextscriptclass_p.h index cf91242..d24dfa6 100644 --- a/src/declarative/qml/qmlcontextscriptclass_p.h +++ b/src/declarative/qml/qmlcontextscriptclass_p.h @@ -68,6 +68,7 @@ public: ~QmlContextScriptClass(); QScriptValue newContext(QmlContext *, QObject * = 0); + QScriptValue newSharedContext(); QmlContext *contextFromValue(const QScriptValue &); diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index 0221458..f136017 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -110,7 +110,7 @@ struct StaticQtMetaObject : public QObject QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) : rootContext(0), currentExpression(0), - isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0), globalClass(0), + isDebugging(false), contextClass(0), sharedContext(0), sharedScope(0), objectClass(0), valueTypeClass(0), globalClass(0), nodeListClass(0), namedNodeMapClass(0), sqlQueryClass(0), cleanup(0), erroredBindings(0), inProgressCreations(0), scriptEngine(this), workerScriptEngine(0), componentAttacheds(0), rootComponent(0), networkAccessManager(0), typeManager(e), uniqueId(1) @@ -160,6 +160,10 @@ QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) scriptEngine.globalObject().setProperty(QLatin1String("createComponent"), scriptEngine.newFunction(QmlEnginePrivate::createComponent, 1)); + // translation functions need to be installed + // before the global script class is constructed (QTBUG-6437) + scriptEngine.installTranslatorFunctions(); + globalClass = new QmlGlobalScriptClass(&scriptEngine); } @@ -226,8 +230,8 @@ void QmlEnginePrivate::init() Q_Q(QmlEngine); qRegisterMetaType<QVariant>("QVariant"); qRegisterMetaType<QmlScriptString>("QmlScriptString"); + qRegisterMetaType<QScriptValue>("QScriptValue"); - scriptEngine.installTranslatorFunctions(); contextClass = new QmlContextScriptClass(q); objectClass = new QmlObjectScriptClass(q); valueTypeClass = new QmlValueTypeScriptClass(q); diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 93fe4a7..9267a66 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -126,6 +126,8 @@ public: struct ImportedNamespace; QmlContextScriptClass *contextClass; + QmlContext *sharedContext; + QObject *sharedScope; QmlObjectScriptClass *objectClass; QmlValueTypeScriptClass *valueTypeClass; QmlTypeNameScriptClass *typeNameClass; diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp index d2d60ee..2239d77 100644 --- a/src/declarative/qml/qmlexpression.cpp +++ b/src/declarative/qml/qmlexpression.cpp @@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE QmlExpressionData::QmlExpressionData() : expressionFunctionValid(false), expressionRewritten(false), me(0), - trackChange(true), line(-1), guardList(0), guardListLength(0) + trackChange(true), isShared(false), line(-1), guardList(0), guardListLength(0) { } @@ -108,27 +108,47 @@ void QmlExpressionPrivate::init(QmlContext *ctxt, void *expr, QmlRefCount *rc, data->expression = QString::fromRawData((QChar *)(exprData + 3), exprData[2]); int progIdx = *(exprData + 1); + bool isShared = progIdx & 0x80000000; + progIdx &= 0x7FFFFFFF; + QmlEngine *engine = ctxt->engine(); QmlEnginePrivate *ep = QmlEnginePrivate::get(engine); QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + if (isShared) { + + if (!dd->cachedClosures.at(progIdx)) { + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + scriptContext->pushScope(ep->contextClass->newSharedContext()); + dd->cachedClosures[progIdx] = new QScriptValue(scriptEngine->evaluate(data->expression, data->url.toString(), data->line)); + scriptEngine->popContext(); + } + + data->expressionFunction = *dd->cachedClosures.at(progIdx); + data->isShared = true; + data->expressionFunctionValid = true; + + } else { + #if !defined(Q_OS_SYMBIAN) //XXX Why doesn't this work? - if (!dd->programs.at(progIdx)) { - dd->programs[progIdx] = - new QScriptProgram(data->expression, data->url.toString(), data->line); - } + if (!dd->cachedPrograms.at(progIdx)) { + dd->cachedPrograms[progIdx] = + new QScriptProgram(data->expression, data->url.toString(), data->line); + } #endif - QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); - scriptContext->pushScope(ep->contextClass->newContext(ctxt, me)); + QScriptContext *scriptContext = QScriptDeclarativeClass::pushCleanContext(scriptEngine); + scriptContext->pushScope(ep->contextClass->newContext(ctxt, me)); #if !defined(Q_OS_SYMBIAN) - data->expressionFunction = scriptEngine->evaluate(*dd->programs[progIdx]); + data->expressionFunction = scriptEngine->evaluate(*dd->cachedPrograms.at(progIdx)); #else - data->expressionFunction = scriptEngine->evaluate(data->expression); + data->expressionFunction = scriptEngine->evaluate(data->expression); #endif - data->expressionFunctionValid = true; - scriptEngine->popContext(); + data->expressionFunctionValid = true; + scriptEngine->popContext(); + } } data->QmlAbstractExpression::setContext(ctxt); @@ -328,8 +348,22 @@ QVariant QmlExpressionPrivate::evalQtScript(QObject *secondaryScope, bool *isUnd data->expressionFunctionValid = true; } + QmlContext *oldSharedContext = 0; + QObject *oldSharedScope = 0; + if (data->isShared) { + oldSharedContext = ep->sharedContext; + oldSharedScope = ep->sharedScope; + ep->sharedContext = data->context(); + ep->sharedScope = data->me; + } + QScriptValue svalue = data->expressionFunction.call(); + if (data->isShared) { + ep->sharedContext = oldSharedContext; + ep->sharedScope = oldSharedScope; + } + if (isUndefined) *isUndefined = svalue.isUndefined() || scriptEngine->hasUncaughtException(); diff --git a/src/declarative/qml/qmlexpression_p.h b/src/declarative/qml/qmlexpression_p.h index c6ba54d..dbb12ba 100644 --- a/src/declarative/qml/qmlexpression_p.h +++ b/src/declarative/qml/qmlexpression_p.h @@ -101,6 +101,8 @@ public: QObject *me; bool trackChange; + bool isShared; + QUrl url; int line; diff --git a/src/declarative/qml/qmlmetatype.cpp b/src/declarative/qml/qmlmetatype.cpp index 5172152..06927457 100644 --- a/src/declarative/qml/qmlmetatype.cpp +++ b/src/declarative/qml/qmlmetatype.cpp @@ -58,6 +58,7 @@ #include <qlocale.h> #include <ctype.h> #include <QtCore/qcryptographichash.h> +#include <QtScript/qscriptvalue.h> #include "qmlcustomparser_p.h" #ifdef QT_BOOTSTRAPPED @@ -929,6 +930,7 @@ QList<QmlType*> QmlMetaType::qmlTypes() #include <QtGui/qvector3d.h> #include <QtGui/qvector4d.h> #include <QtGui/qquaternion.h> +Q_DECLARE_METATYPE(QScriptValue); /*! Copies \a copy into \a data, assuming they both are of type \a type. If @@ -1138,6 +1140,9 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) if (type == qMetaTypeId<QVariant>()) { *static_cast<NS(QVariant) *>(data) = *static_cast<const NS(QVariant)*>(copy); return true; + } else if (type == qMetaTypeId<QScriptValue>()) { + *static_cast<NS(QScriptValue) *>(data) = *static_cast<const NS(QScriptValue)*>(copy); + return true; } else if (typeCategory(type) != Unknown) { *static_cast<void **>(data) = *static_cast<void* const *>(copy); return true; @@ -1340,6 +1345,9 @@ bool QmlMetaType::copy(int type, void *data, const void *copy) if (type == qMetaTypeId<QVariant>()) { *static_cast<NS(QVariant) *>(data) = NS(QVariant)(); return true; + } else if (type == qMetaTypeId<QScriptValue>()) { + *static_cast<NS(QScriptValue) *>(data) = NS(QScriptValue)(); + return true; } else if (typeCategory(type) != Unknown) { *static_cast<void **>(data) = 0; return true; diff --git a/src/declarative/qml/qmlobjectscriptclass.cpp b/src/declarative/qml/qmlobjectscriptclass.cpp index 5110ea0..7c4ce5b 100644 --- a/src/declarative/qml/qmlobjectscriptclass.cpp +++ b/src/declarative/qml/qmlobjectscriptclass.cpp @@ -229,12 +229,16 @@ QScriptValue QmlObjectScriptClass::property(QObject *obj, const Identifier &name } else if (lastData->flags & QmlPropertyCache::Data::IsQmlList) { return enginePriv->listClass->newList(obj, lastData->coreIndex, QmlListScriptClass::QmlListPtr); - } if (lastData->flags & QmlPropertyCache::Data::IsQObjectDerived) { + } else if (lastData->flags & QmlPropertyCache::Data::IsQObjectDerived) { QObject *rv = 0; void *args[] = { &rv, 0 }; QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); - return newQObject(rv, lastData->propType); + } else if (lastData->flags & QmlPropertyCache::Data::IsQScriptValue) { + QScriptValue rv = scriptEngine->nullValue(); + void *args[] = { &rv, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); + return rv; } else { QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj); return enginePriv->scriptValueFromVariant(var); diff --git a/src/declarative/qml/qmlpropertycache.cpp b/src/declarative/qml/qmlpropertycache.cpp index c5cdbaa..0e7da84 100644 --- a/src/declarative/qml/qmlpropertycache.cpp +++ b/src/declarative/qml/qmlpropertycache.cpp @@ -45,6 +45,8 @@ QT_BEGIN_NAMESPACE +Q_DECLARE_METATYPE(QScriptValue); + void QmlPropertyCache::Data::load(const QMetaProperty &p) { propType = p.userType(); @@ -61,6 +63,8 @@ void QmlPropertyCache::Data::load(const QMetaProperty &p) if (propType == qMetaTypeId<QmlBinding *>()) { flags |= Data::IsQmlBinding; + } else if (propType == qMetaTypeId<QScriptValue>()) { + flags |= Data::IsQScriptValue; } else if (p.isEnumType()) { flags |= Data::IsEnumType; } else { diff --git a/src/declarative/qml/qmlpropertycache_p.h b/src/declarative/qml/qmlpropertycache_p.h index 28dd66d..9265910 100644 --- a/src/declarative/qml/qmlpropertycache_p.h +++ b/src/declarative/qml/qmlpropertycache_p.h @@ -83,7 +83,8 @@ public: IsEnumType = 0x00000010, IsQmlList = 0x00000020, IsQList = 0x00000040, - IsQmlBinding = 0x00000080 + IsQmlBinding = 0x00000080, + IsQScriptValue = 0x00000100 }; Q_DECLARE_FLAGS(Flags, Flag) diff --git a/src/declarative/qml/qmlrewrite.cpp b/src/declarative/qml/qmlrewrite.cpp index dc90255..5fcebf4d 100644 --- a/src/declarative/qml/qmlrewrite.cpp +++ b/src/declarative/qml/qmlrewrite.cpp @@ -49,6 +49,22 @@ DEFINE_BOOL_CONFIG_OPTION(rewriteDump, QML_REWRITE_DUMP); namespace QmlRewrite { +bool SharedBindingTester::isSharable(const QString &code) +{ + Engine engine; + NodePool pool(QString(), &engine); + Lexer lexer(&engine); + Parser parser(&engine); + lexer.setCode(code, 0); + parser.parseStatement(); + if (!parser.statement()) + return false; + + _sharable = true; + AST::Node::acceptChild(parser.statement(), this); + return _sharable; +} + QString RewriteBinding::operator()(const QString &code, bool *ok) { Engine engine; diff --git a/src/declarative/qml/qmlrewrite_p.h b/src/declarative/qml/qmlrewrite_p.h index a5cb841..a04a0db 100644 --- a/src/declarative/qml/qmlrewrite_p.h +++ b/src/declarative/qml/qmlrewrite_p.h @@ -63,6 +63,17 @@ QT_BEGIN_NAMESPACE namespace QmlRewrite { using namespace QmlJS; +class SharedBindingTester : protected AST::Visitor +{ + bool _sharable; +public: + bool isSharable(const QString &code); + + virtual bool visit(AST::FunctionDeclaration *) { _sharable = false; return false; } + virtual bool visit(AST::FunctionExpression *) { _sharable = false; return false; } + virtual bool visit(AST::CallExpression *) { _sharable = false; return false; } +}; + class RewriteBinding: protected AST::Visitor { unsigned _position; diff --git a/src/declarative/qml/qmlworkerscript.cpp b/src/declarative/qml/qmlworkerscript.cpp index 75c67cc..a41d9c5 100644 --- a/src/declarative/qml/qmlworkerscript.cpp +++ b/src/declarative/qml/qmlworkerscript.cpp @@ -55,15 +55,15 @@ class WorkerDataEvent : public QEvent public: enum Type { WorkerData = QEvent::User }; - WorkerDataEvent(int workerId, QmlWorkerScriptEngine::Data *data); + WorkerDataEvent(int workerId, const QVariant &data); virtual ~WorkerDataEvent(); int workerId() const; - QmlWorkerScriptEngine::Data *takeData(); + QVariant data() const; private: int m_id; - QmlWorkerScriptEngine::Data *m_data; + QVariant m_data; }; class WorkerLoadEvent : public QEvent @@ -81,31 +81,64 @@ private: QUrl m_url; }; +class WorkerRemoveEvent : public QEvent +{ +public: + enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; + + WorkerRemoveEvent(int workerId); + + int workerId() const; + +private: + int m_id; +}; + class QmlWorkerScriptEnginePrivate : public QObject { public: QmlWorkerScriptEnginePrivate(); - QScriptEngine *workerEngine; + struct ScriptEngine : public QScriptEngine + { + ScriptEngine(QmlWorkerScriptEnginePrivate *parent) : p(parent) {} + QmlWorkerScriptEnginePrivate *p; + }; + ScriptEngine *workerEngine; + static QmlWorkerScriptEnginePrivate *get(QScriptEngine *e) { + return static_cast<ScriptEngine *>(e)->p; + } QMutex m_lock; QWaitCondition m_wait; + struct WorkerScript { + WorkerScript(); + + int id; + bool initialized; + QmlWorkerScript *owner; + QScriptValue object; + + QScriptValue callback; + }; + + QHash<int, WorkerScript *> workers; QScriptValue getWorker(int); int m_nextId; - QHash<int, QScriptValue> m_workers; static QVariant scriptValueToVariant(const QScriptValue &); static QScriptValue variantToScriptValue(const QVariant &, QScriptEngine *); - static QScriptValue onmessage(QScriptContext *ctxt, QScriptEngine *engine); + static QScriptValue onMessage(QScriptContext *ctxt, QScriptEngine *engine); + static QScriptValue sendMessage(QScriptContext *ctxt, QScriptEngine *engine); protected: virtual bool event(QEvent *); private: - void processMessage(int, QmlWorkerScriptEngine::Data *); + void processMessage(int, const QVariant &); void processLoad(int, const QUrl &); }; @@ -114,34 +147,75 @@ QmlWorkerScriptEnginePrivate::QmlWorkerScriptEnginePrivate() { } -QScriptValue QmlWorkerScriptEnginePrivate::onmessage(QScriptContext *ctxt, QScriptEngine *engine) +QScriptValue QmlWorkerScriptEnginePrivate::onMessage(QScriptContext *ctxt, QScriptEngine *engine) { + QmlWorkerScriptEnginePrivate *p = QmlWorkerScriptEnginePrivate::get(engine); + + int id = ctxt->thisObject().data().toVariant().toInt(); + + WorkerScript *script = p->workers.value(id); + if (!script) + return engine->undefinedValue(); + if (ctxt->argumentCount() >= 1) - ctxt->thisObject().setData(ctxt->argument(0)); + script->callback = ctxt->argument(0); + + return script->callback; +} + +QScriptValue QmlWorkerScriptEnginePrivate::sendMessage(QScriptContext *ctxt, QScriptEngine *engine) +{ + if (!ctxt->argumentCount()) + return engine->undefinedValue(); + + QmlWorkerScriptEnginePrivate *p = QmlWorkerScriptEnginePrivate::get(engine); + + int id = ctxt->thisObject().data().toVariant().toInt(); + + WorkerScript *script = p->workers.value(id); + if (!script) + return engine->undefinedValue(); - return ctxt->thisObject().data(); + p->m_lock.lock(); + if (script->owner) + QCoreApplication::postEvent(script->owner, + new WorkerDataEvent(0, scriptValueToVariant(ctxt->argument(0)))); + p->m_lock.unlock(); + + return engine->undefinedValue(); } QScriptValue QmlWorkerScriptEnginePrivate::getWorker(int id) { - QHash<int, QScriptValue>::Iterator iter = m_workers.find(id); - if (iter == m_workers.end()) { - QScriptValue worker = workerEngine->newObject(); + QHash<int, WorkerScript *>::ConstIterator iter = workers.find(id); + + if (iter == workers.end()) + return workerEngine->nullValue(); + + WorkerScript *script = *iter; + if (!script->initialized) { - worker.setProperty(QLatin1String("onmessage"), workerEngine->newFunction(onmessage), - QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + script->initialized = true; + script->object = workerEngine->newObject(); - iter = m_workers.insert(id, worker); + QScriptValue api = workerEngine->newObject(); + api.setData(script->id); + + api.setProperty(QLatin1String("onMessage"), workerEngine->newFunction(onMessage), + QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + api.setProperty(QLatin1String("sendMessage"), workerEngine->newFunction(sendMessage)); + + script->object.setProperty(QLatin1String("WorkerScript"), api); } - return *iter; + return script->object; } bool QmlWorkerScriptEnginePrivate::event(QEvent *event) { if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - processMessage(workerEvent->workerId(), workerEvent->takeData()); + processMessage(workerEvent->workerId(), workerEvent->data()); return true; } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event); @@ -152,19 +226,18 @@ bool QmlWorkerScriptEnginePrivate::event(QEvent *event) } } -void QmlWorkerScriptEnginePrivate::processMessage(int id, QmlWorkerScriptEngine::Data *data) +void QmlWorkerScriptEnginePrivate::processMessage(int id, const QVariant &data) { - QScriptValue worker = getWorker(id); - QScriptValue onmessage = worker.data(); + WorkerScript *script = workers.value(id); + if (!script) + return; - if (onmessage.isFunction()) { + if (script->callback.isFunction()) { QScriptValue args = workerEngine->newArray(1); - args.setProperty(0, variantToScriptValue(data->var, workerEngine)); + args.setProperty(0, variantToScriptValue(data, workerEngine)); - onmessage.call(worker, args); + script->callback.call(script->object, args); } - - if (data) delete data; } void QmlWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) @@ -190,14 +263,80 @@ void QmlWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) } } -WorkerDataEvent::WorkerDataEvent(int workerId, QmlWorkerScriptEngine::Data *data) +QVariant QmlWorkerScriptEnginePrivate::scriptValueToVariant(const QScriptValue &value) +{ + if (value.isBool()) { + return QVariant(value.toBool()); + } else if (value.isString()) { + return QVariant(value.toString()); + } else if (value.isNumber()) { + return QVariant((qreal)value.toNumber()); + } else if (value.isArray()) { + QVariantList list; + + quint32 length = (quint32)value.property("length").toNumber(); + + for (quint32 ii = 0; ii < length; ++ii) { + QVariant v = scriptValueToVariant(ii); + list << v; + } + + return QVariant(list); + } else if (value.isObject()) { + QVariantHash hash; + + QScriptValueIterator iter(value); + + while (iter.hasNext()) { + iter.next(); + hash.insert(iter.name(), scriptValueToVariant(iter.value())); + } + + return QVariant(hash); + } + + return QVariant(); + +} + +QScriptValue QmlWorkerScriptEnginePrivate::variantToScriptValue(const QVariant &value, QScriptEngine *engine) +{ + if (value.type() == QVariant::Bool) { + return QScriptValue(value.toBool()); + } else if (value.type() == QVariant::String) { + return QScriptValue(value.toString()); + } else if (value.type() == (QVariant::Type)QMetaType::QReal) { + return QScriptValue(value.toReal()); + } else if (value.type() == (QVariant::Type)QMetaType::QVariantList) { + QVariantList list = qvariant_cast<QVariantList>(value); + QScriptValue rv = engine->newArray(list.count()); + + for (quint32 ii = 0; ii < list.count(); ++ii) + rv.setProperty(ii, variantToScriptValue(list.at(ii), engine)); + + return rv; + } else if (value.type() == (QVariant::Type)QMetaType::QVariantHash) { + + QVariantHash hash = qvariant_cast<QVariantHash>(value); + + QScriptValue rv = engine->newObject(); + + for (QVariantHash::ConstIterator iter = hash.begin(); iter != hash.end(); ++iter) + rv.setProperty(iter.key(), variantToScriptValue(iter.value(), engine)); + + return rv; + } else { + return engine->nullValue(); + } +} + +WorkerDataEvent::WorkerDataEvent(int workerId, const QVariant &data) : QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) { } WorkerDataEvent::~WorkerDataEvent() { - if (m_data) { delete m_data; m_data = 0; } } int WorkerDataEvent::workerId() const @@ -205,11 +344,9 @@ int WorkerDataEvent::workerId() const return m_id; } -QmlWorkerScriptEngine::Data *WorkerDataEvent::takeData() +QVariant WorkerDataEvent::data() const { - QmlWorkerScriptEngine::Data *rv = m_data; - m_data = 0; - return rv; + return m_data; } WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) @@ -227,6 +364,16 @@ QUrl WorkerLoadEvent::url() const return m_url; } +WorkerRemoveEvent::WorkerRemoveEvent(int workerId) +: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) +{ +} + +int WorkerRemoveEvent::workerId() const +{ + return m_id; +} + QmlWorkerScriptEngine::QmlWorkerScriptEngine(QObject *parent) : QThread(parent), d(new QmlWorkerScriptEnginePrivate) { @@ -234,6 +381,7 @@ QmlWorkerScriptEngine::QmlWorkerScriptEngine(QObject *parent) start(QThread::LowPriority); d->m_wait.wait(&d->m_lock); d->moveToThread(this); + d->m_lock.unlock(); } QmlWorkerScriptEngine::~QmlWorkerScriptEngine() @@ -241,48 +389,44 @@ QmlWorkerScriptEngine::~QmlWorkerScriptEngine() delete d; d = 0; } -QmlWorkerScriptEngine::WorkerScript::WorkerScript() -: engine(0), id(0) +QmlWorkerScriptEnginePrivate::WorkerScript::WorkerScript() +: id(-1), initialized(false), owner(0) { } -void QmlWorkerScriptEngine::WorkerScript::executeUrl(const QUrl &url) +int QmlWorkerScriptEngine::registerWorkerScript(QmlWorkerScript *owner) { - engine->executeUrl(this, url); -} + QmlWorkerScriptEnginePrivate::WorkerScript *script = new QmlWorkerScriptEnginePrivate::WorkerScript; + script->id = d->m_nextId++; + script->owner = owner; -void QmlWorkerScriptEngine::WorkerScript::sendMessage(Data *data) -{ - engine->sendMessage(this, data); + d->m_lock.lock(); + d->workers.insert(script->id, script); + d->m_lock.unlock(); + + return script->id; } -void QmlWorkerScriptEngine::executeUrl(WorkerScript *script, const QUrl &data) +void QmlWorkerScriptEngine::removeWorkerScript(int id) { - QCoreApplication::postEvent(d, new WorkerLoadEvent(script->id, data)); + QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); } -/*! - Ownership of \a data transfers to QmlWorkerScriptEngine. It can not be modified by - the caller. -*/ -void QmlWorkerScriptEngine::sendMessage(WorkerScript *script, Data *data) +void QmlWorkerScriptEngine::executeUrl(int id, const QUrl &url) { - QCoreApplication::postEvent(d, new WorkerDataEvent(script->id, data)); + QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); } -QmlWorkerScriptEngine::WorkerScript *QmlWorkerScriptEngine::createWorkerScript() +void QmlWorkerScriptEngine::sendMessage(int id, const QVariant &data) { - WorkerScript *rv = new WorkerScript; - rv->engine = this; - rv->id = d->m_nextId++; - return rv; + QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); } void QmlWorkerScriptEngine::run() { d->m_lock.lock(); - d->workerEngine = new QScriptEngine; + d->workerEngine = new QmlWorkerScriptEnginePrivate::ScriptEngine(d); d->m_wait.wakeAll(); @@ -294,80 +438,13 @@ void QmlWorkerScriptEngine::run() } QmlWorkerScript::QmlWorkerScript(QObject *parent) -: QObject(parent), m_script(0) +: QObject(parent), m_engine(0), m_scriptId(-1) { } QmlWorkerScript::~QmlWorkerScript() { - if (m_script) { delete m_script; m_script = 0; } -} - -QVariant QmlWorkerScriptEnginePrivate::scriptValueToVariant(const QScriptValue &value) -{ - if (value.isBool()) { - return QVariant(value.toBool()); - } else if (value.isString()) { - return QVariant(value.toString()); - } else if (value.isNumber()) { - return QVariant((qreal)value.toNumber()); - } else if (value.isArray()) { - QVariantList list; - - quint32 length = (quint32)value.property("length").toNumber(); - - for (quint32 ii = 0; ii < length; ++ii) { - QVariant v = scriptValueToVariant(ii); - list << v; - } - - return QVariant(list); - } else if (value.isObject()) { - QVariantHash hash; - - QScriptValueIterator iter(value); - - while (iter.hasNext()) { - iter.next(); - hash.insert(iter.name(), scriptValueToVariant(iter.value())); - } - - return QVariant(hash); - } - - return QVariant(); - -} - -QScriptValue QmlWorkerScriptEnginePrivate::variantToScriptValue(const QVariant &value, QScriptEngine *engine) -{ - if (value.type() == QVariant::Bool) { - return QScriptValue(value.toBool()); - } else if (value.type() == QVariant::String) { - return QScriptValue(value.toString()); - } else if (value.type() == (QVariant::Type)QMetaType::QReal) { - return QScriptValue(value.toReal()); - } else if (value.type() == (QVariant::Type)QMetaType::QVariantList) { - QVariantList list = qvariant_cast<QVariantList>(value); - QScriptValue rv = engine->newArray(list.count()); - - for (quint32 ii = 0; ii < list.count(); ++ii) - rv.setProperty(ii, variantToScriptValue(list.at(ii), engine)); - - return rv; - } else if (value.type() == (QVariant::Type)QMetaType::QVariantHash) { - - QVariantHash hash = qvariant_cast<QVariantHash>(value); - - QScriptValue rv = engine->newObject(); - - for (QVariantHash::ConstIterator iter = hash.begin(); iter != hash.end(); ++iter) - rv.setProperty(iter.key(), variantToScriptValue(iter.value(), engine)); - - return rv; - } else { - return engine->nullValue(); - } + if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); } QUrl QmlWorkerScript::source() const @@ -382,37 +459,55 @@ void QmlWorkerScript::setSource(const QUrl &source) m_source = source; - if (m_script) - m_script->executeUrl(m_source); + if (m_engine) + m_engine->executeUrl(m_scriptId, m_source); emit sourceChanged(); } void QmlWorkerScript::sendMessage(const QScriptValue &message) { - if (!m_script) { + if (!m_engine) { qWarning("QmlWorkerScript: Attempt to send message before WorkerScript establishment"); return; } - QmlWorkerScriptEngine::Data *data = new QmlWorkerScriptEngine::Data; - data->var = QmlWorkerScriptEnginePrivate::scriptValueToVariant(message); - m_script->sendMessage(data); + m_engine->sendMessage(m_scriptId, QmlWorkerScriptEnginePrivate::scriptValueToVariant(message)); } void QmlWorkerScript::componentComplete() { - if (!m_script) { + if (!m_engine) { QmlEngine *engine = qmlEngine(this); if (!engine) { qWarning("QmlWorkerScript: componentComplete() called without qmlEngine() set"); return; } - m_script = QmlEnginePrivate::get(engine)->getWorkerScriptEngine()->createWorkerScript(); + + m_engine = QmlEnginePrivate::get(engine)->getWorkerScriptEngine(); + m_scriptId = m_engine->registerWorkerScript(this); if (m_source.isValid()) - m_script->executeUrl(m_source); + m_engine->executeUrl(m_scriptId, m_source); } } +bool QmlWorkerScript::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + QmlEngine *engine = qmlEngine(this); + if (engine) { + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); + QScriptValue value = + QmlWorkerScriptEnginePrivate::variantToScriptValue(workerEvent->data(), scriptEngine); + emit message(value); + } + return true; + } else { + return QObject::event(event); + } +} + + QML_DEFINE_TYPE(Qt, 4, 6, WorkerScript, QmlWorkerScript); diff --git a/src/declarative/qml/qmlworkerscript_p.h b/src/declarative/qml/qmlworkerscript_p.h index b85238d..96394ae 100644 --- a/src/declarative/qml/qmlworkerscript_p.h +++ b/src/declarative/qml/qmlworkerscript_p.h @@ -59,6 +59,7 @@ #include <QtScript/qscriptvalue.h> #include <QtCore/qurl.h> +class QmlWorkerScript; class QmlWorkerScriptEnginePrivate; class QmlWorkerScriptEngine : public QThread { @@ -67,26 +68,10 @@ public: QmlWorkerScriptEngine(QObject *parent = 0); virtual ~QmlWorkerScriptEngine(); - class Data; - class WorkerScript { - public: - void sendMessage(Data *); - void executeUrl(const QUrl &); - - private: - WorkerScript(); - friend class QmlWorkerScriptEngine; - QmlWorkerScriptEngine *engine; - int id; - }; - WorkerScript *createWorkerScript(); - - struct Data { - QVariant var; - }; - - void executeUrl(WorkerScript *, const QUrl &); - void sendMessage(WorkerScript *, Data *); + int registerWorkerScript(QmlWorkerScript *); + void removeWorkerScript(int); + void executeUrl(int, const QUrl &); + void sendMessage(int, const QVariant &); protected: virtual void run(); @@ -112,12 +97,15 @@ public slots: signals: void sourceChanged(); + void message(const QScriptValue &messageObject); protected: virtual void componentComplete(); + virtual bool event(QEvent *); private: - QmlWorkerScriptEngine::WorkerScript *m_script; + QmlWorkerScriptEngine *m_engine; + int m_scriptId; QUrl m_source; }; QML_DECLARE_TYPE(QmlWorkerScript); diff --git a/tests/benchmarks/declarative/qmltime/example.qml b/tests/benchmarks/declarative/qmltime/example.qml new file mode 100644 index 0000000..68889f0 --- /dev/null +++ b/tests/benchmarks/declarative/qmltime/example.qml @@ -0,0 +1,14 @@ +import Qt 4.6 +import QmlTime 1.0 as QmlTime + +Item { + + property string name: "Bob Smith" + + QmlTime.Timer { + component: Item { + Text { text: name } + } + } +} + diff --git a/tests/benchmarks/declarative/qmltime/qmltime.cpp b/tests/benchmarks/declarative/qmltime/qmltime.cpp new file mode 100644 index 0000000..27019f4 --- /dev/null +++ b/tests/benchmarks/declarative/qmltime/qmltime.cpp @@ -0,0 +1,133 @@ +#include <QmlEngine> +#include <QmlComponent> +#include <QDebug> +#include <QApplication> +#include <QTime> +#include <QmlContext> + +class Timer : public QObject +{ + Q_OBJECT + Q_PROPERTY(QmlComponent *component READ component WRITE setComponent); + +public: + Timer(); + + QmlComponent *component() const; + void setComponent(QmlComponent *); + + static Timer *timerInstance(); + + void run(uint); + +private: + QmlComponent *m_component; + static Timer *m_timer; +}; +QML_DECLARE_TYPE(Timer); +QML_DEFINE_TYPE(QmlTime, 1, 0, Timer, Timer); + +Timer *Timer::m_timer = 0; + +Timer::Timer() +: m_component(0) +{ + if (m_timer) + qWarning("Timer: Timer already registered"); + m_timer = this; +} + +QmlComponent *Timer::component() const +{ + return m_component; +} + +void Timer::setComponent(QmlComponent *c) +{ + m_component = c; +} + +Timer *Timer::timerInstance() +{ + return m_timer; +} + +void Timer::run(uint iterations) +{ + QmlContext context(qmlContext(this)); + + QTime t; + t.start(); + for (uint ii = 0; ii < iterations; ++ii) { + QObject *o = m_component->create(&context); + delete o; + } + + int e = t.elapsed(); + + qWarning() << "Total:" << e << "ms, Per iteration:" << qreal(e) / qreal(iterations) << "ms"; + +} + +void usage(const char *name) +{ + qWarning("Usage: %s [-iterations <count>] <qml file>", name); + exit(-1); +} + +int main(int argc, char ** argv) +{ + QApplication app(argc, argv); + + uint iterations = 1024; + QString filename; + + for (int ii = 1; ii < argc; ++ii) { + QByteArray arg(argv[ii]); + + if (arg == "-iterations") { + if (ii + 1 < argc) { + ++ii; + QByteArray its(argv[ii]); + bool ok = false; + iterations = its.toUInt(&ok); + if (!ok) + usage(argv[0]); + } else { + usage(argv[0]); + } + } else { + filename = QLatin1String(argv[ii]); + } + } + + QmlEngine engine; + QmlComponent component(&engine, filename); + if (component.isError()) { + qWarning() << component.errors(); + return -1; + } + + QObject *obj = component.create(); + if (!obj) { + qWarning() << component.errors(); + return -1; + } + + Timer *timer = Timer::timerInstance(); + if (!timer) { + qWarning() << "A Tester.Timer instance is required."; + return -1; + } + + if (!timer->component()) { + qWarning() << "The timer has no component"; + return -1; + } + + timer->run(iterations); + + return 0; +} + +#include "qmltime.moc" diff --git a/tests/benchmarks/declarative/qmltime/qmltime.pro b/tests/benchmarks/declarative/qmltime/qmltime.pro new file mode 100644 index 0000000..b077d1a --- /dev/null +++ b/tests/benchmarks/declarative/qmltime/qmltime.pro @@ -0,0 +1,8 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = qmltime +QT += declarative +macx:CONFIG -= app_bundle + +SOURCES += qmltime.cpp + |