summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2009-10-14 01:32:03 (GMT)
committerAaron Kennedy <aaron.kennedy@nokia.com>2009-10-14 01:32:03 (GMT)
commit0a10af2463d73106c2f1268553aa6e60890f6180 (patch)
tree2ad916b6a98f16a4c3692a9e227da782dd3c42ac
parentc8198d40af104b5555a975b3156e9d5ba1981e25 (diff)
downloadQt-0a10af2463d73106c2f1268553aa6e60890f6180.zip
Qt-0a10af2463d73106c2f1268553aa6e60890f6180.tar.gz
Qt-0a10af2463d73106c2f1268553aa6e60890f6180.tar.bz2
Add Component::onCompleted attached property
-rw-r--r--doc/src/declarative/ecmascriptblocks.qdoc35
-rw-r--r--src/declarative/qml/qmlcomponent.cpp65
-rw-r--r--src/declarative/qml/qmlcomponent.h3
-rw-r--r--src/declarative/qml/qmlcomponent_p.h21
-rw-r--r--src/declarative/qml/qmlengine.cpp4
-rw-r--r--src/declarative/qml/qmlengine_p.h2
-rw-r--r--tests/auto/declarative/qmllanguage/data/OnCompletedType.qml8
-rw-r--r--tests/auto/declarative/qmllanguage/data/onCompleted.qml17
-rw-r--r--tests/auto/declarative/qmllanguage/tst_qmllanguage.cpp13
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)
{