fix: propagate per monitor overlay signals to main

This commit is contained in:
2 * r + 2 * t 2026-04-12 03:01:58 +10:00
parent ef00097a51
commit ee9dce0268
8 changed files with 43 additions and 26 deletions

View file

@ -10,20 +10,20 @@ Scope {
Toaster.toast(qsTr("Config loaded"), qsTr("Config loaded successfully!"), "rule_settings"); Toaster.toast(qsTr("Config loaded"), qsTr("Config loaded successfully!"), "rule_settings");
} }
function onLoadFailed(error: string): void { function onLoadFailed(error: string, screen: string): void {
Toaster.toast(qsTr("Failed to parse config"), error, "settings_alert", Toast.Warning); Toaster.toast(qsTr("Failed to parse config%1").arg(screen ? " for " + screen : ""), error, "settings_alert", Toast.Warning);
} }
function onSaveFailed(error: string): void { function onSaveFailed(error: string, screen: string): void {
Toaster.toast(qsTr("Failed to save config"), error, "settings_alert", Toast.Error); Toaster.toast(qsTr("Failed to save config%1").arg(screen ? " for " + screen : ""), error, "settings_alert", Toast.Error);
} }
target: GlobalConfig target: GlobalConfig
} }
Connections { Connections {
function onLoadFailed(error: string): void { function onLoadFailed(error: string, screen: string): void {
Toaster.toast(qsTr("Failed to parse token config"), error, "settings_alert", Toast.Warning); Toaster.toast(qsTr("Failed to parse token config%1").arg(screen ? "for " + screen : ""), error, "settings_alert", Toast.Warning);
} }
target: TokenConfig target: TokenConfig

View file

@ -37,7 +37,7 @@ GlobalConfig::GlobalConfig(QObject* parent)
setupFileBackend(configDir() + QStringLiteral("shell.json")); setupFileBackend(configDir() + QStringLiteral("shell.json"));
} }
GlobalConfig::GlobalConfig(GlobalConfig* fallback, const QString& filePath, QObject* parent) GlobalConfig::GlobalConfig(GlobalConfig* fallback, const QString& filePath, const QString& screen, QObject* parent)
: RootConfig(parent) : RootConfig(parent)
, m_appearance(new AppearanceConfig(this)) , m_appearance(new AppearanceConfig(this))
, m_general(new GeneralConfig(this)) , m_general(new GeneralConfig(this))
@ -57,7 +57,7 @@ GlobalConfig::GlobalConfig(GlobalConfig* fallback, const QString& filePath, QObj
, m_services(new ServiceConfig(this)) , m_services(new ServiceConfig(this))
, m_paths(new UserPaths(this)) { , m_paths(new UserPaths(this)) {
if (!filePath.isEmpty()) if (!filePath.isEmpty())
setupFileBackend(filePath); setupFileBackend(filePath, screen);
if (fallback) if (fallback)
syncFromGlobal(fallback); syncFromGlobal(fallback);
@ -73,7 +73,7 @@ GlobalConfig* GlobalConfig::instance() {
GlobalConfig* GlobalConfig::defaults() { GlobalConfig* GlobalConfig::defaults() {
if (!m_defaults) if (!m_defaults)
m_defaults = new GlobalConfig(nullptr, QString(), this); // Non-singleton constructor m_defaults = new GlobalConfig(nullptr, QString(), QString(), this);
return m_defaults; return m_defaults;
} }

View file

@ -56,7 +56,8 @@ public:
private: private:
friend class MonitorConfigManager; friend class MonitorConfigManager;
explicit GlobalConfig(QObject* parent = nullptr); explicit GlobalConfig(QObject* parent = nullptr);
explicit GlobalConfig(GlobalConfig* fallback, const QString& filePath, QObject* parent = nullptr); explicit GlobalConfig(
GlobalConfig* fallback, const QString& filePath, const QString& screen = {}, QObject* parent = nullptr);
GlobalConfig* m_defaults = nullptr; GlobalConfig* m_defaults = nullptr;
bool m_tokensBound = false; bool m_tokensBound = false;

View file

@ -32,7 +32,13 @@ GlobalConfig* MonitorConfigManager::configForScreen(const QString& screen) {
auto& overlay = m_overlays[screen]; auto& overlay = m_overlays[screen];
if (!overlay.config) { if (!overlay.config) {
auto dir = monitorConfigDir(screen); auto dir = monitorConfigDir(screen);
overlay.config = new GlobalConfig(GlobalConfig::instance(), dir + QStringLiteral("shell.json"), this); overlay.config = new GlobalConfig(GlobalConfig::instance(), dir + QStringLiteral("shell.json"), screen, this);
auto* const global = GlobalConfig::instance();
connect(overlay.config, &GlobalConfig::loaded, global, &GlobalConfig::loaded);
connect(overlay.config, &GlobalConfig::saved, global, &GlobalConfig::saved);
connect(overlay.config, &GlobalConfig::loadFailed, global, &GlobalConfig::loadFailed);
connect(overlay.config, &GlobalConfig::saveFailed, global, &GlobalConfig::saveFailed);
} }
return overlay.config; return overlay.config;
} }
@ -41,7 +47,14 @@ TokenConfig* MonitorConfigManager::tokensForScreen(const QString& screen) {
auto& overlay = m_overlays[screen]; auto& overlay = m_overlays[screen];
if (!overlay.tokens) { if (!overlay.tokens) {
auto dir = monitorConfigDir(screen); auto dir = monitorConfigDir(screen);
overlay.tokens = new TokenConfig(TokenConfig::instance(), dir + QStringLiteral("shell-tokens.json"), this); overlay.tokens =
new TokenConfig(TokenConfig::instance(), dir + QStringLiteral("shell-tokens.json"), screen, this);
auto* const global = TokenConfig::instance();
connect(overlay.tokens, &TokenConfig::loaded, global, &TokenConfig::loaded);
connect(overlay.tokens, &TokenConfig::saved, global, &TokenConfig::saved);
connect(overlay.tokens, &TokenConfig::loadFailed, global, &TokenConfig::loadFailed);
connect(overlay.tokens, &TokenConfig::saveFailed, global, &TokenConfig::saveFailed);
} }
return overlay.tokens; return overlay.tokens;
} }

View file

@ -19,8 +19,9 @@ QString watchRoot() {
RootConfig::RootConfig(QObject* parent) RootConfig::RootConfig(QObject* parent)
: ConfigObject(parent) {} : ConfigObject(parent) {}
void RootConfig::setupFileBackend(const QString& path) { void RootConfig::setupFileBackend(const QString& path, const QString& screen) {
m_filePath = path; m_filePath = path;
m_screen = screen;
m_watcher = new QFileSystemWatcher(this); m_watcher = new QFileSystemWatcher(this);
m_saveTimer = new QTimer(this); m_saveTimer = new QTimer(this);
@ -40,7 +41,7 @@ void RootConfig::setupFileBackend(const QString& path) {
if (!file.open(QIODevice::WriteOnly)) { if (!file.open(QIODevice::WriteOnly)) {
auto err = QStringLiteral("Failed to write %1: %2").arg(m_filePath, file.errorString()); auto err = QStringLiteral("Failed to write %1: %2").arg(m_filePath, file.errorString());
qCWarning(lcConfig, "%s", qUtf8Printable(err)); qCWarning(lcConfig, "%s", qUtf8Printable(err));
emit saveFailed(err); emit saveFailed(err, m_screen);
return; return;
} }
@ -51,7 +52,7 @@ void RootConfig::setupFileBackend(const QString& path) {
// Update watches — save may have created directories // Update watches — save may have created directories
updateWatch(); updateWatch();
emit saved(); emit saved(m_screen);
}); });
m_cooldownTimer->setSingleShot(true); m_cooldownTimer->setSingleShot(true);
@ -169,9 +170,9 @@ void RootConfig::reload() {
auto result = reloadFromFile(); auto result = reloadFromFile();
if (result.has_value()) { if (result.has_value()) {
if (result->isEmpty()) if (result->isEmpty())
emit loaded(); emit loaded(m_screen);
else else
emit loadFailed(*result); emit loadFailed(*result, m_screen);
} }
} }

View file

@ -16,7 +16,7 @@ class RootConfig : public ConfigObject {
public: public:
explicit RootConfig(QObject* parent = nullptr); explicit RootConfig(QObject* parent = nullptr);
void setupFileBackend(const QString& path); void setupFileBackend(const QString& path, const QString& screen = {});
void saveToFile(); void saveToFile();
// Returns nullopt if retrying, empty string on success, error message on failure. // Returns nullopt if retrying, empty string on success, error message on failure.
[[nodiscard]] std::optional<QString> reloadFromFile(); [[nodiscard]] std::optional<QString> reloadFromFile();
@ -27,16 +27,17 @@ public:
Q_INVOKABLE void reload(); Q_INVOKABLE void reload();
signals: signals:
void loaded(); void loaded(const QString& screen);
void loadFailed(const QString& error); void loadFailed(const QString& error, const QString& screen);
void saved(); void saved(const QString& screen);
void saveFailed(const QString& error); void saveFailed(const QString& error, const QString& screen);
private: private:
void updateWatch(); void updateWatch();
void onWatcherEvent(); void onWatcherEvent();
QString m_filePath; QString m_filePath;
QString m_screen;
QString m_watchedDir; QString m_watchedDir;
bool m_recentlySaved = false; bool m_recentlySaved = false;

View file

@ -22,12 +22,12 @@ TokenConfig::TokenConfig(QObject* parent)
setupFileBackend(configDir() + QStringLiteral("shell-tokens.json")); setupFileBackend(configDir() + QStringLiteral("shell-tokens.json"));
} }
TokenConfig::TokenConfig(TokenConfig* fallback, const QString& filePath, QObject* parent) TokenConfig::TokenConfig(TokenConfig* fallback, const QString& filePath, const QString& screen, QObject* parent)
: RootConfig(parent) : RootConfig(parent)
, m_appearance(new AppearanceTokens(this)) , m_appearance(new AppearanceTokens(this))
, m_sizes(new SizeTokens(this)) { , m_sizes(new SizeTokens(this)) {
if (!filePath.isEmpty()) if (!filePath.isEmpty())
setupFileBackend(filePath); setupFileBackend(filePath, screen);
if (fallback) if (fallback)
syncFromGlobal(fallback); syncFromGlobal(fallback);
} }
@ -39,7 +39,7 @@ TokenConfig* TokenConfig::instance() {
TokenConfig* TokenConfig::defaults() { TokenConfig* TokenConfig::defaults() {
if (!m_defaults) if (!m_defaults)
m_defaults = new TokenConfig(nullptr, QString(), this); // Non-singleton constructor m_defaults = new TokenConfig(nullptr, QString(), QString(), this);
return m_defaults; return m_defaults;
} }

View file

@ -335,7 +335,8 @@ public:
private: private:
friend class MonitorConfigManager; friend class MonitorConfigManager;
explicit TokenConfig(QObject* parent = nullptr); explicit TokenConfig(QObject* parent = nullptr);
explicit TokenConfig(TokenConfig* fallback, const QString& filePath, QObject* parent = nullptr); explicit TokenConfig(
TokenConfig* fallback, const QString& filePath, const QString& screen = {}, QObject* parent = nullptr);
TokenConfig* m_defaults = nullptr; TokenConfig* m_defaults = nullptr;
}; };