diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-10-14 01:32:03 (GMT) |
---|---|---|
committer | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-10-14 01:32:03 (GMT) |
commit | 0a10af2463d73106c2f1268553aa6e60890f6180 (patch) | |
tree | 2ad916b6a98f16a4c3692a9e227da782dd3c42ac | |
parent | c8198d40af104b5555a975b3156e9d5ba1981e25 (diff) | |
download | Qt-0a10af2463d73106c2f1268553aa6e60890f6180.zip Qt-0a10af2463d73106c2f1268553aa6e60890f6180.tar.gz Qt-0a10af2463d73106c2f1268553aa6e60890f6180.tar.bz2 |
Add Component::onCompleted attached property
-rw-r--r-- | doc/src/declarative/ecmascriptblocks.qdoc | 35 | ||||
-rw-r--r-- | src/declarative/qml/qmlcomponent.cpp | 65 | ||||
-rw-r--r-- | src/declarative/qml/qmlcomponent.h | 3 | ||||
-rw-r--r-- | src/declarative/qml/qmlcomponent_p.h | 21 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine.cpp | 4 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine_p.h | 2 | ||||
-rw-r--r-- | tests/auto/declarative/qmllanguage/data/OnCompletedType.qml | 8 | ||||
-rw-r--r-- | tests/auto/declarative/qmllanguage/data/onCompleted.qml | 17 | ||||
-rw-r--r-- | tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp | 13 |
9 files changed, 163 insertions, 5 deletions
diff --git a/doc/src/declarative/ecmascriptblocks.qdoc b/doc/src/declarative/ecmascriptblocks.qdoc index f683af8..815c68c 100644 --- a/doc/src/declarative/ecmascriptblocks.qdoc +++ b/doc/src/declarative/ecmascriptblocks.qdoc @@ -115,6 +115,37 @@ accessible, an error will occur. If the source is on a network resource, the enclosing QML document will remain in the \l {QmlComponent::status()}{waiting state} until the script has been retrieved. +\section1 Running Script at Startup + +It is occasionally necessary to run a block of ECMAScript code at application (or +component instance) "startup". While it is tempting to just include the startup +script as \e {global code} in an external script file, this can have sever limitations +as the QML environment may not have been fully established. For example, some objects +might not have been created or some \l {Property Binding}s may not have been run. +\l {QML Script Restrictions} covers the exact limitations of global script code. + +The QML \l Component element provides an \e attached \c onCompleted property that +can be used to trigger the execution of script code at startup after the +QML environment has been completely established. + +The following QML code shows how to use the \c Component::onCompleted property. + +\code +Rectangle { + Script { + function startupFunction() { + // ... startup code + } + } + + Component.onCompleted: startupFunction(); +} +\endcode + +Any element in a QML file - including nested elements and nested QML component +instances - can use this attached property. If there is more than one script to +execute at startup, they are run sequentially in an undefined order. + \section1 QML Script Restrictions QML \l Script blocks contain standard ECMAScript code. QML introduces the following @@ -174,8 +205,8 @@ var initialPosition = { rootObject.x, rootObject.y } \endcode This restriction exists as the QML environment is not yet fully established. -To run code after the environment setup has completed - at "startup" - use -the \l Component \c onCompleted attached property. +To run code after the environment setup has completed, refer to +\l {Running Script at Startup}. \endlist diff --git a/src/declarative/qml/qmlcomponent.cpp b/src/declarative/qml/qmlcomponent.cpp index 9a761b2..dc71989 100644 --- a/src/declarative/qml/qmlcomponent.cpp +++ b/src/declarative/qml/qmlcomponent.cpp @@ -95,6 +95,27 @@ Item { Loader { sourceComponent: RedSquare; x: 20 } } \endqml + + \section1 Attached Properties + + \e onCompleted + + Emitted after component "startup" has completed. This can be used to + execute script code at startup, once the full QML environment has been + established. + + The \c {Component::onCompleted} attached property can be applied to + any element. The order of running the \c onCompleted scripts is + undefined. + + \qml + Rectangle { + Component.onCompleted: print("Completed Running!") + Rectangle { + Component.onCompleted: print("Nested Completed Running!") + } + } + \endqml */ QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,Component,QmlComponent); @@ -532,6 +553,11 @@ QmlComponentPrivate::beginCreate(QmlContext *context, const QBitField &bindings) bindValues = ep->bindValues; parserStatus = ep->parserStatus; + componentAttacheds = ep->componentAttacheds; + if (componentAttacheds) + componentAttacheds->prev = &componentAttacheds; + + ep->componentAttacheds = 0; ep->bindValues.clear(); ep->parserStatus.clear(); completePending = true; @@ -593,10 +619,49 @@ void QmlComponentPrivate::completeCreate() QmlEnginePrivate::clear(ps); } + while (componentAttacheds) { + QmlComponentAttached *a = componentAttacheds; + if (a->next) a->next->prev = &componentAttacheds; + componentAttacheds = a->next; + a->prev = 0; a->next = 0; + emit a->completed(); + } + bindValues.clear(); parserStatus.clear(); completePending = false; } } +QmlComponentAttached::QmlComponentAttached(QObject *parent) +: QObject(parent), prev(0), next(0) +{ +} + +QmlComponentAttached::~QmlComponentAttached() +{ + if (prev) *prev = next; + if (next) next->prev = prev; + prev = 0; + next = 0; +} + +QmlComponentAttached *QmlComponent::qmlAttachedProperties(QObject *obj) +{ + QmlComponentAttached *a = new QmlComponentAttached(obj); + + QmlEngine *engine = qmlEngine(obj); + if (!engine || !QmlEnginePrivate::get(engine)->rootComponent) + return a; + + QmlEnginePrivate *p = QmlEnginePrivate::get(engine); + + a->next = p->componentAttacheds; + a->prev = &p->componentAttacheds; + if (a->next) a->next->prev = &a->next; + p->componentAttacheds = a; + + return a; +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcomponent.h b/src/declarative/qml/qmlcomponent.h index c6924e3..dcf9347 100644 --- a/src/declarative/qml/qmlcomponent.h +++ b/src/declarative/qml/qmlcomponent.h @@ -59,6 +59,7 @@ class QmlCompiledData; class QByteArray; class QmlComponentPrivate; class QmlEngine; +class QmlComponentAttached; class Q_DECLARATIVE_EXPORT QmlComponent : public QObject { Q_OBJECT @@ -95,6 +96,8 @@ public: void loadUrl(const QUrl &url); void setData(const QByteArray &, const QUrl &baseUrl); + static QmlComponentAttached *qmlAttachedProperties(QObject *); + Q_SIGNALS: void statusChanged(QmlComponent::Status); void progressChanged(qreal); diff --git a/src/declarative/qml/qmlcomponent_p.h b/src/declarative/qml/qmlcomponent_p.h index 2d0c77f..7eedfbd 100644 --- a/src/declarative/qml/qmlcomponent_p.h +++ b/src/declarative/qml/qmlcomponent_p.h @@ -70,12 +70,13 @@ class QmlComponent; class QmlEngine; class QmlCompiledData; +class QmlComponentAttached; class QmlComponentPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QmlComponent) public: - QmlComponentPrivate() : typeData(0), progress(0.), start(-1), count(-1), cc(0), completePending(false), engine(0) {} + QmlComponentPrivate() : typeData(0), progress(0.), start(-1), count(-1), cc(0), completePending(false), componentAttacheds(0), engine(0) {} QObject *create(QmlContext *context, const QBitField &); @@ -98,6 +99,7 @@ public: QList<QmlEnginePrivate::SimpleList<QmlAbstractBinding> > bindValues; QList<QmlEnginePrivate::SimpleList<QmlParserStatus> > parserStatus; + QmlComponentAttached *componentAttacheds; bool completePending; @@ -110,6 +112,23 @@ public: } }; +class QmlComponentAttached : public QObject +{ + Q_OBJECT +public: + QmlComponentAttached(QObject *parent = 0); + ~QmlComponentAttached(); + + QmlComponentAttached **prev; + QmlComponentAttached *next; + +signals: + void completed(); + +private: + friend class QmlComponentPrivate; +}; + QT_END_NAMESPACE #endif // QMLCOMPONENT_P_H diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index ef0f975..7e95428 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -125,8 +125,8 @@ static QString userLocalDataPath(const QString& app) QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) : rootContext(0), currentExpression(0), isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0), globalClass(0), - nodeListClass(0), namedNodeMapClass(0), sqlQueryClass(0), scriptEngine(this), rootComponent(0), - networkAccessManager(0), typeManager(e), uniqueId(1) + nodeListClass(0), namedNodeMapClass(0), sqlQueryClass(0), scriptEngine(this), + componentAttacheds(0), rootComponent(0), networkAccessManager(0), typeManager(e), uniqueId(1) { QScriptValue qtObject = scriptEngine.newQMetaObject(StaticQtMetaObject::get()); diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 7978023..4c90a80 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -95,6 +95,7 @@ class QmlAbstractBinding; class QScriptDeclarativeClass; class QmlTypeNameScriptClass; class QmlTypeNameCache; +class QmlComponentAttached; class QmlEnginePrivate : public QObjectPrivate { @@ -174,6 +175,7 @@ public: QList<SimpleList<QmlAbstractBinding> > bindValues; QList<SimpleList<QmlParserStatus> > parserStatus; + QmlComponentAttached *componentAttacheds; QmlComponent *rootComponent; mutable QNetworkAccessManager *networkAccessManager; diff --git a/tests/auto/declarative/qmllanguage/data/OnCompletedType.qml b/tests/auto/declarative/qmllanguage/data/OnCompletedType.qml new file mode 100644 index 0000000..cdba495 --- /dev/null +++ b/tests/auto/declarative/qmllanguage/data/OnCompletedType.qml @@ -0,0 +1,8 @@ +import Test 1.0 +import Qt 4.6 + +MyQmlObject { + property int a: Math.max(10, 9) + property int b: 11 + Component.onCompleted: print("Completed " + a + " " + b); +} diff --git a/tests/auto/declarative/qmllanguage/data/onCompleted.qml b/tests/auto/declarative/qmllanguage/data/onCompleted.qml new file mode 100644 index 0000000..ae47d4b --- /dev/null +++ b/tests/auto/declarative/qmllanguage/data/onCompleted.qml @@ -0,0 +1,17 @@ +import Test 1.0 +import Qt 4.6 + +MyTypeObject { + // We set a and b to ensure that onCompleted is executed after bindings and + // constants have been assigned + property int a: Math.min(6, 7) + Component.onCompleted: print("Completed " + a + " " + nestedObject.b) + + objectProperty: OnCompletedType { + qmlobjectProperty: MyQmlObject { + id: nestedObject + property int b: 10 + Component.onCompleted: print("Completed " + a + " " + nestedObject.b) + } + } +} diff --git a/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp b/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp index cf42792..b99d040 100644 --- a/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp +++ b/tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp @@ -60,6 +60,7 @@ private slots: void componentCompositeType(); void i18n(); void i18n_data(); + void onCompleted(); void importsBuiltin_data(); void importsBuiltin(); @@ -705,6 +706,18 @@ void tst_qmllanguage::i18n() delete object; } +// Check that the Component::onCompleted attached property works +void tst_qmllanguage::onCompleted() +{ + QmlComponent component(&engine, TEST_FILE("onCompleted.qml")); + VERIFY_ERRORS(0); + QTest::ignoreMessage(QtDebugMsg, "Completed 6 10"); + QTest::ignoreMessage(QtDebugMsg, "Completed 6 10"); + QTest::ignoreMessage(QtDebugMsg, "Completed 10 11"); + QObject *object = component.create(); + QVERIFY(object != 0); +} + // Check that first child of qml is of given type. Empty type insists on error. void tst_qmllanguage::testType(const QString& qml, const QString& type) { |