Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Allow parent to be specified for Qt.createComponent
Add an optional parameter to the Qt.createComponent function which
allows the caller to specify the object that will become the parent
for the created component.

If the parent is not specified by the caller, it becomes the QML
engine; this behavior will change in the near future to allow
these component objects to be collected when no longer referenced.

Task-number: QTBUG-24840
Change-Id: I2742133fe8ab8cbc80d8012a9d9b9fc2e4596fca
Reviewed-by: Martin Jones <martin.jones@nokia.com>
  • Loading branch information
Matthew Vogt authored and Qt by Nokia committed Mar 20, 2012
1 parent c5cd60a commit 610df5c
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 5 deletions.
37 changes: 32 additions & 5 deletions src/qml/qml/v8/qqmlbuiltinfunctions.cpp
Expand Up @@ -1120,7 +1120,7 @@ v8::Handle<v8::Value> 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.
Expand All @@ -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.
Expand All @@ -1150,7 +1153,8 @@ use \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()}.
v8::Handle<v8::Value> 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();
Expand All @@ -1167,19 +1171,42 @@ v8::Handle<v8::Value> 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<v8::Value> &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);
Expand Down
52 changes: 52 additions & 0 deletions 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);
}
}
85 changes: 85 additions & 0 deletions tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
Expand Up @@ -97,6 +97,8 @@ private slots:
void importScope();
void signalParameterTypes();
void objectsCompareAsEqual();
void componentCreation_data();
void componentCreation();
void dynamicCreation_data();
void dynamicCreation();
void dynamicDestruction();
Expand Down Expand Up @@ -1201,6 +1203,89 @@ void tst_qqmlecmascript::aliasPropertyReset()
delete object;
}

void tst_qqmlecmascript::componentCreation_data()
{
QTest::addColumn<QString>("method");
QTest::addColumn<QString>("creationError");
QTest::addColumn<QString>("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<MyTypeObject*>(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<QString>("method");
Expand Down

0 comments on commit 610df5c

Please sign in to comment.