diff options
author | Richard Weickelt <richard@weickelt.de> | 2018-05-19 15:30:35 +0200 |
---|---|---|
committer | Richard Weickelt <richard@weickelt.de> | 2018-06-08 20:24:49 +0000 |
commit | 6e16cd5bd8843c6dae219c5fe1493969a51198d3 (patch) | |
tree | 742ad3b945f81da34e275974eff974cc12153617 | |
parent | af46cc19937dd8d33538e62ad5ebfed8e958a8f5 (diff) |
Ensure identical behavior for singleton types defined in C++ and QML
Singleton types defined in C++ have no QML context. Therefore, both
qmlContext(obj) and qmlEngine(obj) return zero. Although documented,
this behavior is surprising and inconsistent with singleton types defined
in QML. The current behavior was decided upon in QTBUG-23116.
This patch puts C++ singleton types into a sub-context of the root context
by default, just like it is the case for QML-defined singleton types. This
doesn't cause any harm, but avoids surprises. It also fixes a bug in
QmlTypeWrapper that returned an invalid QVariant for QJSValue singletons.
Task-number: QTBUG-38583
Change-Id: Id1d48ecdc49f0e22714857a1b49b457885889e5e
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r-- | src/qml/doc/src/qmlfunctions.qdoc | 24 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 12 | ||||
-rw-r--r-- | src/qml/qml/qqmltypewrapper.cpp | 13 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/singletonTest18.qml | 9 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/testtypes.cpp | 9 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 26 |
6 files changed, 62 insertions, 31 deletions
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 55ca040af6..6445a003a1 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -538,30 +538,6 @@ } \endqml - Since singleton types do not have an associated QQmlContext object, then within the functions of a QObject-derived - type that is registered as a singleton type implementation the QML context and engine information is not available. - The QQmlEngine::contextForObject() function returns NULL when supplied with a pointer to an QObject that - implements a singleton type. - - Extending the above example: - - \code - class SingletonTypeExample : public QObject - { - ... - - Q_INVOKABLE void doSomethingElse() - { - // QML Engine/Context information is not accessible here: - Q_ASSERT(QQmlEngine::contextForObject(this) == 0); - Q_ASSERT(qmlContext(this) == 0); - Q_ASSERT(qmlEngine(this) == 0); - } - - ... - } - \endcode - \sa {Choosing the Correct Integration Method Between C++ and QML} */ diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 0429df6185..307f9a9ec6 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -264,7 +264,14 @@ void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv) void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) { if (scriptCallback && scriptApi(e).isUndefined()) { - setScriptApi(e, scriptCallback(e, e)); + QJSValue value = scriptCallback(e, e); + if (value.isQObject()) { + QObject *o = value.toQObject(); + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); + } + setScriptApi(e, value); } else if (qobjectCallback && !qobjectApi(e)) { QObject *o = qobjectCallback(e, e); setQObjectApi(e, o); @@ -273,6 +280,9 @@ void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) } // if this object can use a property cache, create it now QQmlData::ensurePropertyCache(e, o); + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); } else if (!url.isEmpty() && !qobjectApi(e)) { QQmlComponent component(e, url, QQmlComponent::PreferSynchronous); QObject *o = component.beginCreate(e->rootContext()); diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 26f035705c..7270cffb00 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -95,12 +95,17 @@ QObject* QQmlTypeWrapper::singletonObject() const QVariant QQmlTypeWrapper::toVariant() const { - QObject *qobjectSingleton = singletonObject(); - if (qobjectSingleton) + // Only Singleton type wrappers can be converted to a variant. + if (!isSingleton()) + return QVariant(); + + QQmlEngine *e = engine()->qmlEngine(); + QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo(); + siinfo->init(e); + if (QObject *qobjectSingleton = siinfo->qobjectApi(e)) return QVariant::fromValue<QObject*>(qobjectSingleton); - // only QObject Singleton Type can be converted to a variant. - return QVariant(); + return QVariant::fromValue<QJSValue>(siinfo->scriptApi(e)); } diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest18.qml b/tests/auto/qml/qqmllanguage/data/singletonTest18.qml new file mode 100644 index 0000000000..7616c23531 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singletonTest18.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 +import "singleton" +import Test 1.0 + +Item { + property var qmlSingleton: SingletonType + property var jsSingleton: MyQJSValueQObjectSingleton + property var cppSingleton: MyTypeObjectSingleton +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 0eb4f26878..7e247b1906 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -35,6 +35,14 @@ static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngin return new MyTypeObject(); } +static QJSValue myQJSValueQObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + + QJSValue value = scriptEngine->newQObject(new MyTypeObject()); + return value; +} + void registerTypes() { qmlRegisterInterface<MyInterface>("MyInterface"); @@ -101,6 +109,7 @@ void registerTypes() qmlRegisterType<MyCompositeBaseType>("Test", 1, 0, "MyCompositeBaseType"); qmlRegisterSingletonType<MyTypeObjectSingleton>("Test", 1, 0, "MyTypeObjectSingleton", myTypeObjectSingleton); + qmlRegisterSingletonType("Test", 1, 0, "MyQJSValueQObjectSingleton", myQJSValueQObjectSingleton); qmlRegisterType<MyArrayBufferTestClass>("Test", 1, 0, "MyArrayBufferTestClass"); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 400c28928a..39f973f3fd 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -246,6 +246,8 @@ private slots: void compositeSingletonRegistered(); void compositeSingletonCircular(); + void singletonsHaveContextAndEngine(); + void customParserBindingScopes(); void customParserEvaluateEnum(); void customParserProperties(); @@ -3923,11 +3925,13 @@ void tst_qqmllanguage::getSingletonInstance(QObject* o, const char* propertyName return; QVariant variant = o->property(propertyName); - QVERIFY(variant.userType() == qMetaTypeId<QObject *>()); + QVERIFY(variant.isValid()); QObject *singleton = nullptr; - if (variant.canConvert<QObject*>()) + if (variant.userType() == qMetaTypeId<QObject *>()) singleton = variant.value<QObject*>(); + else if (variant.userType() == qMetaTypeId<QJSValue>()) + singleton = variant.value<QJSValue>().toQObject(); QVERIFY(singleton != nullptr); *result = singleton; @@ -4232,6 +4236,24 @@ void tst_qqmllanguage::compositeSingletonCircular() QCOMPARE(o->property("value").toInt(), 2); } +void tst_qqmllanguage::singletonsHaveContextAndEngine() +{ + QObject *qmlSingleton = nullptr; + getSingletonInstance(engine, "singletonTest18.qml", "qmlSingleton", &qmlSingleton); + QVERIFY(qmlContext(qmlSingleton)); + QCOMPARE(qmlEngine(qmlSingleton), &engine); + + QObject *jsSingleton = nullptr; + getSingletonInstance(engine, "singletonTest18.qml", "jsSingleton", &jsSingleton); + QVERIFY(qmlContext(jsSingleton)); + QCOMPARE(qmlEngine(jsSingleton), &engine); + + QObject *cppSingleton = nullptr; + getSingletonInstance(engine, "singletonTest18.qml", "cppSingleton", &cppSingleton); + QVERIFY(qmlContext(cppSingleton)); + QCOMPARE(qmlEngine(cppSingleton), &engine); +} + void tst_qqmllanguage::customParserBindingScopes() { QQmlComponent component(&engine, testFile("customParserBindingScopes.qml")); |