From 305149a7957571e097af5e3c92f4ab1605ec050a Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Tue, 30 Jun 2026 13:50:01 +0200 Subject: [PATCH] refactor(plugins): Use signals to update UI (#7030) The plugin UI manually updated itself whenever the user changed the state by clicking a button (e.g. enabling a plugin). This gets more complicated when other parts of Chatterino can also load/toggle plugins. To help with this, I added a signal for the current list of plugins. The UI also reacts to changes to the enabled plugins (a setting). Reviewed-by: pajlada --- src/controllers/plugins/PluginController.cpp | 18 ++++++++++++++++++ src/controllers/plugins/PluginController.hpp | 6 ++++++ src/widgets/settingspages/PluginsPage.cpp | 19 ++++++++++++------- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 579316ce9..f01bc2b10 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -312,6 +312,7 @@ void PluginController::load(const QFileInfo &index, const QDir &pluginDir, auto plugin = std::make_unique(pluginName, l, meta, pluginDir); auto *temp = plugin.get(); this->plugins_.insert({pluginName, std::move(plugin)}); + this->queueChangeNotification(); if (getApp()->getArgs().safeMode) { @@ -361,6 +362,7 @@ bool PluginController::reload(const QString &id) QDir loadDir = it->second->loadDirectory_; // Since Plugin owns the state, it will clean up everything related to it this->plugins_.erase(id); + this->queueChangeNotification(); this->tryLoadFromDir(loadDir); return true; } @@ -488,5 +490,21 @@ WebSocketPool &PluginController::webSocketPool() return this->webSocketPool_; } +void PluginController::queueChangeNotification() +{ + if (this->changeNotificationQueued) + { + return; + } + this->changeNotificationQueued = true; + QMetaObject::invokeMethod( + qApp, + [this] { + this->changeNotificationQueued = false; + this->onPluginsUpdated.invoke(); + }, + Qt::QueuedConnection); +} + } // namespace chatterino #endif diff --git a/src/controllers/plugins/PluginController.hpp b/src/controllers/plugins/PluginController.hpp index 8944e1c58..0bb268938 100644 --- a/src/controllers/plugins/PluginController.hpp +++ b/src/controllers/plugins/PluginController.hpp @@ -69,6 +69,7 @@ public: WebSocketPool &webSocketPool(); pajlada::Signals::Signal onPluginLoaded; + pajlada::Signals::NoArgSignal onPluginsUpdated; private: void loadPlugins(); @@ -82,6 +83,9 @@ private: static void loadChatterinoLib(lua_State *l); bool tryLoadFromDir(const QDir &pluginDir); + + void queueChangeNotification(); + std::map> plugins_; WebSocketPool webSocketPool_; @@ -89,6 +93,8 @@ private: std::pair>> loaders_; + bool changeNotificationQueued = false; + // This is for tests, pay no attention friend class PluginControllerAccess; }; diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index 628d2752b..b65bcabde 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -98,7 +98,15 @@ PluginsPage::PluginsPage() } } - this->rebuildContent(); + this->managedConnections_.managedConnect( + getApp()->getPlugins()->onPluginsUpdated, [this] { + this->rebuildContent(); + }); + getSettings()->enabledPlugins.connect( + [this] { + this->rebuildContent(); + }, + this->managedConnections_); } void PluginsPage::rebuildContent() @@ -231,17 +239,14 @@ void PluginsPage::rebuildContent() } getSettings()->enabledPlugins.setValue(val); getApp()->getPlugins()->reload(name); - this->rebuildContent(); }); pluginEntry->addRow(toggleButton); } auto *reloadButton = new QPushButton("Reload", this->dataFrame_); - QObject::connect(reloadButton, &QPushButton::pressed, - [name = id, this]() { - getApp()->getPlugins()->reload(name); - this->rebuildContent(); - }); + QObject::connect(reloadButton, &QPushButton::pressed, [name = id]() { + getApp()->getPlugins()->reload(name); + }); pluginEntry->addRow(reloadButton); if (getApp()->getArgs().safeMode) {