diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index b9f2b627da..57a1cb0dcb 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1120,7 +1120,7 @@ v8::Handle createQmlObject(const v8::Arguments &args) } /*! -\qmlmethod object Qt::createComponent(url, mode) +\qmlmethod object Qt::createComponent(url, mode, parent) Returns a \l Component object created using the QML file at the specified \a url, or \c null if an empty string was given. @@ -1135,6 +1135,9 @@ will be \c Component.Loading while it is loading. The status will change to \c Component.Ready if the component loads successfully, or \c Component.Error if loading fails. +If the optional \a parent parameter is given, it should refer to the object +that will become the parent for the created \l Component object. + Call \l {Component::createObject()}{Component.createObject()} on the returned component to create an object instance of the component. @@ -1150,7 +1153,8 @@ use \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()}. v8::Handle createComponent(const v8::Arguments &args) { const char *invalidArgs = "Qt.createComponent(): Invalid arguments"; - if (args.Length() < 1 || args.Length() > 2) + const char *invalidParent = "Qt.createComponent(): Invalid parent object"; + if (args.Length() < 1 || args.Length() > 3) V8THROW_ERROR(invalidArgs); QV8Engine *v8engine = V8ENGINE(); @@ -1167,19 +1171,42 @@ v8::Handle createComponent(const v8::Arguments &args) return v8::Null(); QQmlComponent::CompilationMode compileMode = QQmlComponent::PreferSynchronous; - if (args.Length() == 2) { + + // Default to engine parent; this will be removed in the near future (QTBUG-24841) + QObject *parentArg = engine; + + unsigned consumedCount = 1; + if (args.Length() > 1) { + const v8::Local &lastArg = args[args.Length()-1]; + + // The second argument could be the mode enum if (args[1]->IsInt32()) { int mode = args[1]->Int32Value(); if (mode != int(QQmlComponent::PreferSynchronous) && mode != int(QQmlComponent::Asynchronous)) V8THROW_ERROR(invalidArgs); compileMode = QQmlComponent::CompilationMode(mode); + consumedCount += 1; } else { - V8THROW_ERROR(invalidArgs); + // The second argument could be the parent only if there are exactly two args + if ((args.Length() != 2) || !(lastArg->IsObject() || lastArg->IsNull())) + V8THROW_ERROR(invalidArgs); + } + + if (consumedCount < args.Length()) { + if (lastArg->IsObject()) { + parentArg = v8engine->toQObject(lastArg); + if (!parentArg) + V8THROW_ERROR(invalidParent); + } else if (lastArg->IsNull()) { + parentArg = 0; + } else { + V8THROW_ERROR(invalidParent); + } } } QUrl url = context->resolvedUrl(QUrl(arg)); - QQmlComponent *c = new QQmlComponent(engine, url, compileMode, engine); + QQmlComponent *c = new QQmlComponent(engine, url, compileMode, parentArg); QQmlComponentPrivate::get(c)->creationContext = effectiveContext; QQmlData::get(c, true)->setImplicitDestructible(); return v8engine->newQObject(c); diff --git a/tests/auto/qml/qqmlecmascript/data/componentCreation.qml b/tests/auto/qml/qqmlecmascript/data/componentCreation.qml new file mode 100644 index 0000000000..d21301ea13 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/componentCreation.qml @@ -0,0 +1,52 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyTypeObject{ + id: obj + objectName: "obj" + + function url() + { + obj.componentProperty = Qt.createComponent('dynamicCreation.helper.qml'); + } + + function urlMode() + { + obj.componentProperty = Qt.createComponent('dynamicCreation.helper.qml', Component.PreferSynchronous); + } + + function urlParent() + { + obj.componentProperty = Qt.createComponent('dynamicCreation.helper.qml', obj); + } + + function urlNullParent() + { + obj.componentProperty = Qt.createComponent('dynamicCreation.helper.qml', null); + } + + function urlModeParent() + { + obj.componentProperty = Qt.createComponent('dynamicCreation.helper.qml', Component.PreferSynchronous, obj); + } + + function urlModeNullParent() + { + obj.componentProperty = Qt.createComponent('dynamicCreation.helper.qml', Component.PreferSynchronous, null); + } + + function invalidSecondArg() + { + obj.componentProperty = Qt.createComponent('dynamicCreation.helper.qml', 'Bad argument'); + } + + function invalidThirdArg() + { + obj.componentProperty = Qt.createComponent('dynamicCreation.helper.qml', Component.PreferSynchronous, 'Bad argument'); + } + + function invalidMode() + { + obj.componentProperty = Qt.createComponent('dynamicCreation.helper.qml', -666); + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 2f27ccee31..650197ea4f 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -97,6 +97,8 @@ private slots: void importScope(); void signalParameterTypes(); void objectsCompareAsEqual(); + void componentCreation_data(); + void componentCreation(); void dynamicCreation_data(); void dynamicCreation(); void dynamicDestruction(); @@ -1201,6 +1203,89 @@ void tst_qqmlecmascript::aliasPropertyReset() delete object; } +void tst_qqmlecmascript::componentCreation_data() +{ + QTest::addColumn("method"); + QTest::addColumn("creationError"); + QTest::addColumn("createdParent"); + + QTest::newRow("url") + << "url" + << "" + << ""; + QTest::newRow("urlMode") + << "urlMode" + << "" + << ""; + QTest::newRow("urlParent") + << "urlParent" + << "" + << "obj"; + QTest::newRow("urlNullParent") + << "urlNullParent" + << "" + << "null"; + QTest::newRow("urlModeParent") + << "urlModeParent" + << "" + << "obj"; + QTest::newRow("urlModeNullParent") + << "urlModeNullParent" + << "" + << "null"; + QTest::newRow("invalidSecondArg") + << "invalidSecondArg" + << ":40: Error: Qt.createComponent(): Invalid arguments" + << ""; + QTest::newRow("invalidThirdArg") + << "invalidThirdArg" + << ":45: Error: Qt.createComponent(): Invalid parent object" + << ""; + QTest::newRow("invalidMode") + << "invalidMode" + << ":50: Error: Qt.createComponent(): Invalid arguments" + << ""; +} + +/* +Test using createComponent to dynamically generate a component. +*/ +void tst_qqmlecmascript::componentCreation() +{ + QFETCH(QString, method); + QFETCH(QString, creationError); + QFETCH(QString, createdParent); + + QUrl testUrl(testFileUrl("componentCreation.qml")); + + if (!creationError.isEmpty()) { + QString warning = testUrl.toString() + creationError; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + } + + QQmlComponent component(&engine, testUrl); + MyTypeObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, method.toUtf8()); + QQmlComponent *created = object->componentProperty(); + + if (creationError.isEmpty()) { + QVERIFY(created); + + QObject *expectedParent; + if (createdParent.isEmpty()) { + // For now, the parent should be the engine; this will change for QTBUG-24841 + expectedParent = &engine; + } else if (createdParent == QLatin1String("obj")) { + expectedParent = object; + } else if (createdParent == QLatin1String("null")) { + expectedParent = 0; + } + QCOMPARE(created->parent(), expectedParent); + } +} + void tst_qqmlecmascript::dynamicCreation_data() { QTest::addColumn("method");