diff options
-rw-r--r-- | src/qml/doc/snippets/code/src_qml_qqmlengine.cpp | 7 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent.cpp | 45 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.cpp | 72 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.h | 12 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine_p.h | 20 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/CMakeLists.txt | 14 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/declarativelyregistered.cpp | 8 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/declarativelyregistered.h | 20 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 21 |
9 files changed, 174 insertions, 45 deletions
diff --git a/src/qml/doc/snippets/code/src_qml_qqmlengine.cpp b/src/qml/doc/snippets/code/src_qml_qqmlengine.cpp index 7057718b52..ff4cf634a0 100644 --- a/src/qml/doc/snippets/code/src_qml_qqmlengine.cpp +++ b/src/qml/doc/snippets/code/src_qml_qqmlengine.cpp @@ -42,3 +42,10 @@ void wrapper4(int typeId) { QJSValue instance = engine.singletonInstance<QJSValue>(typeId); //! [4] } + +void wrapper5() { +///! [5] + QQmlEngine engine; + MySingleton *singleton = engine.singletonInstance<MySingleton *>("mymodule", "MySingleton"); +///! [5] +} diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 7b9c96b6d6..e615332402 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1238,51 +1238,6 @@ QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj) return a; } -struct LoadHelper final : QQmlTypeLoader::Blob -{ - LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri) - : QQmlTypeLoader::Blob({}, QQmlDataBlob::QmlFile, loader) - , m_uri(uri.toString()) - - { - auto import = std::make_shared<PendingImport>(); - import->uri = uri.toString(); - QList<QQmlError> errorList; - Blob::addImport(import, &errorList); - } - - struct ResolveTypeResult - { - enum Status { NoSuchModule, ModuleFound } status; - QQmlType type; - }; - - ResolveTypeResult resolveType(QAnyStringView typeName) - { - QQmlType type; - QQmlTypeModule *module = QQmlMetaType::typeModule(m_uri, QTypeRevision{}); - if (!module) - return {ResolveTypeResult::NoSuchModule, type}; - type = module->type(typeName.toString(), {}); - if (type.isValid()) - return {ResolveTypeResult::ModuleFound, type}; - QTypeRevision versionReturn; - QList<QQmlError> errors; - QQmlImportNamespace *ns_return = nullptr; - m_importCache->resolveType(typeName.toString(), &type, &versionReturn, - &ns_return, - &errors); - return {ResolveTypeResult::ModuleFound, type}; - } - -protected: - void dataReceived(const SourceCodeData &) override { Q_UNREACHABLE(); } - void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *) override { Q_UNREACHABLE(); } - -private: - QString m_uri; -}; - /*! Load the QQmlComponent for \a typeName in the module \a uri. If the type is implemented via a QML file, \a mode is used to diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index bd33650d9a..ae56c7ea2a 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -999,6 +999,48 @@ QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId) return d->singletonInstance<QJSValue>(type); } + +/*! + \fn template<typename T> T QQmlEngine::singletonInstance(QAnyStringView uri, QAnyStringView typeName) + + \overload + Returns the instance of a singleton type named \a typeName from the module specified by \a uri. + + This method can be used as an alternative to calling qmlTypeId followed by the id based overload of + singletonInstance. This is convenient when one only needs to do a one time setup of a + singleton; if repeated access to the singleton is required, caching its typeId will allow + faster subsequent access via the + \l {QQmlEngine::singletonInstance(int qmlTypeId)}{type-id based overload}. + + The template argument \e T may be either QJSValue or a pointer to a QObject-derived + type and depends on how the singleton was registered. If no instance of \e T has been + created yet, it is created now. If \a typeName does not represent a valid singleton + type, either a default constructed QJSValue or a \c nullptr is returned. + + \snippet code/src_qml_qqmlengine.cpp 5 + + \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId() + \since 6.5 +*/ +template<> +QJSValue QQmlEngine::singletonInstance<QJSValue>(QAnyStringView uri, QAnyStringView typeName) +{ + Q_D(QQmlEngine); + + auto loadHelper = QQml::makeRefPointer<LoadHelper>(&d->typeLoader, uri); + + auto [moduleStatus, type] = loadHelper->resolveType(typeName); + + if (moduleStatus == LoadHelper::ResolveTypeResult::NoSuchModule) + return {}; + if (!type.isValid()) + return {}; + if (!type.isSingleton()) + return {}; + + return d->singletonInstance<QJSValue>(type); +} + /*! Refreshes all binding expressions that use strings marked for translation. @@ -1976,8 +2018,38 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */) void hasJsOwnershipIndicator(QQmlGuardImpl *) {}; +LoadHelper::LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri) + : QQmlTypeLoader::Blob({}, QQmlDataBlob::QmlFile, loader) + , m_uri(uri.toString()) + +{ + auto import = std::make_shared<PendingImport>(); + import->uri = uri.toString(); + QList<QQmlError> errorList; + Blob::addImport(import, &errorList); +} + +LoadHelper::ResolveTypeResult LoadHelper::resolveType(QAnyStringView typeName) +{ + QQmlType type; + QQmlTypeModule *module = QQmlMetaType::typeModule(m_uri, QTypeRevision{}); + if (!module) + return {ResolveTypeResult::NoSuchModule, type}; + type = module->type(typeName.toString(), {}); + if (type.isValid()) + return {ResolveTypeResult::ModuleFound, type}; + QTypeRevision versionReturn; + QList<QQmlError> errors; + QQmlImportNamespace *ns_return = nullptr; + m_importCache->resolveType(typeName.toString(), &type, &versionReturn, + &ns_return, + &errors); + return {ResolveTypeResult::ModuleFound, type}; +} + QT_END_NAMESPACE #include "moc_qqmlengine_p.cpp" #include "moc_qqmlengine.cpp" + diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 7f225f57f5..a8b19e41c6 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -126,6 +126,9 @@ public: template<typename T> T singletonInstance(int qmlTypeId); + template<typename T> + T singletonInstance(QAnyStringView moduleName, QAnyStringView typeName); + void captureProperty(QObject *object, const QMetaProperty &property) const; public Q_SLOTS: @@ -160,6 +163,15 @@ T QQmlEngine::singletonInstance(int qmlTypeId) { return qobject_cast<T>(singletonInstance<QJSValue>(qmlTypeId).toQObject()); } +template<> +Q_QML_EXPORT QJSValue QQmlEngine::singletonInstance<QJSValue>(QAnyStringView uri, QAnyStringView typeName); + +template<typename T> +T QQmlEngine::singletonInstance(QAnyStringView uri, QAnyStringView typeName) +{ + return qobject_cast<T>(singletonInstance<QJSValue>(uri, typeName).toQObject()); +} + QT_END_NAMESPACE #endif // QQMLENGINE_H diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 6d27d7759c..366d34c366 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -378,6 +378,26 @@ T QQmlEnginePrivate::singletonInstance(const QQmlType &type) { return qobject_cast<T>(singletonInstance<QJSValue>(type).toQObject()); } +struct LoadHelper final : QQmlTypeLoader::Blob +{ + LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri); + + struct ResolveTypeResult + { + enum Status { NoSuchModule, ModuleFound } status; + QQmlType type; + }; + + ResolveTypeResult resolveType(QAnyStringView typeName); + +protected: + void dataReceived(const SourceCodeData &) final { Q_UNREACHABLE(); } + void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *) final { Q_UNREACHABLE(); } + +private: + QString m_uri; +}; + QT_END_NAMESPACE diff --git a/tests/auto/qml/qqmlengine/CMakeLists.txt b/tests/auto/qml/qqmlengine/CMakeLists.txt index a2eb1abc29..a799a20dcd 100644 --- a/tests/auto/qml/qqmlengine/CMakeLists.txt +++ b/tests/auto/qml/qqmlengine/CMakeLists.txt @@ -26,6 +26,20 @@ qt_internal_add_test(tst_qqmlengine TESTDATA ${test_data} ) + +qt_add_library(tst_qqmlengine_qml STATIC) +qt_autogen_tools_initial_setup(tst_qqmlengine_qml) +qt_add_qml_module(tst_qqmlengine_qml + URI OnlyDeclarative + VERSION 1.0 + SOURCES + "declarativelyregistered.h" + "declarativelyregistered.cpp" +) + +qt_autogen_tools_initial_setup(tst_qqmlengine_qmlplugin) +target_link_libraries(tst_qqmlengine PRIVATE tst_qqmlengine_qmlplugin) + # Resources: set(qmake_immediate_resource_files "data/qrcurls.js" diff --git a/tests/auto/qml/qqmlengine/declarativelyregistered.cpp b/tests/auto/qml/qqmlengine/declarativelyregistered.cpp new file mode 100644 index 0000000000..9893fa0311 --- /dev/null +++ b/tests/auto/qml/qqmlengine/declarativelyregistered.cpp @@ -0,0 +1,8 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "declarativelyregistered.h" + +PurelyDeclarativeSingleton::PurelyDeclarativeSingleton(QObject *parent) : QObject(parent) {}; + +#include "moc_declarativelyregistered.cpp" diff --git a/tests/auto/qml/qqmlengine/declarativelyregistered.h b/tests/auto/qml/qqmlengine/declarativelyregistered.h new file mode 100644 index 0000000000..98f7250d76 --- /dev/null +++ b/tests/auto/qml/qqmlengine/declarativelyregistered.h @@ -0,0 +1,20 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef DECLARATIVELYREGISTERED_H +#define DECLARATIVELYREGISTERED_H + +#include <QtCore/qobject.h> +#include <QtQml/qqmlregistration.h> + +class PurelyDeclarativeSingleton : public QObject +{ + Q_OBJECT + QML_SINGLETON + QML_ELEMENT +public: + PurelyDeclarativeSingleton(QObject *parent = nullptr); +}; + + +#endif diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 991af073fb..895513d822 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -16,12 +16,17 @@ #include <QQmlExpression> #include <QQmlIncubationController> #include <QTemporaryDir> + #include <QQmlEngineExtensionPlugin> #include <private/qqmlengine_p.h> #include <private/qqmltypedata_p.h> #include <private/qqmlcomponentattached_p.h> #include <QQmlAbstractUrlInterceptor> #include <QtQuickTestUtils/private/qmlutils_p.h> +#include "declarativelyregistered.h" + +Q_IMPORT_QML_PLUGIN(OnlyDeclarativePlugin) + class tst_qqmlengine : public QQmlDataTest { Q_OBJECT @@ -1148,6 +1153,8 @@ void tst_qqmlengine::singletonInstance() QObject *instance = value.toQObject(); QVERIFY(instance); QCOMPARE(instance->metaObject()->className(), "CppSingleton"); + + QCOMPARE(engine.singletonInstance<CppSingleton *>("Test", "CppSingleton"), instance); } { @@ -1219,6 +1226,20 @@ void tst_qqmlengine::singletonInstance() QVERIFY(!noSinglePtr); } + // test the case where we haven't loaded the module yet + { + auto singleton = engine.singletonInstance<PurelyDeclarativeSingleton *>("OnlyDeclarative", "PurelyDeclarativeSingleton"); + QVERIFY(singleton); + // requesting the singleton twice yields the same result + auto again = engine.singletonInstance<PurelyDeclarativeSingleton *>("OnlyDeclarative", "PurelyDeclarativeSingleton"); + QCOMPARE(again, singleton); + + // different engines -> different singletons + QQmlEngine engine2; + auto differentEngine = engine2.singletonInstance<PurelyDeclarativeSingleton *>("OnlyDeclarative", "PurelyDeclarativeSingleton"); + QCOMPARE_NE(differentEngine, singleton); + } + { // Invalid types QJSValue value; |