Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,58 @@
# SPDX-FileCopyrightText: 2022 Alexander Lohnau <alexander.lohnau@gmx.de>
# SPDX-License-Identifier: BSD-3-Clause
add_library(KF6KCMUtilsQuick
kquickconfigmodule.cpp
kquickmanagedconfigmodule.cpp
sharedqmlengine.cpp
kquickconfigmoduleloader.cpp
)
qt_extract_metatypes(KF6KCMUtilsQuick)
ecm_generate_export_header(KF6KCMUtilsQuick
BASE_NAME KCMUtilsQuick
GROUP_BASE_NAME KF
VERSION ${KF_VERSION}
DEPRECATED_BASE_VERSION 0
)
target_include_directories(KF6KCMUtilsQuick INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KCMUtilsQuick>")
target_link_libraries(KF6KCMUtilsQuick
PUBLIC
KF6::CoreAddons
KF6::ConfigCore
Qt6::Qml
KF6KCMUtilsCore
PRIVATE
KF6::I18n
KF6::ConfigGui
Qt6::Quick
kcmutils_logging_STATIC
)
if(TARGET KF6::I18nQml)
target_link_libraries(KF6KCMUtilsQuick PRIVATE
KF6::I18nQml
)
endif()
set_target_properties(KF6KCMUtilsQuick PROPERTIES
VERSION ${KCMUTILS_VERSION}
SOVERSION ${KCMUTILS_SOVERSION}
EXPORT_NAME KCMUtilsQuick)
ecm_generate_headers(KCMUtilsQuick_HEADERS
HEADER_NAMES
KQuickConfigModule
KQuickManagedConfigModule
KQuickConfigModuleLoader
REQUIRED_HEADERS KCMUtilsQuick_HEADERS
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/kcmutilsquick_export.h
${KCMUtilsQuick_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KCMUtilsQuick COMPONENT Devel
)
install(TARGETS KF6KCMUtilsQuick EXPORT KF6KCMUtilsTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
@@ -0,0 +1,251 @@
/*
SPDX-FileCopyrightText: 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
SPDX-FileCopyrightText: 2001 Michael Goffioul <kdeprint@swing.be>
SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com>
SPDX-FileCopyrightText: 2009 Dario Freddi <drf@kde.org>
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kquickconfigmodule.h"
#include "kabstractconfigmodule.h"
#include "kcmutils_debug.h"
#include "sharedqmlengine_p.h"
#include <QDebug>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQmlFileSelector>
#include <QQuickItem>
#include <QResource>
#include <QUrl>
#include <KLocalizedContext>
#include <KLocalizedString>
#include <memory>
class KQuickConfigModulePrivate
{
public:
KQuickConfigModulePrivate(KQuickConfigModule *module)
: q(module)
{
}
KQuickConfigModule *q;
SharedQmlEngine *engine = nullptr;
std::shared_ptr<QQmlEngine> passedInEngine;
QList<QQuickItem *> subPages;
int columnWidth = -1;
int currentIndex = 0;
QString errorString;
static QHash<QQmlContext *, KQuickConfigModule *> rootObjects;
QString getResourcePath(const QString &file)
{
return QLatin1String("/kcm/") + q->metaData().pluginId() + QLatin1String("/") + file;
}
QUrl getResourceUrl(const QString &resourcePath)
{
return QUrl(QLatin1String("qrc:") + resourcePath);
}
};
QHash<QQmlContext *, KQuickConfigModule *> KQuickConfigModulePrivate::rootObjects = QHash<QQmlContext *, KQuickConfigModule *>();
KQuickConfigModule::KQuickConfigModule(QObject *parent, const KPluginMetaData &metaData)
: KAbstractConfigModule(parent, metaData)
, d(new KQuickConfigModulePrivate(this))
{
}
void KQuickConfigModule::setInternalEngine(const std::shared_ptr<QQmlEngine> &engine)
{
d->passedInEngine = engine;
}
KQuickConfigModule::~KQuickConfigModule()
{
// in case mainUi was never called
if (d->engine) {
// delete the mainUi before removing the root object.
// Otherwise, we get lots of console errors about trying to read properties of null objects
delete d->engine->rootObject();
KQuickConfigModulePrivate::rootObjects.remove(d->engine->rootContext());
}
}
KQuickConfigModule *KQuickConfigModule::qmlAttachedProperties(QObject *object)
{
// at the moment of the attached object creation, the root item is the only one that hasn't a parent
// only way to avoid creation of this attached for everybody but the root item
const QQmlEngine *engine = qmlEngine(object);
QQmlContext *ctx = qmlContext(object);
// Search the qml context that is the "root" for the sharedqmlobject,
// which is an ancestor of qmlContext(object) and the direct child of the
// engine's root context: we can do this assumption on the internals as
// we are distributed on the same repo.
while (ctx->parentContext() && ctx->parentContext() != engine->rootContext()) {
ctx = ctx->parentContext();
}
if (!object->parent() && KQuickConfigModulePrivate::rootObjects.contains(ctx)) {
return KQuickConfigModulePrivate::rootObjects.value(ctx);
} else {
return nullptr;
}
}
QQuickItem *KQuickConfigModule::mainUi()
{
Q_ASSERT(d->passedInEngine);
if (d->engine) {
return qobject_cast<QQuickItem *>(d->engine->rootObject());
}
d->errorString.clear();
d->engine = new SharedQmlEngine(d->passedInEngine, this);
const QString componentName = metaData().pluginId();
KQuickConfigModulePrivate::rootObjects[d->engine->rootContext()] = this;
d->engine->setTranslationDomain(componentName);
d->engine->setInitializationDelayed(true);
const QString resourcePath = d->getResourcePath(QStringLiteral("main.qml"));
if (QResource r(resourcePath); !r.isValid()) {
d->errorString = i18n("Could not find resource '%1'", resourcePath);
qCWarning(KCMUTILS_LOG) << "Could not find resource" << resourcePath;
return nullptr;
}
new QQmlFileSelector(d->engine->engine().get(), this);
d->engine->setSource(d->getResourceUrl(resourcePath));
d->engine->rootContext()->setContextProperty(QStringLiteral("kcm"), this);
d->engine->completeInitialization();
if (d->engine->isError()) {
d->errorString = d->engine->errorString();
return nullptr;
}
Q_EMIT mainUiReady();
return qobject_cast<QQuickItem *>(d->engine->rootObject());
}
void KQuickConfigModule::push(const QString &fileName, const QVariantMap &initialProperties)
{
// ensure main ui is created
if (!mainUi()) {
return;
}
const QString resourcePath = d->getResourcePath(fileName);
if (QResource r(resourcePath); !r.isValid()) {
qCWarning(KCMUTILS_LOG) << "Requested resource" << resourcePath << "does not exist";
}
QObject *object = d->engine->createObjectFromSource(d->getResourceUrl(resourcePath), d->engine->rootContext(), initialProperties);
QQuickItem *item = qobject_cast<QQuickItem *>(object);
if (!item) {
if (object) {
object->deleteLater();
}
return;
}
d->subPages << item;
Q_EMIT pagePushed(item);
Q_EMIT depthChanged(depth());
setCurrentIndex(d->currentIndex + 1);
}
void KQuickConfigModule::push(QQuickItem *item)
{
// ensure main ui is created
if (!mainUi()) {
return;
}
d->subPages << item;
Q_EMIT pagePushed(item);
Q_EMIT depthChanged(depth());
setCurrentIndex(d->currentIndex + 1);
}
void KQuickConfigModule::pop()
{
if (QQuickItem *page = takeLast()) {
page->deleteLater();
}
}
QQuickItem *KQuickConfigModule::takeLast()
{
if (d->subPages.isEmpty()) {
return nullptr;
}
QQuickItem *page = d->subPages.takeLast();
Q_EMIT pageRemoved();
Q_EMIT depthChanged(depth());
setCurrentIndex(qMin(d->currentIndex, depth() - 1));
return page;
}
int KQuickConfigModule::columnWidth() const
{
return d->columnWidth;
}
void KQuickConfigModule::setColumnWidth(int width)
{
if (d->columnWidth == width) {
return;
}
d->columnWidth = width;
Q_EMIT columnWidthChanged(width);
}
int KQuickConfigModule::depth() const
{
return d->subPages.count() + 1;
}
void KQuickConfigModule::setCurrentIndex(int index)
{
if (index < 0 || index > d->subPages.count() || index == d->currentIndex) {
return;
}
d->currentIndex = index;
Q_EMIT currentIndexChanged(index);
}
int KQuickConfigModule::currentIndex() const
{
return d->currentIndex;
}
std::shared_ptr<QQmlEngine> KQuickConfigModule::engine() const
{
return d->engine->engine();
}
QString KQuickConfigModule::errorString() const
{
return d->errorString;
}
QQuickItem *KQuickConfigModule::subPage(int index) const
{
return d->subPages[index];
}
#include "moc_kquickconfigmodule.cpp"
@@ -0,0 +1,254 @@
/*
SPDX-FileCopyrightText: 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
SPDX-FileCopyrightText: 2001 Michael Goffioul <kdeprint@swing.be>
SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com>
SPDX-FileCopyrightText: 2009 Dario Freddi <drf@kde.org>
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KQUICKCONFIGMODULE_H
#define KQUICKCONFIGMODULE_H
#include "kcmutilsquick_export.h"
#include <QObject>
#include <QQmlComponent>
#include <QStringList>
#include <QVariant>
#include <KPluginFactory>
#include <KPluginMetaData>
#include <memory>
#include <qqmlintegration.h>
#include "kabstractconfigmodule.h"
#include "kquickconfigmoduleloader.h"
class QQuickItem;
class QQmlEngine;
class KQuickConfigModulePrivate;
/**
* @class KQuickConfigModule kquickconfigmodule.h KQuickConfigModule
*
* The base class for QtQuick configuration modules.
* Configuration modules are realized as plugins that are dynamically loaded.
*
* All the necessary glue logic and the GUI bells and whistles
* are provided by the control center and must not concern
* the module author.
*
* To write a config module, you have to create a C++ plugin
* and an accompaning QML user interface.
*
* To allow KCMUtils to load your ConfigModule subclass, you must create a KPluginFactory implementation.
*
* \code
* #include <KPluginFactory>
*
* K_PLUGIN_CLASS_WITH_JSON(MyConfigModule, "yourmetadata.json")
* \endcode
*
* The constructor of the ConfigModule then looks like this:
* \code
* YourConfigModule::YourConfigModule(QObject *parent, const KPluginMetaData &metaData)
* : KQuickConfigModule(parent, metaData)
* {
* }
* \endcode
*
* The QML part must be in the KPackage format, installed under share/kpackage/kcms.
* @see KPackage::Package
*
* The package must have the same name as the plugin filename, to be installed
* by CMake with the command:
* \code
* kpackage_install_package(packagedir kcm_yourconfigmodule kcms)
* \endcode
* The "packagedir" is the subdirectory in the source tree where the package sources are
* located, and "kcm_yourconfigmodule" is id of the plugin.
* Finally "kcms" is the literal string "kcms", so that the package is
* installed as a configuration module (and not some other kind of package).
*
* The QML part can access all the properties of ConfigModule (together with the properties
* defined in its subclass) by accessing to the global object "kcm", or with the
* import of "org.kde.kcmutils" the ConfigModule attached property.
*
* \code
* import QtQuick
* import QtQuick.Controls as QQC2
* import org.kde.kcmutils as KCMUtils
* import org.kde.kirigami as Kirigami
*
* Item {
* // implicit size will be used as initial size when loaded in kcmshell6
* implicitWidth: Kirigami.Units.gridUnit * 30
* implicitHeight: Kirigami.Units.gridUnit * 30
*
* KCMUtils.ConfigModule.buttons: KCMUtils.ConfigModule.Help | KCMUtils.ConfigModule.Apply
*
* QQC2.Label {
* // The following two bindings are equivalent:
* text: kcm.needsSave
* enabled: KCMUtils.ConfigModule.needsSave
* }
* }
* \endcode
*
* See https://develop.kde.org/docs/extend/kcm/ for more detailed documentation.
* @since 6.0
*/
class KCMUTILSQUICK_EXPORT KQuickConfigModule : public KAbstractConfigModule
{
Q_OBJECT
Q_PROPERTY(QQuickItem *mainUi READ mainUi CONSTANT)
Q_PROPERTY(int columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged)
Q_PROPERTY(int depth READ depth NOTIFY depthChanged)
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
QML_NAMED_ELEMENT(ConfigModule)
QML_ATTACHED(KQuickConfigModule)
public:
/**
* Destroys the module.
*/
~KQuickConfigModule() override;
/**
* @return the qml engine that built the main config UI
*/
std::shared_ptr<QQmlEngine> engine() const;
/**
* The error string in case the mainUi failed to load.
*/
QString errorString() const;
// QML property accessors
/**
* @return The main UI for this configuration module. It's a QQuickItem coming from
* the QML package named the same as the KAboutData's component name for
* this config module
*/
QQuickItem *mainUi();
/*
* @return a subpage at a given depth
* @note This does not include the mainUi. i.e a depth of 2 is a mainUi and one subPage
* at index 0
*/
QQuickItem *subPage(int index) const;
/**
* returns the width the kcm wants in column mode.
* If a columnWidth is valid ( > 0 ) and less than the systemsettings' view width,
* more than one will be visible at once, and the first page will be a sidebar to the last page pushed.
* As default, this is -1 which will make the shell always show only one page at a time.
*/
int columnWidth() const;
/**
* Sets the column width we want.
*/
void setColumnWidth(int width);
/**
* @returns how many pages this kcm has.
* It is guaranteed to be at least 1 (the main ui) plus how many times a new page has been pushed without pop
*/
int depth() const;
/**
* Sets the current page index this kcm should display
*/
void setCurrentIndex(int index);
/**
* @returns the index of the page this kcm should display
*/
int currentIndex() const;
static KQuickConfigModule *qmlAttachedProperties(QObject *object);
public Q_SLOTS:
/**
* Push a new sub page in the KCM hierarchy: pages will be seen as a Kirigami PageRow
*/
void push(const QString &fileName, const QVariantMap &initialProperties = QVariantMap());
/**
*
*/
void push(QQuickItem *item);
/**
* pop the last page of the KCM hierarchy, the page is destroyed
*/
void pop();
/**
* remove and return the last page of the KCM hierarchy:
* the popped page won't be deleted, it's the caller's responsibility to manage the lifetime of the returned item
* @returns the last page if any, nullptr otherwise
*/
QQuickItem *takeLast();
Q_SIGNALS:
// QML NOTIFY signaling
/**
* Emitted when a new sub page is pushed
*/
void pagePushed(QQuickItem *page);
/**
* Emitted when a sub page is popped
*/
// RFC: page argument?
void pageRemoved();
/**
* Emitted when the wanted column width of the kcm changes
*/
void columnWidthChanged(int width);
/**
* Emitted when the current page changed
*/
void currentIndexChanged(int index);
/**
* Emitted when the number of pages changed
*/
void depthChanged(int index);
/**
* Emitted when the main Ui has loaded successfully and `mainUi()` is available
*/
void mainUiReady();
protected:
/**
* Base class for all QtQuick config modules.
* Use KQuickConfigModuleLoader to instantiate this class
*
* @note do not emit changed signals here, since they are not yet connected to any slot.
*/
explicit KQuickConfigModule(QObject *parent, const KPluginMetaData &metaData);
private:
void setInternalEngine(const std::shared_ptr<QQmlEngine> &engine);
friend KPluginFactory::Result<KQuickConfigModule>
KQuickConfigModuleLoader::loadModule(const KPluginMetaData &metaData, QObject *parent, const QVariantList &args, const std::shared_ptr<QQmlEngine> &engine);
const std::unique_ptr<KQuickConfigModulePrivate> d;
};
#endif // KQUICKCONFIGMODULE_H
@@ -0,0 +1,50 @@
/*
SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kquickconfigmoduleloader.h"
#include "kcmutils_debug.h"
#include <KPluginFactory>
#include <QJsonArray>
#include <QQmlEngine>
#include "kquickconfigmodule.h"
std::weak_ptr<QQmlEngine> s_kcmutilsCreatedEngine;
KPluginFactory::Result<KQuickConfigModule>
KQuickConfigModuleLoader::loadModule(const KPluginMetaData &metaData, QObject *parent, const QVariantList &args, const std::shared_ptr<QQmlEngine> &engineArg)
{
const auto factoryResult = KPluginFactory::loadFactory(metaData);
KPluginFactory::Result<KQuickConfigModule> result;
if (!factoryResult) {
result.errorReason = factoryResult.errorReason;
result.errorString = factoryResult.errorString;
result.errorText = factoryResult.errorText;
return result;
}
KPluginFactory *factory = factoryResult.plugin;
factory->setMetaData(KPluginMetaData(metaData));
const QVariantList pluginArgs = QVariantList(args) << metaData.rawData().value(QLatin1String("X-KDE-KCM-Args")).toArray().toVariantList();
if (const auto kcm = factory->create<KQuickConfigModule>(parent, pluginArgs)) {
const std::shared_ptr<QQmlEngine> engine =
engineArg ? engineArg : (s_kcmutilsCreatedEngine.expired() ? std::make_shared<QQmlEngine>() : s_kcmutilsCreatedEngine.lock());
if (!engineArg && s_kcmutilsCreatedEngine.expired()) {
s_kcmutilsCreatedEngine = engine;
}
kcm->setInternalEngine(engine);
result.plugin = kcm;
qCDebug(KCMUTILS_LOG) << "loaded QML KCM" << metaData.fileName();
} else {
result.errorReason = KPluginFactory::INVALID_KPLUGINFACTORY_INSTANTIATION;
}
return result;
}
@@ -0,0 +1,26 @@
/*
SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KQUICKCONFIGMODULELOADER_H
#define KQUICKCONFIGMODULELOADER_H
#include "kcmutilsquick_export.h"
#include <KPluginFactory>
#include <memory>
class QQmlEngine;
class KQuickConfigModule;
namespace KQuickConfigModuleLoader
{
/**
* Loads a QML KCM from the given plugin metadata.
* @param engine The QQmlEngine to use, if not set, an internal engine will be created. If your application has an exisiting engine, this must be passed in.
*/
KCMUTILSQUICK_EXPORT KPluginFactory::Result<KQuickConfigModule>
loadModule(const KPluginMetaData &metaData, QObject *parent = nullptr, const QVariantList &args = {}, const std::shared_ptr<QQmlEngine> &engine = {});
}
#endif
@@ -0,0 +1,140 @@
/*
SPDX-FileCopyrightText: 2019 Kevin Ottens <kevin.ottens@enioka.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kquickmanagedconfigmodule.h"
#include <KCoreConfigSkeleton>
#include <QPointer>
#include <QTimer>
class KQuickManagedConfigModulePrivate
{
public:
KQuickManagedConfigModulePrivate(KQuickManagedConfigModule *mod)
{
QTimer::singleShot(0, mod, [mod]() {
const auto skeletons = mod->findChildren<KCoreConfigSkeleton *>();
for (auto *skeleton : skeletons) {
mod->registerSettings(skeleton);
}
});
}
QList<QPointer<KCoreConfigSkeleton>> _skeletons;
};
KQuickManagedConfigModule::KQuickManagedConfigModule(QObject *parent, const KPluginMetaData &metaData)
: KQuickConfigModule(parent, metaData)
, d(new KQuickManagedConfigModulePrivate(this))
{
}
KQuickManagedConfigModule::~KQuickManagedConfigModule() = default;
void KQuickManagedConfigModule::load()
{
for (const auto &skeleton : std::as_const(d->_skeletons)) {
if (skeleton) {
skeleton->load();
}
}
}
void KQuickManagedConfigModule::save()
{
for (const auto &skeleton : std::as_const(d->_skeletons)) {
if (skeleton) {
skeleton->save();
}
}
}
void KQuickManagedConfigModule::defaults()
{
for (const auto &skeleton : std::as_const(d->_skeletons)) {
if (skeleton) {
skeleton->setDefaults();
}
}
}
bool KQuickManagedConfigModule::isSaveNeeded() const
{
return false;
}
bool KQuickManagedConfigModule::isDefaults() const
{
return true;
}
void KQuickManagedConfigModule::settingsChanged()
{
bool needsSave = false;
bool representsDefaults = true;
for (const auto &skeleton : std::as_const(d->_skeletons)) {
if (skeleton) {
needsSave |= skeleton->isSaveNeeded();
representsDefaults &= skeleton->isDefaults();
}
}
if (!needsSave) {
needsSave = isSaveNeeded();
}
if (representsDefaults) {
representsDefaults = isDefaults();
}
setRepresentsDefaults(representsDefaults);
setNeedsSave(needsSave);
}
void KQuickManagedConfigModule::registerSettings(KCoreConfigSkeleton *skeleton)
{
if (!skeleton || d->_skeletons.contains(skeleton)) {
return;
}
d->_skeletons.append(skeleton);
auto settingsChangedSlotIndex = metaObject()->indexOfMethod("settingsChanged()");
auto settingsChangedSlot = metaObject()->method(settingsChangedSlotIndex);
QObject::connect(skeleton, &KCoreConfigSkeleton::configChanged, this, &KQuickManagedConfigModule::settingsChanged);
const auto items = skeleton->items();
for (auto item : items) {
const auto itemHasSignals = dynamic_cast<KConfigCompilerSignallingItem *>(item) || dynamic_cast<KPropertySkeletonItem *>(item);
if (!itemHasSignals) {
continue;
}
auto name = item->name();
if (name.at(0).isUpper()) {
name[0] = name[0].toLower();
}
const auto metaObject = skeleton->metaObject();
const auto propertyIndex = metaObject->indexOfProperty(name.toUtf8().constData());
const auto property = metaObject->property(propertyIndex);
if (!property.hasNotifySignal()) {
continue;
}
const auto changedSignal = property.notifySignal();
QObject::connect(skeleton, changedSignal, this, settingsChangedSlot);
}
auto toRemove = std::remove_if(d->_skeletons.begin(), d->_skeletons.end(), [](const QPointer<KCoreConfigSkeleton> &value) {
return value.isNull();
});
d->_skeletons.erase(toRemove, d->_skeletons.end());
QMetaObject::invokeMethod(this, "settingsChanged", Qt::QueuedConnection);
}
#include "moc_kquickmanagedconfigmodule.cpp"
@@ -0,0 +1,137 @@
/*
SPDX-FileCopyrightText: 2019 Kevin Ottens <kevin.ottens@enioka.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef MANAGEDCONFIGMODULE_H
#define MANAGEDCONFIGMODULE_H
#include "kquickconfigmodule.h"
#include <memory>
class KCoreConfigSkeleton;
class KQuickManagedConfigModulePrivate;
/**
* @class KQuickManagedConfigModule managedconfigmodule.h KQuickAddons/ManagedConfigModule
*
* The base class for configuration modules using KConfigXT settings.
*
* We are assuming here that SettingsObject is a class generated from a kcfg file
* and that it will be somehow exposed as a constant property to be used from the QML side.
* It will be automatically discovered by ManagedConfigModule which will update
* the saveNeeded and defaults inherited properties by itself. Thus by inheriting from
* this class you shall not try to manage those properties yourselves.
* By passing in "this" as a parent, we prevent memory leaks and allow KQuickManagedConfigModule to
* automatically find the created settings object.
*
* The constructor of the ConfigModule then looks like this:
* \code
* YourConfigModule::YourConfigModule(QObject *parent, const KPluginMetaData &metaData)
* : ManagedConfigModule(parent, metaData)
* , m_settingsObject(new SettingsObject(this))
* {
* }
* \endcode
*
* @since 6.0
*/
class KCMUTILSQUICK_EXPORT KQuickManagedConfigModule : public KQuickConfigModule
{
Q_OBJECT
public:
/**
* Destroys the module.
*/
~KQuickManagedConfigModule() override;
public Q_SLOTS:
/**
* Load the configuration data into the module.
*
* This method is invoked whenever the module should read its configuration
* (most of the times from a config file) and update the user interface.
* This happens when the user clicks the "Reset" button in the control
* center, to undo all of his changes and restore the currently valid
* settings. It is also called right after construction.
*
* By default this will load the settings from the child setting objects
* of this module.
*/
void load() override;
/**
* Save the configuration data.
*
* The save method stores the config information as shown
* in the user interface in the config files.
* It is called when the user clicks "Apply" or "Ok".
*
* By default this will save the child setting objects
* of this module.
*/
void save() override;
/**
* Sets the configuration to sensible default values.
*
* This method is called when the user clicks the "Default"
* button. It should set the display to useful values.
*
* By default this will reset to defaults the child setting objects
* of this module.
*/
void defaults() override;
protected Q_SLOTS:
/**
* Forces the module to reevaluate the saveNeeded and
* representsDefault state.
*
* This is required for some modules which might have
* some settings managed outside of KConfigXT objects.
*/
void settingsChanged();
/**
* Allow to register manually settings class generated from a kcfg file.
* Used by derived class when automatic discovery is not possible.
* After skeleton is registered it will automatically call settingsChanged().
*/
void registerSettings(KCoreConfigSkeleton *skeleton);
protected:
/**
* Base class for all KControlModules.
* Use KQuickConfigModuleLoader to instantiate this class
*
* @note do not emit changed signals here, since they are not yet connected to any slot.
*/
explicit KQuickManagedConfigModule(QObject *parent, const KPluginMetaData &metaData);
private:
/**
* Allows to indicate if the module requires saving.
*
* By default this returns false, it needs to be overridden only
* if the module has state outside of the settings declared in
* the KConfigXT classes it uses.
*/
virtual bool isSaveNeeded() const;
/**
* Allows to indicate if the module state is representing its defaults.
*
* By default this returns true, it needs to be overridden only
* if the module has state outside of the settings declared in
* the KConfigXT classes it uses.
*/
virtual bool isDefaults() const;
const std::unique_ptr<KQuickManagedConfigModulePrivate> d;
friend class KQuickManagedConfigModulePrivate;
};
#endif // MANAGEDCONFIGMODULE_H
@@ -0,0 +1,286 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "sharedqmlengine_p.h"
#include <KLocalizedQmlContext>
#include <QDebug>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQmlIncubator>
#include <QQmlNetworkAccessManagerFactory>
#include <QQuickItem>
#include <QResource>
#include <QTimer>
#include "kcmutils_debug.h"
using namespace Qt::StringLiterals;
class SharedQmlEnginePrivate
{
public:
SharedQmlEnginePrivate(const std::shared_ptr<QQmlEngine> &engine, SharedQmlEngine *parent)
: q(parent)
, component(nullptr)
, delay(false)
, m_engine(engine)
{
executionEndTimer.setInterval(0);
executionEndTimer.setSingleShot(true);
QObject::connect(&executionEndTimer, &QTimer::timeout, q, [this]() {
scheduleExecutionEnd();
});
}
~SharedQmlEnginePrivate()
{
delete incubator.object();
}
void errorPrint(QQmlComponent *component, QQmlIncubator *incubator = nullptr);
void execute(const QUrl &source);
void scheduleExecutionEnd();
void minimumWidthChanged();
void minimumHeightChanged();
void maximumWidthChanged();
void maximumHeightChanged();
void preferredWidthChanged();
void preferredHeightChanged();
void checkInitializationCompleted();
SharedQmlEngine *q;
QUrl source;
QQmlIncubator incubator;
QQmlComponent *component;
QTimer executionEndTimer;
KLocalizedQmlContext *context{nullptr};
QQmlContext *rootContext;
bool delay;
std::shared_ptr<QQmlEngine> m_engine;
};
void SharedQmlEnginePrivate::errorPrint(QQmlComponent *component, QQmlIncubator *incubator)
{
QList<QQmlError> errors;
if (component && component->isError()) {
errors = component->errors();
} else if (incubator && incubator->isError()) {
errors = incubator->errors();
} else {
return;
}
qCWarning(KCMUTILS_LOG).noquote() << "Error loading QML file" << component->url().toString();
for (const auto &error : errors) {
constexpr const QLatin1String indent(" ");
qCWarning(KCMUTILS_LOG).noquote().nospace() << indent << error;
}
}
void SharedQmlEnginePrivate::execute(const QUrl &source)
{
Q_ASSERT(!source.isEmpty());
delete component;
component = new QQmlComponent(m_engine.get(), q);
delete incubator.object();
m_engine->addImportPath(QStringLiteral("qrc:/"));
component->loadUrl(source);
if (delay) {
executionEndTimer.start(0);
} else {
scheduleExecutionEnd();
}
}
void SharedQmlEnginePrivate::scheduleExecutionEnd()
{
if (component->isReady() || component->isError()) {
q->completeInitialization();
} else {
QObject::connect(component, &QQmlComponent::statusChanged, q, [this]() {
q->completeInitialization();
});
}
}
SharedQmlEngine::SharedQmlEngine(const std::shared_ptr<QQmlEngine> &engine, QObject *parent)
: QObject(parent)
, d(new SharedQmlEnginePrivate(engine, this))
{
d->rootContext = new QQmlContext(engine.get());
d->rootContext->setParent(this); // Delete the context when deleting the shared engine
d->context = new KLocalizedQmlContext(d->rootContext);
d->rootContext->setContextObject(d->context);
}
SharedQmlEngine::~SharedQmlEngine() = default;
void SharedQmlEngine::setTranslationDomain(const QString &translationDomain)
{
d->context->setTranslationDomain(translationDomain);
}
QString SharedQmlEngine::translationDomain() const
{
return d->context->translationDomain();
}
void SharedQmlEngine::setSource(const QUrl &source)
{
d->source = source;
d->execute(source);
}
QUrl SharedQmlEngine::source() const
{
return d->source;
}
void SharedQmlEngine::setInitializationDelayed(const bool delay)
{
d->delay = delay;
}
bool SharedQmlEngine::isInitializationDelayed() const
{
return d->delay;
}
std::shared_ptr<QQmlEngine> SharedQmlEngine::engine()
{
return d->m_engine;
}
QObject *SharedQmlEngine::rootObject() const
{
if (d->incubator.isLoading()) {
qCWarning(KCMUTILS_LOG) << "Trying to use rootObject before initialization is completed, whilst using setInitializationDelayed. Forcing completion";
d->incubator.forceCompletion();
}
return d->incubator.object();
}
QQmlComponent *SharedQmlEngine::mainComponent() const
{
return d->component;
}
QQmlContext *SharedQmlEngine::rootContext() const
{
return d->rootContext;
}
bool SharedQmlEngine::isError() const
{
return !d->m_engine || !d->component || d->component->isError() || d->incubator.isError();
}
static QString qmlErrorsToString(const QList<QQmlError> &errors)
{
QString ret;
for (const auto &e : errors) {
ret += e.url().toString() + QLatin1Char(':') + QString::number(e.line()) + QLatin1Char(' ') + e.description() + QLatin1Char('\n');
}
return ret;
}
QString SharedQmlEngine::errorString() const
{
if (d->component && d->component->isError()) {
return d->component->errorString();
} else if (d->incubator.isError()) {
return qmlErrorsToString(d->incubator.errors());
} else {
return {};
}
}
void SharedQmlEnginePrivate::checkInitializationCompleted()
{
if (!incubator.isReady() && !incubator.isError()) {
QTimer::singleShot(0, q, [this]() {
checkInitializationCompleted();
});
return;
}
if (!incubator.object()) {
errorPrint(component, &incubator);
}
Q_EMIT q->finished();
}
void SharedQmlEngine::completeInitialization(const QVariantMap &initialProperties)
{
d->executionEndTimer.stop();
if (d->incubator.object()) {
return;
}
if (!d->component) {
qCWarning(KCMUTILS_LOG) << "No component for" << source();
return;
}
if (!d->component->isReady()) {
d->errorPrint(d->component);
return;
}
d->incubator.setInitialProperties(initialProperties);
d->component->create(d->incubator, d->rootContext);
if (d->delay) {
d->checkInitializationCompleted();
} else {
d->incubator.forceCompletion();
if (!d->incubator.object()) {
d->errorPrint(d->component, &d->incubator);
}
Q_EMIT finished();
}
}
QObject *SharedQmlEngine::createObjectFromSource(const QUrl &source, QQmlContext *context, const QVariantMap &initialProperties)
{
QQmlComponent *component = new QQmlComponent(d->m_engine.get(), this);
component->loadUrl(source);
return createObjectFromComponent(component, context, initialProperties);
}
QObject *SharedQmlEngine::createObjectFromComponent(QQmlComponent *component, QQmlContext *context, const QVariantMap &initialProperties)
{
QObject *object = component->createWithInitialProperties(initialProperties, context ? context : d->rootContext);
if (!component->isError() && object) {
// memory management
const auto root = rootObject();
object->setParent(root);
component->setParent(object);
// visually reparent to root object if wasn't specified otherwise by initialProperties
if (!initialProperties.contains(QLatin1String("parent")) && root && root->isQuickItemType()) {
object->setProperty("parent", QVariant::fromValue(root));
}
return object;
} else {
d->errorPrint(component);
delete object;
return nullptr;
}
}
#include "moc_sharedqmlengine_p.cpp"
@@ -0,0 +1,182 @@
/*
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText:
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef PLASMA_SHAREDQMLENGINE_H
#define PLASMA_SHAREDQMLENGINE_H
#include "ki18n_version.h"
#include <QObject>
#include <QQmlComponent>
#include <QQmlContext>
#include <memory>
class QQmlComponent;
class QQmlEngine;
class KLocalizedQmlContext;
class SharedQmlEnginePrivate;
/**
* @class Plasma::SharedQmlEngine Plasma/sharedqmlengine.h Plasma/SharedQmlEngine
*
* @short An object that instantiates an entire QML context, with its own declarative engine
*
* Plasma::SharedQmlEngine provides a class to conveniently use QML based
* declarative user interfaces.
* A SharedQmlEngine corresponds to one QML file (which can include others).
* It will a shared QQmlEngine with a single root object, described in the QML file.
*/
class SharedQmlEngine : public QObject
{
Q_OBJECT
Q_PROPERTY(QUrl source READ source WRITE setSource)
Q_PROPERTY(QString translationDomain READ translationDomain WRITE setTranslationDomain)
Q_PROPERTY(bool initializationDelayed READ isInitializationDelayed WRITE setInitializationDelayed)
Q_PROPERTY(QObject *rootObject READ rootObject)
public:
/**
* Construct a new Plasma::SharedQmlEngine
*
* @param parent The QObject parent for this object.
*/
explicit SharedQmlEngine(const std::shared_ptr<QQmlEngine> &engine, QObject *parent = nullptr);
~SharedQmlEngine() override;
/**
* Call this method before calling setupBindings to install a translation domain for all
* i18n global functions. If a translation domain is set all i18n calls delegate to the
* matching i18nd calls with the provided translation domain.
*
* The translationDomain affects all i18n calls including those from imports. Because of
* that modules intended to be used as imports should prefer the i18nd variants and set
* the translation domain explicitly in each call.
*
* This method is only required if your declarative usage is inside a library. If it's
* in an application there is no need to set the translation domain as the application's
* domain can be used.
*
* @param translationDomain The translation domain to be used for i18n calls.
*/
void setTranslationDomain(const QString &translationDomain);
/**
* @return the translation domain for the i18n calls done in this QML engine
*/
QString translationDomain() const;
/**
* Sets the path of the QML file to parse and execute
*
* @param path the absolute path of a QML file
*/
void setSource(const QUrl &source);
/**
* @return the absolute path of the current QML file
*/
QUrl source() const;
/**
* Sets whether the execution of the QML file has to be delayed later in the event loop. It has to be called before setQmlPath().
* In this case it will be possible to assign new objects in the main engine context
* before the main component gets initialized.
* In that case it will be possible to access it immediately from the QML code.
* The initialization will either be completed automatically asynchronously
* or explicitly by calling completeInitialization()
*
* @param delay if true the initialization of the QML file will be delayed
* at the end of the event loop
*/
void setInitializationDelayed(const bool delay);
/**
* @return true if the initialization of the QML file will be delayed
* at the end of the event loop
*/
bool isInitializationDelayed() const;
/**
* @return the declarative engine that runs the qml file assigned to this widget.
*/
std::shared_ptr<QQmlEngine> engine();
/**
* @return the root object of the declarative object tree
*/
QObject *rootObject() const;
/**
* @return the main QQmlComponent of the engine
*/
QQmlComponent *mainComponent() const;
/**
* The component's creation context.
*/
QQmlContext *rootContext() const;
/*
* The component's or incubator's error status.
*/
bool isError() const;
/*
* The component's or incubator's error string.
*/
QString errorString() const;
/**
* Creates and returns an object based on the provided url to a Qml file
* with the same QQmlEngine and the same root context as the main object,
* that will be the parent of the newly created object
* @param source url where the QML file is located
* @param context The QQmlContext in which we will create the object,
* if 0 it will use the engine's root context
* @param initialProperties optional properties that will be set on
* the object when created (and before Component.onCompleted
* gets emitted
*/
QObject *createObjectFromSource(const QUrl &source, QQmlContext *context = nullptr, const QVariantMap &initialProperties = QVariantMap());
/**
* Creates and returns an object based on the provided QQmlComponent
* with the same QQmlEngine and the same root context as the admin object,
* that will be the parent of the newly created object
* @param component the component we want to instantiate
* @param context The QQmlContext in which we will create the object,
* if 0 it will use the engine's root context
* @param initialProperties optional properties that will be set on
* the object when created (and before Component.onCompleted
* gets emitted
*/
QObject *createObjectFromComponent(QQmlComponent *component, QQmlContext *context = nullptr, const QVariantMap &initialProperties = QVariantMap());
public Q_SLOTS:
/**
* Finishes the process of initialization.
* If isInitializationDelayed() is false, calling this will have no effect.
* @param initialProperties optional properties that will be set on
* the object when created (and before Component.onCompleted
* gets emitted
*/
void completeInitialization(const QVariantMap &initialProperties = QVariantMap());
Q_SIGNALS:
/**
* Emitted when the parsing and execution of the QML file is terminated
*/
void finished();
private:
const std::unique_ptr<SharedQmlEnginePrivate> d;
};
#endif // multiple inclusion guard