File indexing completed on 2024-05-12 03:54:09

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"