File indexing completed on 2025-03-09 05:02:43
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.h" 0009 #include "appletcontext_p.h" 0010 0011 #include <KLocalizedContext> 0012 #include <QDebug> 0013 #include <QQmlContext> 0014 #include <QQmlEngine> 0015 #include <QQmlNetworkAccessManagerFactory> 0016 #include <QQuickItem> 0017 #include <QTimer> 0018 0019 #include <Plasma/Applet> 0020 0021 #include "debug_p.h" 0022 0023 namespace PlasmaQuick 0024 { 0025 0026 class SharedQmlEnginePrivate 0027 { 0028 public: 0029 SharedQmlEnginePrivate(SharedQmlEngine *parent) 0030 : q(parent) 0031 , component(nullptr) 0032 , delay(false) 0033 , m_engine(engine()) 0034 { 0035 executionEndTimer = new QTimer(q); 0036 executionEndTimer->setInterval(0); 0037 executionEndTimer->setSingleShot(true); 0038 QObject::connect(executionEndTimer, &QTimer::timeout, q, [this]() { 0039 scheduleExecutionEnd(); 0040 }); 0041 } 0042 0043 ~SharedQmlEnginePrivate() = default; 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 0055 SharedQmlEngine *q; 0056 0057 QUrl source; 0058 0059 QPointer<QObject> rootObject; 0060 QQmlComponent *component; 0061 QTimer *executionEndTimer; 0062 KLocalizedContext *context{nullptr}; 0063 QQmlContext *rootContext; 0064 bool delay; 0065 std::shared_ptr<QQmlEngine> m_engine; 0066 0067 private: 0068 static std::shared_ptr<QQmlEngine> engine() 0069 { 0070 if (auto locked = s_engine.lock()) { 0071 return locked; 0072 } 0073 auto createdEngine = std::make_shared<QQmlEngine>(); 0074 s_engine = createdEngine; 0075 return createdEngine; 0076 } 0077 0078 static std::weak_ptr<QQmlEngine> s_engine; 0079 }; 0080 0081 std::weak_ptr<QQmlEngine> SharedQmlEnginePrivate::s_engine = {}; 0082 0083 void SharedQmlEnginePrivate::errorPrint(QQmlComponent *component) 0084 { 0085 QString errorStr = QStringLiteral("Error loading QML file.\n"); 0086 if (component->isError()) { 0087 const QList<QQmlError> errors = component->errors(); 0088 for (const QQmlError &error : errors) { 0089 errorStr += 0090 (error.line() > 0 ? QString(QString::number(error.line()) + QLatin1String(": ")) : QLatin1String("")) + error.description() + QLatin1Char('\n'); 0091 } 0092 } 0093 qWarning(LOG_PLASMAQUICK) << component->url().toString() << '\n' << errorStr; 0094 } 0095 0096 void SharedQmlEnginePrivate::execute(const QUrl &source) 0097 { 0098 if (source.isEmpty()) { 0099 qWarning(LOG_PLASMAQUICK) << "File name empty!"; 0100 return; 0101 } 0102 0103 delete component; 0104 component = new QQmlComponent(m_engine.get(), q); 0105 QObject::connect(component, &QQmlComponent::statusChanged, q, &SharedQmlEngine::statusChanged, Qt::QueuedConnection); 0106 0107 component->loadUrl(source); 0108 rootObject = component->beginCreate(rootContext); 0109 0110 if (delay) { 0111 executionEndTimer->start(0); 0112 } else { 0113 scheduleExecutionEnd(); 0114 } 0115 } 0116 0117 void SharedQmlEnginePrivate::scheduleExecutionEnd() 0118 { 0119 if (component->isReady() || component->isError()) { 0120 q->completeInitialization(); 0121 } else { 0122 QObject::connect(component, &QQmlComponent::statusChanged, q, [this]() { 0123 q->completeInitialization(); 0124 }); 0125 } 0126 } 0127 0128 SharedQmlEngine::SharedQmlEngine(QObject *parent) 0129 : QObject(parent) 0130 , d(new SharedQmlEnginePrivate(this)) 0131 { 0132 d->rootContext = new QQmlContext(engine().get()); 0133 d->rootContext->setParent(this); // Delete the context when deleting the shared engine 0134 0135 d->context = new KLocalizedContext(d->rootContext); 0136 d->rootContext->setContextObject(d->context); 0137 } 0138 0139 SharedQmlEngine::SharedQmlEngine(Plasma::Applet *applet, QObject *parent) 0140 : QObject(parent) 0141 , d(new SharedQmlEnginePrivate(this)) 0142 { 0143 d->rootContext = new AppletContext(engine().get(), applet, this); 0144 0145 d->context = new KLocalizedContext(d->rootContext); 0146 d->rootContext->setContextObject(d->context); 0147 } 0148 0149 SharedQmlEngine::~SharedQmlEngine() 0150 { 0151 delete d->component; 0152 if (QJSEngine::objectOwnership(d->rootObject) == QJSEngine::CppOwnership) { 0153 delete d->rootObject; 0154 } 0155 } 0156 0157 void SharedQmlEngine::setTranslationDomain(const QString &translationDomain) 0158 { 0159 d->context->setTranslationDomain(translationDomain); 0160 } 0161 0162 QString SharedQmlEngine::translationDomain() const 0163 { 0164 return d->context->translationDomain(); 0165 } 0166 0167 void SharedQmlEngine::setSource(const QUrl &source) 0168 { 0169 d->source = source; 0170 d->execute(source); 0171 } 0172 0173 QUrl SharedQmlEngine::source() const 0174 { 0175 return d->source; 0176 } 0177 0178 void SharedQmlEngine::setInitializationDelayed(const bool delay) 0179 { 0180 d->delay = delay; 0181 } 0182 0183 bool SharedQmlEngine::isInitializationDelayed() const 0184 { 0185 return d->delay; 0186 } 0187 0188 std::shared_ptr<QQmlEngine> SharedQmlEngine::engine() 0189 { 0190 return d->m_engine; 0191 } 0192 0193 QObject *SharedQmlEngine::rootObject() const 0194 { 0195 return d->rootObject; 0196 } 0197 0198 QQmlComponent *SharedQmlEngine::mainComponent() const 0199 { 0200 return d->component; 0201 } 0202 0203 QQmlContext *SharedQmlEngine::rootContext() const 0204 { 0205 return d->rootContext; 0206 } 0207 0208 QQmlComponent::Status SharedQmlEngine::status() const 0209 { 0210 if (!d->m_engine) { 0211 return QQmlComponent::Error; 0212 } 0213 0214 if (!d->component) { 0215 return QQmlComponent::Null; 0216 } 0217 0218 return QQmlComponent::Status(d->component->status()); 0219 } 0220 0221 void SharedQmlEngine::completeInitialization(const QVariantHash &initialProperties) 0222 { 0223 d->executionEndTimer->stop(); 0224 0225 if (!d->component) { 0226 qWarning(LOG_PLASMAQUICK) << "No component for" << source(); 0227 return; 0228 } 0229 0230 if (d->component->status() != QQmlComponent::Ready || d->component->isError()) { 0231 d->errorPrint(d->component); 0232 return; 0233 } 0234 0235 for (auto it = initialProperties.constBegin(); it != initialProperties.constEnd(); ++it) { 0236 d->rootObject->setProperty(it.key().toUtf8().data(), it.value()); 0237 } 0238 0239 d->component->completeCreate(); 0240 Q_EMIT finished(); 0241 } 0242 0243 QObject *SharedQmlEngine::createObjectFromSource(const QUrl &source, QQmlContext *context, const QVariantHash &initialProperties) 0244 { 0245 QQmlComponent *component = new QQmlComponent(d->m_engine.get(), this); 0246 component->loadUrl(source); 0247 0248 return createObjectFromComponent(component, context, initialProperties); 0249 } 0250 0251 QObject *SharedQmlEngine::createObjectFromComponent(QQmlComponent *component, QQmlContext *context, const QVariantHash &initialProperties) 0252 { 0253 QObject *object = component->beginCreate(context ? context : d->rootContext); 0254 0255 for (auto it = initialProperties.constBegin(); it != initialProperties.constEnd(); ++it) { 0256 object->setProperty(it.key().toUtf8().data(), it.value()); 0257 } 0258 component->completeCreate(); 0259 0260 if (!component->isError() && object) { 0261 // memory management 0262 component->setParent(object); 0263 // reparent to root object if wasn't specified otherwise by initialProperties 0264 if (!initialProperties.contains(QLatin1String("parent"))) { 0265 if (qobject_cast<QQuickItem *>(rootObject())) { 0266 object->setProperty("parent", QVariant::fromValue(rootObject())); 0267 } else { 0268 object->setParent(rootObject()); 0269 } 0270 } 0271 0272 return object; 0273 0274 } else { 0275 d->errorPrint(component); 0276 delete object; 0277 return nullptr; 0278 } 0279 } 0280 } 0281 0282 #include "moc_sharedqmlengine.cpp"