File indexing completed on 2024-05-05 16:07:09
0001 /* 0002 SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "qmlobject.h" 0008 #include "private/kdeclarative_p.h" 0009 0010 #include <QQmlContext> 0011 #include <QQmlEngine> 0012 #include <QQmlIncubator> 0013 #include <QQmlNetworkAccessManagerFactory> 0014 #include <QQuickItem> 0015 #include <QTimer> 0016 0017 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 98) 0018 #include <KPackage/PackageLoader> 0019 #endif 0020 #include <QDebug> 0021 #include <kdeclarative.h> 0022 0023 //#include "packageaccessmanagerfactory.h" 0024 //#include "private/declarative/dataenginebindings_p.h" 0025 0026 namespace KDeclarative 0027 { 0028 class QmlObjectIncubator : public QQmlIncubator 0029 { 0030 public: 0031 QVariantHash m_initialProperties; 0032 0033 protected: 0034 void setInitialState(QObject *object) override 0035 { 0036 QHashIterator<QString, QVariant> i(m_initialProperties); 0037 while (i.hasNext()) { 0038 i.next(); 0039 object->setProperty(i.key().toLatin1().data(), i.value()); 0040 } 0041 } 0042 }; 0043 0044 class QmlObjectPrivate 0045 { 0046 public: 0047 QmlObjectPrivate(QmlObject *parent) 0048 : q(parent) 0049 , engine(nullptr) 0050 , component(nullptr) 0051 , delay(false) 0052 { 0053 executionEndTimer = new QTimer(q); 0054 executionEndTimer->setInterval(0); 0055 executionEndTimer->setSingleShot(true); 0056 QObject::connect(executionEndTimer, &QTimer::timeout, q, [this]() { 0057 scheduleExecutionEnd(); 0058 }); 0059 } 0060 0061 ~QmlObjectPrivate() 0062 { 0063 delete incubator.object(); 0064 } 0065 0066 void errorPrint(QQmlComponent *component); 0067 void execute(const QUrl &source); 0068 void scheduleExecutionEnd(); 0069 void minimumWidthChanged(); 0070 void minimumHeightChanged(); 0071 void maximumWidthChanged(); 0072 void maximumHeightChanged(); 0073 void preferredWidthChanged(); 0074 void preferredHeightChanged(); 0075 void checkInitializationCompleted(); 0076 0077 QmlObject *q; 0078 0079 QUrl source; 0080 std::shared_ptr<QQmlEngine> engine; 0081 0082 QmlObjectIncubator incubator; 0083 QQmlComponent *component; 0084 QTimer *executionEndTimer; 0085 KLocalizedContext *context{nullptr}; 0086 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 98) 0087 KPackage::Package package; 0088 #endif 0089 QQmlContext *rootContext; 0090 bool delay : 1; 0091 }; 0092 0093 void QmlObjectPrivate::errorPrint(QQmlComponent *component) 0094 { 0095 QString errorStr = QStringLiteral("Error loading QML file.\n"); 0096 if (component->isError()) { 0097 const QList<QQmlError> errors = component->errors(); 0098 for (const QQmlError &error : errors) { 0099 errorStr += 0100 (error.line() > 0 ? QString(QString::number(error.line()) + QLatin1String(": ")) : QLatin1String("")) + error.description() + QLatin1Char('\n'); 0101 } 0102 } 0103 qWarning() << component->url().toString() << '\n' << errorStr; 0104 } 0105 0106 void QmlObjectPrivate::execute(const QUrl &source) 0107 { 0108 if (source.isEmpty()) { 0109 qWarning() << "File name empty!"; 0110 return; 0111 } 0112 0113 delete component; 0114 component = new QQmlComponent(engine.get(), q); 0115 QObject::connect(component, &QQmlComponent::statusChanged, q, &QmlObject::statusChanged, Qt::QueuedConnection); 0116 delete incubator.object(); 0117 0118 component->loadUrl(source); 0119 0120 if (delay) { 0121 executionEndTimer->start(0); 0122 } else { 0123 scheduleExecutionEnd(); 0124 } 0125 } 0126 0127 void QmlObjectPrivate::scheduleExecutionEnd() 0128 { 0129 if (component->isReady() || component->isError()) { 0130 q->completeInitialization(); 0131 } else { 0132 QObject::connect(component, &QQmlComponent::statusChanged, q, [this]() { 0133 q->completeInitialization(); 0134 }); 0135 } 0136 } 0137 0138 QmlObject::QmlObject(QObject *parent) 0139 : QmlObject(nullptr, nullptr, parent) 0140 { 0141 } 0142 0143 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 95) 0144 QmlObject::QmlObject(QQmlEngine *engine, QObject *parent) 0145 : QmlObject(std::shared_ptr<QQmlEngine>(engine), nullptr, parent) 0146 { 0147 } 0148 0149 QmlObject::QmlObject(QQmlEngine *engine, QQmlContext *rootContext, QObject *parent) 0150 : QmlObject(std::shared_ptr<QQmlEngine>(engine), rootContext, parent) 0151 { 0152 } 0153 0154 QmlObject::QmlObject(QQmlEngine *engine, QQmlContext *rootContext, QmlObject *obj, QObject *parent) 0155 : QmlObject(std::shared_ptr<QQmlEngine>(engine), rootContext, parent) 0156 { 0157 Q_UNUSED(obj); 0158 } 0159 #endif 0160 0161 QmlObject::QmlObject(std::shared_ptr<QQmlEngine> engine, QQmlContext *rootContext, QObject *parent) 0162 : QObject(parent) 0163 , d(new QmlObjectPrivate(this)) 0164 { 0165 if (engine) { 0166 d->engine = engine; 0167 } else { 0168 d->engine = std::make_shared<QQmlEngine>(); 0169 } 0170 0171 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 98) 0172 if (d->engine.use_count() <= 2) { 0173 KDeclarative::setupEngine(d->engine.get()); 0174 } 0175 #endif 0176 0177 if (rootContext) { 0178 d->rootContext = rootContext; 0179 } else { 0180 d->rootContext = d->engine->rootContext(); 0181 } 0182 0183 d->context = new KLocalizedContext(d->rootContext); 0184 d->rootContext->setContextObject(d->context); 0185 } 0186 0187 QmlObject::~QmlObject() 0188 { 0189 if (d->engine.use_count() == 1) { 0190 // QQmlEngine does not take ownership of the QNAM factory so we need to 0191 // make sure to clean it, but only if we are the last user of the engine 0192 // otherwise we risk resetting the factory on an engine that is still in 0193 // use. 0194 auto factory = d->engine->networkAccessManagerFactory(); 0195 d->engine->setNetworkAccessManagerFactory(nullptr); 0196 delete factory; 0197 } 0198 0199 delete d; 0200 } 0201 0202 void QmlObject::setTranslationDomain(const QString &translationDomain) 0203 { 0204 d->context->setTranslationDomain(translationDomain); 0205 } 0206 0207 QString QmlObject::translationDomain() const 0208 { 0209 return d->context->translationDomain(); 0210 } 0211 0212 void QmlObject::setSource(const QUrl &source) 0213 { 0214 d->source = source; 0215 d->execute(source); 0216 } 0217 0218 QUrl QmlObject::source() const 0219 { 0220 return d->source; 0221 } 0222 0223 #if KDECLARATIVE_BUILD_DEPRECATED_SINCE(5, 98) 0224 void QmlObject::loadPackage(const QString &packageName) 0225 { 0226 d->package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("KPackage/GenericQML")); 0227 d->package.setPath(packageName); 0228 setSource(QUrl::fromLocalFile(d->package.filePath("mainscript"))); 0229 } 0230 0231 void QmlObject::setPackage(const KPackage::Package &package) 0232 { 0233 d->package = package; 0234 setSource(QUrl::fromLocalFile(package.filePath("mainscript"))); 0235 } 0236 0237 KPackage::Package QmlObject::package() const 0238 { 0239 return d->package; 0240 } 0241 #endif 0242 0243 void QmlObject::setInitializationDelayed(const bool delay) 0244 { 0245 d->delay = delay; 0246 } 0247 0248 bool QmlObject::isInitializationDelayed() const 0249 { 0250 return d->delay; 0251 } 0252 0253 QQmlEngine *QmlObject::engine() 0254 { 0255 return d->engine.get(); 0256 } 0257 0258 QObject *QmlObject::rootObject() const 0259 { 0260 if (d->incubator.status() == QQmlIncubator::Loading) { 0261 qWarning() << "Trying to use rootObject before initialization is completed, whilst using setInitializationDelayed. Forcing completion"; 0262 d->incubator.forceCompletion(); 0263 } 0264 return d->incubator.object(); 0265 } 0266 0267 QQmlComponent *QmlObject::mainComponent() const 0268 { 0269 return d->component; 0270 } 0271 0272 QQmlContext *QmlObject::rootContext() const 0273 { 0274 return d->rootContext; 0275 } 0276 0277 QQmlComponent::Status QmlObject::status() const 0278 { 0279 if (!d->engine) { 0280 return QQmlComponent::Error; 0281 } 0282 0283 if (!d->component) { 0284 return QQmlComponent::Null; 0285 } 0286 0287 return QQmlComponent::Status(d->component->status()); 0288 } 0289 0290 void QmlObjectPrivate::checkInitializationCompleted() 0291 { 0292 if (!incubator.isReady() && incubator.status() != QQmlIncubator::Error) { 0293 QTimer::singleShot(0, q, SLOT(checkInitializationCompleted())); 0294 return; 0295 } 0296 0297 if (!incubator.object()) { 0298 errorPrint(component); 0299 } 0300 0301 Q_EMIT q->finished(); 0302 } 0303 0304 void QmlObject::completeInitialization(const QVariantHash &initialProperties) 0305 { 0306 d->executionEndTimer->stop(); 0307 if (d->incubator.object()) { 0308 return; 0309 } 0310 0311 if (!d->component) { 0312 qWarning() << "No component for" << source(); 0313 return; 0314 } 0315 0316 if (d->component->status() != QQmlComponent::Ready || d->component->isError()) { 0317 d->errorPrint(d->component); 0318 return; 0319 } 0320 0321 d->incubator.m_initialProperties = initialProperties; 0322 d->component->create(d->incubator, d->rootContext); 0323 0324 if (d->delay) { 0325 d->checkInitializationCompleted(); 0326 } else { 0327 d->incubator.forceCompletion(); 0328 0329 if (!d->incubator.object()) { 0330 d->errorPrint(d->component); 0331 } 0332 Q_EMIT finished(); 0333 } 0334 } 0335 0336 QObject *QmlObject::createObjectFromSource(const QUrl &source, QQmlContext *context, const QVariantHash &initialProperties) 0337 { 0338 QQmlComponent *component = new QQmlComponent(d->engine.get(), this); 0339 component->loadUrl(source); 0340 0341 return createObjectFromComponent(component, context, initialProperties); 0342 } 0343 0344 QObject *QmlObject::createObjectFromComponent(QQmlComponent *component, QQmlContext *context, const QVariantHash &initialProperties) 0345 { 0346 QmlObjectIncubator incubator; 0347 incubator.m_initialProperties = initialProperties; 0348 component->create(incubator, context ? context : d->rootContext); 0349 incubator.forceCompletion(); 0350 0351 QObject *object = incubator.object(); 0352 0353 if (!component->isError() && object) { 0354 // memory management 0355 component->setParent(object); 0356 // reparent to root object if wasn't specified otherwise by initialProperties 0357 if (!initialProperties.contains(QLatin1String("parent"))) { 0358 if (qobject_cast<QQuickItem *>(rootObject())) { 0359 object->setProperty("parent", QVariant::fromValue(rootObject())); 0360 } else { 0361 object->setParent(rootObject()); 0362 } 0363 } 0364 0365 return object; 0366 0367 } else { 0368 d->errorPrint(component); 0369 delete object; 0370 return nullptr; 0371 } 0372 } 0373 0374 } 0375 0376 #include "moc_qmlobject.cpp"