From db4addcf3408140bb34fa8884c7192c1d9667be8 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Tue, 9 Jun 2009 12:46:10 +1000 Subject: Add dynamic object creation from Script. Can now create, inside script, objects from qml snippets and files. These objects can then be manipulated from script. Deleting these items is still being looked into. --- src/declarative/qml/qmlcomponent.cpp | 18 ++++++ src/declarative/qml/qmlcomponent.h | 11 ++-- src/declarative/qml/qmlengine.cpp | 122 +++++++++++++++++++++++++++++++++++ src/declarative/qml/qmlengine.h | 9 +++ 4 files changed, 156 insertions(+), 4 deletions(-) diff --git a/src/declarative/qml/qmlcomponent.cpp b/src/declarative/qml/qmlcomponent.cpp index 78137e8..267fba8 100644 --- a/src/declarative/qml/qmlcomponent.cpp +++ b/src/declarative/qml/qmlcomponent.cpp @@ -387,6 +387,24 @@ QmlComponent::QmlComponent(QmlComponentPrivate &dd, QObject *parent) } /*! + Create a script object instance from this component. Returns a null + script object if creation failed. It will create the instance in the + engine's \l {QmlEngine::rootContext()}{root context}. + + Similar to QmlComponent::create(), but creates an object suitable + for usage within scripts. +*/ +QScriptValue QmlComponent::createObject() +{ + Q_D(QmlComponent); + QObject* ret = create(); + if(ret) + return QmlEngine::qmlScriptObject(ret, d->engine); + else + return d->engine->scriptEngine()->nullValue(); +} + +/*! Create an object instance from this component. Returns 0 if creation failed. \a context specifies the context within which to create the object instance. diff --git a/src/declarative/qml/qmlcomponent.h b/src/declarative/qml/qmlcomponent.h index e7386d9..bb76c8b 100644 --- a/src/declarative/qml/qmlcomponent.h +++ b/src/declarative/qml/qmlcomponent.h @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -70,18 +71,20 @@ public: const QUrl &baseUrl=QUrl(), QObject *parent=0); virtual ~QmlComponent(); + Q_ENUMS( Status ); enum Status { Null, Ready, Loading, Error }; Status status() const; - bool isNull() const; - bool isReady() const; - bool isError() const; - bool isLoading() const; + Q_INVOKABLE bool isNull() const; + Q_INVOKABLE bool isReady() const; + Q_INVOKABLE bool isError() const; + Q_INVOKABLE bool isLoading() const; QList errors() const; QUrl url() const; + Q_INVOKABLE QScriptValue createObject(); virtual QObject *create(QmlContext *context = 0); virtual QObject *beginCreate(QmlContext *); virtual void completeCreate(); diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index 18f28ed..d8ca809 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -203,6 +203,13 @@ void QmlEnginePrivate::init() debugger->attachTo(&scriptEngine); } #endif + //###needed for the other funcs, but should it be exposed? + scriptEngine.globalObject().setProperty(QLatin1String("qmlEngine"), + scriptEngine.newQObject(q)); + scriptEngine.globalObject().setProperty(QLatin1String("evalQml"), + scriptEngine.newFunction(QmlEngine::createQMLObject, 1)); + scriptEngine.globalObject().setProperty(QLatin1String("createComponent"), + scriptEngine.newFunction(QmlEngine::createComponent, 1)); } QmlContext *QmlEnginePrivate::setCurrentBindContext(QmlContext *c) @@ -808,7 +815,122 @@ QmlEngine *QmlEngine::activeEngine() return engines->top(); } +/*! + Creates a QScriptValue allowing you to use \a object in QML script. + \a engine is the QmlEngine it is to be created in. + + The QScriptValue returned is a QtScript Object, not a QtScript QObject, due + to the special needs of QML requiring more functionality than a standard + QtScript QObject. + + You'll want to use this function if you are writing C++ code which + dynamically creates and returns objects when called from QtScript, + and these objects are visual items in the QML tree. + + \sa QmlEngine::newQObject() +*/ +QScriptValue QmlEngine::qmlScriptObject(QObject* object, QmlEngine* engine) +{ + return engine->scriptEngine()->newObject(new QmlObjectScriptClass(engine), + engine->scriptEngine()->newQObject(object)); +} + +/*! + This function is intended for use inside QML only. In C++ just create a + component object as usual. + + This function takes the URL of a QML file as its only argument. It returns + a component object which can be used to create and load that QML file. + + Example JavaScript: + \code + component = createComponent("Sprite.qml"); + if(component.isReady()){ + sprite = component.create(); + if(sprite == 0){ + // Error Handling + }else{ + sprite.parent = page; + sprite.x = 200; + //... + } + }else{ + // The finishCreation function does the same thing as the above + // if(component.isReady()) branch + component.statusChanged.connect(finishCreation); + } + \endcode + + Remember that QML files that might be loaded over the network cannot be + expected to be ready immediately. +*/ +QScriptValue QmlEngine::createComponent(QScriptContext *ctxt, QScriptEngine *engine) +{ + QmlComponent* c; + QmlEngine* activeEngine = qobject_cast( + engine->globalObject().property(QLatin1String("qmlEngine")).toQObject()); + if(ctxt->argumentCount() != 1 || !activeEngine){ + c = new QmlComponent(activeEngine); + }else{ + c = new QmlComponent(activeEngine, QUrl(ctxt->argument(0).toString()), + activeEngine); + } + return engine->newQObject(c); +} +/*! + Creates a new object from the specified string of qml. If a second argument + is provided, this is treated as the filepath that the qml came from. + + This function is intended for use inside QML only. It is intended to behave + similarly to eval, but for creating QML elements. Thus, it is called as + evalQml() in QtScript. + + Returns the created object, or null if there is an error. In the case of an + error, details of the error are output using qWarning(). +*/ +QScriptValue QmlEngine::createQMLObject(QScriptContext *ctxt, QScriptEngine *engine) +{ + QmlEngine* activeEngine = qobject_cast( + engine->globalObject().property(QLatin1String("qmlEngine")).toQObject()); + if(ctxt->argumentCount() < 1 || !activeEngine){ + if(ctxt->argumentCount() < 1){ + qWarning() << "createQMLObject requires a string argument."; + }else{ + qWarning() << "createQMLObject failed inexplicably."; + } + return engine->nullValue(); + } + + QString qml = ctxt->argument(0).toString(); + QUrl url; + if(ctxt->argumentCount() > 1) + url = QUrl(ctxt->argument(1).toString()); + QmlComponent component(activeEngine, qml.toUtf8(), url); + if(component.isError()) { + QList errors = component.errors(); + foreach (const QmlError &error, errors) { + qWarning() << error; + } + + return engine->nullValue(); + } + + QObject *obj = component.create(); + if(component.isError()) { + QList errors = component.errors(); + foreach (const QmlError &error, errors) { + qWarning() << error; + } + + return engine->nullValue(); + } + + if(obj){ + return qmlScriptObject(obj, activeEngine); + } + return engine->nullValue(); +} QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b) : q(b), ctxt(0), sseData(0), proxy(0), me(0), trackChange(false), line(-1), id(0), log(0) diff --git a/src/declarative/qml/qmlengine.h b/src/declarative/qml/qmlengine.h index 9382389..ca66097 100644 --- a/src/declarative/qml/qmlengine.h +++ b/src/declarative/qml/qmlengine.h @@ -44,6 +44,7 @@ #include #include +#include QT_BEGIN_HEADER @@ -57,6 +58,7 @@ class QmlExpression; class QmlContext; class QUrl; class QScriptEngine; +class QScriptContext; class QNetworkAccessManager; class Q_DECLARATIVE_EXPORT QmlEngine : public QObject { @@ -85,6 +87,13 @@ public: static QmlContext *contextForObject(const QObject *); static void setContextForObject(QObject *, QmlContext *); + + static QScriptValue qmlScriptObject(QObject*, QmlEngine*); + + // Below two functions provide a way to dynamically create objects from JS + static QScriptValue createComponent(QScriptContext*, QScriptEngine*); + static QScriptValue createQMLObject(QScriptContext*, QScriptEngine*); + private: // LK: move to the private class QScriptEngine *scriptEngine(); -- cgit v0.12