diff options
Diffstat (limited to 'src/qml/qml/qqmlapplicationengine.cpp')
-rw-r--r-- | src/qml/qml/qqmlapplicationengine.cpp | 191 |
1 files changed, 137 insertions, 54 deletions
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index e0ab25892f..82cc335c8e 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Research In Motion. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtQml/qqmlfile.h> #include <QtCore/QCoreApplication> @@ -43,7 +7,9 @@ #include <QQmlComponent> #include "qqmlapplicationengine.h" #include "qqmlapplicationengine_p.h" -#include "qqmlfileselector.h" +#include <QtQml/private/qqmlcomponent_p.h> +#include <QtQml/private/qqmldirdata_p.h> +#include <QtQml/private/qqmlfileselector_p.h> QT_BEGIN_NAMESPACE @@ -57,10 +23,18 @@ QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate() { } +void QQmlApplicationEnginePrivate::ensureInitialized() +{ + if (!isInitialized) { + init(); + isInitialized = true; + } +} + void QQmlApplicationEnginePrivate::cleanUp() { Q_Q(QQmlApplicationEngine); - for (auto obj : qAsConst(objects)) + for (auto obj : std::as_const(objects)) obj->disconnect(q); qDeleteAll(objects); @@ -73,7 +47,9 @@ void QQmlApplicationEnginePrivate::init() &QCoreApplication::quit, Qt::QueuedConnection); q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit, Qt::QueuedConnection); - q->connect(q, SIGNAL(uiLanguageChanged()), q_func(), SLOT(_q_loadTranslations())); + QObject::connect(q, &QJSEngine::uiLanguageChanged, q, [this](){ + _q_loadTranslations(); + }); #if QT_CONFIG(translation) QTranslator* qtTranslator = new QTranslator(q); if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::path(QLibraryInfo::TranslationsPath), QLatin1String(".qm"))) @@ -89,18 +65,17 @@ void QQmlApplicationEnginePrivate::init() void QQmlApplicationEnginePrivate::_q_loadTranslations() { #if QT_CONFIG(translation) + Q_Q(QQmlApplicationEngine); if (translationsDirectory.isEmpty()) return; - Q_Q(QQmlApplicationEngine); - - QScopedPointer<QTranslator> translator(new QTranslator); + auto translator = std::make_unique<QTranslator>(); if (!uiLanguage.value().isEmpty()) { QLocale locale(uiLanguage); if (translator->load(locale, QLatin1String("qml"), QLatin1String("_"), translationsDirectory, QLatin1String(".qm"))) { if (activeTranslator) - QCoreApplication::removeTranslator(activeTranslator.data()); - QCoreApplication::installTranslator(translator.data()); + QCoreApplication::removeTranslator(activeTranslator.get()); + QCoreApplication::installTranslator(translator.get()); activeTranslator.swap(translator); } } else { @@ -114,10 +89,7 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray & { Q_Q(QQmlApplicationEngine); - if (!isInitialized) { - init(); - isInitialized = true; - } + ensureInitialized(); if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc")) { QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url)); @@ -134,11 +106,40 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray & else c->loadUrl(url); - if (!c->isLoading()) { - finishLoad(c); - return; + ensureLoadingFinishes(c); +} + +void QQmlApplicationEnginePrivate::startLoad(QAnyStringView uri, QAnyStringView typeName) +{ + Q_Q(QQmlApplicationEngine); + + QQmlComponent *c = new QQmlComponent(q, q); + + ensureInitialized(); + + auto *componentPriv = QQmlComponentPrivate::get(c); + const auto [status, type] = componentPriv->prepareLoadFromModule(uri, typeName); + + if (type.sourceUrl().isValid()) { + const auto qmlDirData = typeLoader.getQmldir(type.sourceUrl()); + const QUrl url = qmlDirData->finalUrl(); + if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc")) { + QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url)); + translationsDirectory = fi.path() + QLatin1String("/i18n"); + } else { + translationsDirectory.clear(); + } } - QObject::connect(c, &QQmlComponent::statusChanged, q, [this, c] { this->finishLoad(c); }); + + /* Translations must be loaded before the QML file. They require translationDirectory to + * already be resolved. But, in order to resolve the translationDirectory, the type of the + * module to load needs to be known. Therefore, loadFromModule is split into resolution and + * loading because the translation directory needs to be set in between. + */ + _q_loadTranslations(); + componentPriv->completeLoadFromModule(uri, typeName, type, status); + + ensureLoadingFinishes(c); } void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) @@ -149,6 +150,7 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) qWarning() << "QQmlApplicationEngine failed to load component"; warning(c->errors()); q->objectCreated(nullptr, c->url()); + q->objectCreationFailed(c->url()); break; case QQmlComponent::Ready: { auto newObj = initialProperties.empty() ? c->create() : c->createWithInitialProperties(initialProperties); @@ -157,6 +159,7 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) qWarning() << "QQmlApplicationEngine failed to create component"; warning(c->errors()); q->objectCreated(nullptr, c->url()); + q->objectCreationFailed(c->url()); break; } @@ -173,6 +176,16 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) c->deleteLater(); } +void QQmlApplicationEnginePrivate::ensureLoadingFinishes(QQmlComponent *c) +{ + Q_Q(QQmlApplicationEngine); + if (!c->isLoading()) { + finishLoad(c); + return; + } + QObject::connect(c, &QQmlComponent::statusChanged, q, [this, c] { this->finishLoad(c); }); +} + /*! \class QQmlApplicationEngine \since 5.1 @@ -233,6 +246,32 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) */ /*! + \fn QQmlApplicationEngine::objectCreationFailed(const QUrl &url) + \since 6.4 + + This signal is emitted when loading finishes because an error occurred. + + The \a url to the component that failed to load is provided as an argument. + + \code + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine; + + // exit on error + QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, + &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection); + engine.load(QUrl()); + return app.exec(); + \endcode + + \note If the path to the component was provided as a QString containing a + relative path, the \a url will contain a fully resolved path to the file. + + See also \l {QQmlApplicationEngine::objectCreated}, which will be emitted in + addition to this signal (even though creation failed). +*/ + +/*! Create a new QQmlApplicationEngine with the given \a parent. You will have to call load() later in order to load a QML file. */ @@ -253,6 +292,20 @@ QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent) } /*! + Create a new QQmlApplicationEngine and loads the QML type specified by + \a uri and \a typeName + This is provided as a convenience, and is the same as using the empty constructor and calling + loadFromModule afterwards. + + \since 6.5 +*/ +QQmlApplicationEngine::QQmlApplicationEngine(QAnyStringView uri, QAnyStringView typeName, QObject *parent) + : QQmlApplicationEngine(parent) +{ + loadFromModule(uri, typeName); +} + +/*! Create a new QQmlApplicationEngine and loads the QML file at the given \a filePath, which must be a local file path. If a relative path is given then it will be interpreted as relative to the working directory of the @@ -306,6 +359,36 @@ void QQmlApplicationEngine::load(const QString &filePath) } /*! + Loads the QML type \a typeName from the module specified by \a uri. + If the type originates from a QML file located at a remote url, + the type will be loaded asynchronously. + Listen to the \l {QQmlApplicationEngine::objectCreated()}{objectCreated} + signal to determine when the object tree is ready. + + If an error occurs, the \l {QQmlApplicationEngine::objectCreated()}{objectCreated} + signal is emitted with a null pointer as parameter and error messages are printed + with qWarning. + + \code + QQmlApplicationEngine engine; + engine.loadFromModule("QtQuick", "Rectangle"); + \endcode + + \note The module identified by \a uri is searched in the + \l {QML Import Path}{import path}, in the same way as if + you were doing \c{import uri} inside a QML file. If the + module cannot be located there, this function will fail. + + \since 6.5 + \sa QQmlComponent::loadFromModule + */ +void QQmlApplicationEngine::loadFromModule(QAnyStringView uri, QAnyStringView typeName) +{ + Q_D(QQmlApplicationEngine); + d->startLoad(uri, typeName); +} + +/*! Sets the \a initialProperties with which the QML component gets initialized after it gets loaded. |