File indexing completed on 2024-05-12 07:47:32
0001 /* 0002 SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org> 0003 SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "sharedqmlengine_p.h" 0009 0010 #include <KLocalizedContext> 0011 #include <QDebug> 0012 #include <QQmlContext> 0013 #include <QQmlEngine> 0014 #include <QQmlIncubator> 0015 #include <QQmlNetworkAccessManagerFactory> 0016 #include <QQuickItem> 0017 #include <QResource> 0018 #include <QTimer> 0019 0020 #include "kcmutils_debug.h" 0021 0022 using namespace Qt::StringLiterals; 0023 0024 class SharedQmlEnginePrivate 0025 { 0026 public: 0027 SharedQmlEnginePrivate(const std::shared_ptr<QQmlEngine> &engine, SharedQmlEngine *parent) 0028 : q(parent) 0029 , component(nullptr) 0030 , delay(false) 0031 , m_engine(engine) 0032 { 0033 executionEndTimer.setInterval(0); 0034 executionEndTimer.setSingleShot(true); 0035 QObject::connect(&executionEndTimer, &QTimer::timeout, q, [this]() { 0036 scheduleExecutionEnd(); 0037 }); 0038 } 0039 0040 ~SharedQmlEnginePrivate() 0041 { 0042 delete incubator.object(); 0043 } 0044 0045 void errorPrint(QQmlComponent *component); 0046 void execute(const QUrl &source); 0047 void scheduleExecutionEnd(); 0048 void minimumWidthChanged(); 0049 void minimumHeightChanged(); 0050 void maximumWidthChanged(); 0051 void maximumHeightChanged(); 0052 void preferredWidthChanged(); 0053 void preferredHeightChanged(); 0054 void checkInitializationCompleted(); 0055 0056 SharedQmlEngine *q; 0057 0058 QUrl source; 0059 0060 QQmlIncubator incubator; 0061 QQmlComponent *component; 0062 QTimer executionEndTimer; 0063 KLocalizedContext *context{nullptr}; 0064 QQmlContext *rootContext; 0065 bool delay; 0066 std::shared_ptr<QQmlEngine> m_engine; 0067 }; 0068 0069 void SharedQmlEnginePrivate::errorPrint(QQmlComponent *component) 0070 { 0071 if (component->isError()) { 0072 qCWarning(KCMUTILS_LOG).noquote() << "Error loading QML file" << component->url().toString(); 0073 const auto errors = component->errors(); 0074 for (const auto &error : errors) { 0075 constexpr const QLatin1String indent(" "); 0076 qCWarning(KCMUTILS_LOG).noquote().nospace() << indent << error; 0077 } 0078 } 0079 } 0080 0081 void SharedQmlEnginePrivate::execute(const QUrl &source) 0082 { 0083 Q_ASSERT(!source.isEmpty()); 0084 delete component; 0085 component = new QQmlComponent(m_engine.get(), q); 0086 QObject::connect(component, &QQmlComponent::statusChanged, q, &SharedQmlEngine::statusChanged, Qt::QueuedConnection); 0087 delete incubator.object(); 0088 0089 m_engine->addImportPath(QStringLiteral("qrc:/")); 0090 component->loadUrl(source); 0091 0092 if (delay) { 0093 executionEndTimer.start(0); 0094 } else { 0095 scheduleExecutionEnd(); 0096 } 0097 } 0098 0099 void SharedQmlEnginePrivate::scheduleExecutionEnd() 0100 { 0101 if (component->isReady() || component->isError()) { 0102 q->completeInitialization(); 0103 } else { 0104 QObject::connect(component, &QQmlComponent::statusChanged, q, [this]() { 0105 q->completeInitialization(); 0106 }); 0107 } 0108 } 0109 0110 SharedQmlEngine::SharedQmlEngine(const std::shared_ptr<QQmlEngine> &engine, QObject *parent) 0111 : QObject(parent) 0112 , d(new SharedQmlEnginePrivate(engine, this)) 0113 { 0114 d->rootContext = new QQmlContext(engine.get()); 0115 d->rootContext->setParent(this); // Delete the context when deleting the shared engine 0116 0117 d->context = new KLocalizedContext(d->rootContext); 0118 d->rootContext->setContextObject(d->context); 0119 } 0120 0121 SharedQmlEngine::~SharedQmlEngine() = default; 0122 0123 void SharedQmlEngine::setTranslationDomain(const QString &translationDomain) 0124 { 0125 d->context->setTranslationDomain(translationDomain); 0126 } 0127 0128 QString SharedQmlEngine::translationDomain() const 0129 { 0130 return d->context->translationDomain(); 0131 } 0132 0133 void SharedQmlEngine::setSource(const QUrl &source) 0134 { 0135 d->source = source; 0136 d->execute(source); 0137 } 0138 0139 QUrl SharedQmlEngine::source() const 0140 { 0141 return d->source; 0142 } 0143 0144 void SharedQmlEngine::setInitializationDelayed(const bool delay) 0145 { 0146 d->delay = delay; 0147 } 0148 0149 bool SharedQmlEngine::isInitializationDelayed() const 0150 { 0151 return d->delay; 0152 } 0153 0154 std::shared_ptr<QQmlEngine> SharedQmlEngine::engine() 0155 { 0156 return d->m_engine; 0157 } 0158 0159 QObject *SharedQmlEngine::rootObject() const 0160 { 0161 if (d->incubator.status() == QQmlIncubator::Loading) { 0162 qCWarning(KCMUTILS_LOG) << "Trying to use rootObject before initialization is completed, whilst using setInitializationDelayed. Forcing completion"; 0163 d->incubator.forceCompletion(); 0164 } 0165 return d->incubator.object(); 0166 } 0167 0168 QQmlComponent *SharedQmlEngine::mainComponent() const 0169 { 0170 return d->component; 0171 } 0172 0173 QQmlContext *SharedQmlEngine::rootContext() const 0174 { 0175 return d->rootContext; 0176 } 0177 0178 QQmlComponent::Status SharedQmlEngine::status() const 0179 { 0180 if (!d->m_engine) { 0181 return QQmlComponent::Error; 0182 } 0183 0184 if (!d->component) { 0185 return QQmlComponent::Null; 0186 } 0187 0188 return d->component->status(); 0189 } 0190 0191 void SharedQmlEnginePrivate::checkInitializationCompleted() 0192 { 0193 if (!incubator.isReady() && incubator.status() != QQmlIncubator::Error) { 0194 QTimer::singleShot(0, q, [this]() { 0195 checkInitializationCompleted(); 0196 }); 0197 return; 0198 } 0199 0200 if (!incubator.object()) { 0201 errorPrint(component); 0202 } 0203 0204 Q_EMIT q->finished(); 0205 } 0206 0207 void SharedQmlEngine::completeInitialization(const QVariantMap &initialProperties) 0208 { 0209 d->executionEndTimer.stop(); 0210 if (d->incubator.object()) { 0211 return; 0212 } 0213 0214 if (!d->component) { 0215 qCWarning(KCMUTILS_LOG) << "No component for" << source(); 0216 return; 0217 } 0218 0219 if (d->component->status() != QQmlComponent::Ready || d->component->isError()) { 0220 d->errorPrint(d->component); 0221 return; 0222 } 0223 0224 d->incubator.setInitialProperties(initialProperties); 0225 d->component->create(d->incubator, d->rootContext); 0226 0227 if (d->delay) { 0228 d->checkInitializationCompleted(); 0229 } else { 0230 d->incubator.forceCompletion(); 0231 0232 if (!d->incubator.object()) { 0233 d->errorPrint(d->component); 0234 } 0235 Q_EMIT finished(); 0236 } 0237 } 0238 0239 QObject *SharedQmlEngine::createObjectFromSource(const QUrl &source, QQmlContext *context, const QVariantMap &initialProperties) 0240 { 0241 QQmlComponent *component = new QQmlComponent(d->m_engine.get(), this); 0242 component->loadUrl(source); 0243 0244 return createObjectFromComponent(component, context, initialProperties); 0245 } 0246 0247 QObject *SharedQmlEngine::createObjectFromComponent(QQmlComponent *component, QQmlContext *context, const QVariantMap &initialProperties) 0248 { 0249 QObject *object = component->createWithInitialProperties(initialProperties, context ? context : d->rootContext); 0250 0251 if (!component->isError() && object) { 0252 // memory management 0253 component->setParent(object); 0254 // reparent to root object if wasn't specified otherwise by initialProperties 0255 if (!initialProperties.contains(QLatin1String("parent"))) { 0256 const auto root = rootObject(); 0257 if (root && root->isQuickItemType()) { 0258 object->setProperty("parent", QVariant::fromValue(root)); 0259 } else { 0260 object->setParent(root); 0261 } 0262 } 0263 0264 return object; 0265 0266 } else { 0267 d->errorPrint(component); 0268 delete object; 0269 return nullptr; 0270 } 0271 } 0272 0273 #include "moc_sharedqmlengine_p.cpp"