File indexing completed on 2024-05-12 07:47:31
0001 /* 0002 SPDX-FileCopyrightText: 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> 0003 SPDX-FileCopyrightText: 2001 Michael Goffioul <kdeprint@swing.be> 0004 SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com> 0005 SPDX-FileCopyrightText: 2009 Dario Freddi <drf@kde.org> 0006 SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org> 0007 SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de> 0008 0009 SPDX-License-Identifier: LGPL-2.0-or-later 0010 */ 0011 0012 #include "kquickconfigmodule.h" 0013 #include "kabstractconfigmodule.h" 0014 #include "kcmutils_debug.h" 0015 #include "sharedqmlengine_p.h" 0016 0017 #include <QDebug> 0018 #include <QQmlContext> 0019 #include <QQmlEngine> 0020 #include <QQmlFileSelector> 0021 #include <QQuickItem> 0022 #include <QResource> 0023 #include <QUrl> 0024 0025 #include <KLocalizedContext> 0026 #include <KLocalizedString> 0027 0028 #include <memory> 0029 0030 class KQuickConfigModulePrivate 0031 { 0032 public: 0033 KQuickConfigModulePrivate(KQuickConfigModule *module) 0034 : q(module) 0035 { 0036 } 0037 0038 KQuickConfigModule *q; 0039 SharedQmlEngine *engine = nullptr; 0040 std::shared_ptr<QQmlEngine> passedInEngine; 0041 QList<QQuickItem *> subPages; 0042 int columnWidth = -1; 0043 int currentIndex = 0; 0044 QString errorString; 0045 0046 static QHash<QQmlContext *, KQuickConfigModule *> rootObjects; 0047 0048 QString getResourcePath(const QString &file) 0049 { 0050 return QLatin1String("/kcm/") + q->metaData().pluginId() + QLatin1String("/") + file; 0051 } 0052 QUrl getResourceUrl(const QString &resourcePath) 0053 { 0054 return QUrl(QLatin1String("qrc:") + resourcePath); 0055 } 0056 }; 0057 0058 QHash<QQmlContext *, KQuickConfigModule *> KQuickConfigModulePrivate::rootObjects = QHash<QQmlContext *, KQuickConfigModule *>(); 0059 0060 KQuickConfigModule::KQuickConfigModule(QObject *parent, const KPluginMetaData &metaData) 0061 : KAbstractConfigModule(parent, metaData) 0062 , d(new KQuickConfigModulePrivate(this)) 0063 { 0064 } 0065 0066 void KQuickConfigModule::setInternalEngine(const std::shared_ptr<QQmlEngine> &engine) 0067 { 0068 d->passedInEngine = engine; 0069 } 0070 0071 KQuickConfigModule::~KQuickConfigModule() 0072 { 0073 // in case mainUi was never called 0074 if (d->engine) { 0075 // delete the mainUi before removing the root object. 0076 // Otherwise, we get lots of console errors about trying to read properties of null objects 0077 delete d->engine->rootObject(); 0078 KQuickConfigModulePrivate::rootObjects.remove(d->engine->rootContext()); 0079 } 0080 } 0081 0082 KQuickConfigModule *KQuickConfigModule::qmlAttachedProperties(QObject *object) 0083 { 0084 // at the moment of the attached object creation, the root item is the only one that hasn't a parent 0085 // only way to avoid creation of this attached for everybody but the root item 0086 const QQmlEngine *engine = qmlEngine(object); 0087 QQmlContext *ctx = qmlContext(object); 0088 0089 // Search the qml context that is the "root" for the sharedqmlobject, 0090 // which is an ancestor of qmlContext(object) and the direct child of the 0091 // engine's root context: we can do this assumption on the internals as 0092 // we are distributed on the same repo. 0093 while (ctx->parentContext() && ctx->parentContext() != engine->rootContext()) { 0094 ctx = ctx->parentContext(); 0095 } 0096 0097 if (!object->parent() && KQuickConfigModulePrivate::rootObjects.contains(ctx)) { 0098 return KQuickConfigModulePrivate::rootObjects.value(ctx); 0099 } else { 0100 return nullptr; 0101 } 0102 } 0103 0104 QQuickItem *KQuickConfigModule::mainUi() 0105 { 0106 Q_ASSERT(d->passedInEngine); 0107 if (d->engine) { 0108 return qobject_cast<QQuickItem *>(d->engine->rootObject()); 0109 } 0110 0111 d->errorString.clear(); 0112 d->engine = new SharedQmlEngine(d->passedInEngine, this); 0113 0114 const QString componentName = metaData().pluginId(); 0115 KQuickConfigModulePrivate::rootObjects[d->engine->rootContext()] = this; 0116 d->engine->setTranslationDomain(componentName); 0117 d->engine->setInitializationDelayed(true); 0118 0119 const QString resourcePath = d->getResourcePath(QStringLiteral("main.qml")); 0120 if (QResource r(resourcePath); !r.isValid()) { 0121 d->errorString = i18n("Could not find resource '%1'", resourcePath); 0122 qCWarning(KCMUTILS_LOG) << "Could not find resource" << resourcePath; 0123 return nullptr; 0124 } 0125 0126 new QQmlFileSelector(d->engine->engine().get(), this); 0127 d->engine->setSource(d->getResourceUrl(resourcePath)); 0128 d->engine->rootContext()->setContextProperty(QStringLiteral("kcm"), this); 0129 d->engine->completeInitialization(); 0130 0131 if (d->engine->status() != QQmlComponent::Ready) { 0132 d->errorString = d->engine->mainComponent()->errorString(); 0133 return nullptr; 0134 } 0135 0136 Q_EMIT mainUiReady(); 0137 0138 return qobject_cast<QQuickItem *>(d->engine->rootObject()); 0139 } 0140 0141 void KQuickConfigModule::push(const QString &fileName, const QVariantMap &initialProperties) 0142 { 0143 // ensure main ui is created 0144 if (!mainUi()) { 0145 return; 0146 } 0147 0148 const QString resourcePath = d->getResourcePath(fileName); 0149 if (QResource r(resourcePath); !r.isValid()) { 0150 qCWarning(KCMUTILS_LOG) << "Requested resource" << resourcePath << "does not exist"; 0151 } 0152 QObject *object = d->engine->createObjectFromSource(d->getResourceUrl(resourcePath), d->engine->rootContext(), initialProperties); 0153 0154 QQuickItem *item = qobject_cast<QQuickItem *>(object); 0155 if (!item) { 0156 object->deleteLater(); 0157 return; 0158 } 0159 0160 d->subPages << item; 0161 Q_EMIT pagePushed(item); 0162 Q_EMIT depthChanged(depth()); 0163 setCurrentIndex(d->currentIndex + 1); 0164 } 0165 0166 void KQuickConfigModule::push(QQuickItem *item) 0167 { 0168 // ensure main ui is created 0169 if (!mainUi()) { 0170 return; 0171 } 0172 0173 d->subPages << item; 0174 Q_EMIT pagePushed(item); 0175 Q_EMIT depthChanged(depth()); 0176 setCurrentIndex(d->currentIndex + 1); 0177 } 0178 0179 void KQuickConfigModule::pop() 0180 { 0181 if (QQuickItem *page = takeLast()) { 0182 page->deleteLater(); 0183 } 0184 } 0185 0186 QQuickItem *KQuickConfigModule::takeLast() 0187 { 0188 if (d->subPages.isEmpty()) { 0189 return nullptr; 0190 } 0191 QQuickItem *page = d->subPages.takeLast(); 0192 Q_EMIT pageRemoved(); 0193 Q_EMIT depthChanged(depth()); 0194 setCurrentIndex(qMin(d->currentIndex, depth() - 1)); 0195 return page; 0196 } 0197 0198 int KQuickConfigModule::columnWidth() const 0199 { 0200 return d->columnWidth; 0201 } 0202 0203 void KQuickConfigModule::setColumnWidth(int width) 0204 { 0205 if (d->columnWidth == width) { 0206 return; 0207 } 0208 0209 d->columnWidth = width; 0210 Q_EMIT columnWidthChanged(width); 0211 } 0212 0213 int KQuickConfigModule::depth() const 0214 { 0215 return d->subPages.count() + 1; 0216 } 0217 0218 void KQuickConfigModule::setCurrentIndex(int index) 0219 { 0220 if (index < 0 || index > d->subPages.count() || index == d->currentIndex) { 0221 return; 0222 } 0223 0224 d->currentIndex = index; 0225 0226 Q_EMIT currentIndexChanged(index); 0227 } 0228 0229 int KQuickConfigModule::currentIndex() const 0230 { 0231 return d->currentIndex; 0232 } 0233 0234 std::shared_ptr<QQmlEngine> KQuickConfigModule::engine() const 0235 { 0236 return d->engine->engine(); 0237 } 0238 0239 QString KQuickConfigModule::errorString() const 0240 { 0241 return d->errorString; 0242 } 0243 0244 QQuickItem *KQuickConfigModule::subPage(int index) const 0245 { 0246 return d->subPages[index]; 0247 } 0248 0249 #include "moc_kquickconfigmodule.cpp"