From 5a13cc22f618c65719e5bc53fcc97d0079a285b7 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sun, 12 Apr 2026 03:12:13 +1000 Subject: [PATCH] feat: add unknown option toasts --- modules/ConfigToasts.qml | 8 ++++ .../Caelestia/Config/monitorconfigmanager.cpp | 2 + plugin/src/Caelestia/Config/rootconfig.cpp | 38 ++++++++++++++++++- plugin/src/Caelestia/Config/rootconfig.hpp | 2 + 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/modules/ConfigToasts.qml b/modules/ConfigToasts.qml index bcba7a7d..81463b8d 100644 --- a/modules/ConfigToasts.qml +++ b/modules/ConfigToasts.qml @@ -18,6 +18,10 @@ Scope { Toaster.toast(qsTr("Failed to save config%1").arg(screen ? " for " + screen : ""), error, "settings_alert", Toast.Error); } + function onUnknownOption(key: string, screen: string): void { + Toaster.toast(qsTr("Unknown option in%1 config").arg(screen ? " " + screen : ""), key, "question_mark", Toast.Warning); + } + target: GlobalConfig } @@ -26,6 +30,10 @@ Scope { Toaster.toast(qsTr("Failed to parse token config%1").arg(screen ? "for " + screen : ""), error, "settings_alert", Toast.Warning); } + function onUnknownOption(key: string, screen: string): void { + Toaster.toast(qsTr("Unknown option in%1 token config").arg(screen ? " " + screen : ""), key, "question_mark", Toast.Warning); + } + target: TokenConfig } } diff --git a/plugin/src/Caelestia/Config/monitorconfigmanager.cpp b/plugin/src/Caelestia/Config/monitorconfigmanager.cpp index 3746097f..fb1745ec 100644 --- a/plugin/src/Caelestia/Config/monitorconfigmanager.cpp +++ b/plugin/src/Caelestia/Config/monitorconfigmanager.cpp @@ -39,6 +39,7 @@ GlobalConfig* MonitorConfigManager::configForScreen(const QString& screen) { connect(overlay.config, &GlobalConfig::saved, global, &GlobalConfig::saved); connect(overlay.config, &GlobalConfig::loadFailed, global, &GlobalConfig::loadFailed); connect(overlay.config, &GlobalConfig::saveFailed, global, &GlobalConfig::saveFailed); + connect(overlay.config, &GlobalConfig::unknownOption, global, &GlobalConfig::unknownOption); } return overlay.config; } @@ -55,6 +56,7 @@ TokenConfig* MonitorConfigManager::tokensForScreen(const QString& screen) { connect(overlay.tokens, &TokenConfig::saved, global, &TokenConfig::saved); connect(overlay.tokens, &TokenConfig::loadFailed, global, &TokenConfig::loadFailed); connect(overlay.tokens, &TokenConfig::saveFailed, global, &TokenConfig::saveFailed); + connect(overlay.tokens, &TokenConfig::unknownOption, global, &TokenConfig::unknownOption); } return overlay.tokens; } diff --git a/plugin/src/Caelestia/Config/rootconfig.cpp b/plugin/src/Caelestia/Config/rootconfig.cpp index fd90122a..8a10c950 100644 --- a/plugin/src/Caelestia/Config/rootconfig.cpp +++ b/plugin/src/Caelestia/Config/rootconfig.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include namespace caelestia::config { @@ -19,6 +20,35 @@ QString watchRoot() { RootConfig::RootConfig(QObject* parent) : ConfigObject(parent) {} +QStringList RootConfig::collectUnknownKeys(const ConfigObject* obj, const QJsonObject& json) { + QStringList unknown; + const auto* meta = obj->metaObject(); + + QSet known; + for (int i = meta->propertyOffset(); i < meta->propertyCount(); ++i) + known.insert(QString::fromUtf8(meta->property(i).name())); + + for (auto it = json.begin(); it != json.end(); ++it) { + if (!known.contains(it.key())) { + unknown.append(it.key()); + } else if (it.value().isObject()) { + int idx = meta->indexOfProperty(it.key().toUtf8().constData()); + if (idx >= 0) { + auto prop = meta->property(idx); + auto value = prop.read(obj); + auto* subObj = value.value(); + if (subObj) { + const auto subUnknown = collectUnknownKeys(subObj, it.value().toObject()); + for (const auto& subKey : subUnknown) + unknown.append(it.key() + QStringLiteral(".") + subKey); + } + } + } + } + + return unknown; +} + void RootConfig::setupFileBackend(const QString& path, const QString& screen) { m_filePath = path; m_screen = screen; @@ -157,7 +187,13 @@ std::optional RootConfig::reloadFromFile() { qCDebug(lcConfig) << "Reloading" << metaObject()->className() << "from" << m_filePath; clearLoadedKeys(); - loadFromJson(doc.object()); + + auto jsonObj = doc.object(); + const auto unknownKeys = collectUnknownKeys(this, jsonObj); + for (const auto& key : unknownKeys) + emit unknownOption(key, m_screen); + + loadFromJson(jsonObj); return QString(); // success } diff --git a/plugin/src/Caelestia/Config/rootconfig.hpp b/plugin/src/Caelestia/Config/rootconfig.hpp index a7a0c72b..25b69002 100644 --- a/plugin/src/Caelestia/Config/rootconfig.hpp +++ b/plugin/src/Caelestia/Config/rootconfig.hpp @@ -31,8 +31,10 @@ signals: void loadFailed(const QString& error, const QString& screen); void saved(const QString& screen); void saveFailed(const QString& error, const QString& screen); + void unknownOption(const QString& key, const QString& screen); private: + static QStringList collectUnknownKeys(const ConfigObject* obj, const QJsonObject& json); void updateWatch(); void onWatcherEvent();