diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 03797512b..679d1bc6d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -542,6 +542,8 @@ set(SOURCE_FILES widgets/settingspages/NicknamesPage.hpp widgets/settingspages/NotificationPage.cpp widgets/settingspages/NotificationPage.hpp + widgets/settingspages/PluginsPage.cpp + widgets/settingspages/PluginsPage.hpp widgets/settingspages/SettingsPage.cpp widgets/settingspages/SettingsPage.hpp diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index b504536af..c3d05dfc0 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -11,6 +11,8 @@ #include "widgets/splits/Split.hpp" #include "widgets/Window.hpp" +#include + #include #include @@ -43,14 +45,32 @@ void PluginController::initialize(Settings &settings, Paths &paths) << "Missing index.lua in plugin directory" << pluginDir; continue; } - qCDebug(chatterinoLua) << "found index.lua, running it!"; + qCDebug(chatterinoLua) + << "found index.lua, now looking for info.json!"; + auto info = QFileInfo(pluginDir.filePath("info.json")); + if (!info.exists()) + { + qCDebug(chatterinoLua) + << "Missing info.json in plugin directory" << pluginDir; + continue; + } + QFile infoFile(info.absoluteFilePath()); + infoFile.open(QIODevice::ReadOnly); + auto everything = infoFile.readAll(); + auto doc = QJsonDocument::fromJson(everything); + if (!doc.isObject()) + { + qCDebug(chatterinoLua) + << "info.json root is not an object" << pluginDir; + continue; + } - this->load(index, pluginDir); + this->load(index, pluginDir, PluginMeta(doc.object())); } } } -void PluginController::load(QFileInfo index, QDir pluginDir) +void PluginController::load(QFileInfo index, QDir pluginDir, PluginMeta meta) { qCDebug(chatterinoLua) << "Running lua file" << index; lua_State *l = luaL_newstate(); @@ -58,8 +78,8 @@ void PluginController::load(QFileInfo index, QDir pluginDir) this->loadChatterinoLib(l); auto pluginName = pluginDir.dirName(); - auto plugin = std::make_unique(pluginName, l); - this->plugins.insert({pluginName, std::move(plugin)}); + auto plugin = std::make_unique(pluginName, l, meta); + this->plugins_.insert({pluginName, std::move(plugin)}); luaL_dofile(l, index.absoluteFilePath().toStdString().c_str()); qCInfo(chatterinoLua) << "Loaded" << pluginName << "plugin from" << index; @@ -67,7 +87,7 @@ void PluginController::load(QFileInfo index, QDir pluginDir) void PluginController::callEvery(const QString &functionName) { - for (const auto &[name, plugin] : this->plugins) + for (const auto &[name, plugin] : this->plugins_) { lua_getglobal(plugin->state_, functionName.toStdString().c_str()); lua_pcall(plugin->state_, 0, 0, 0); @@ -78,7 +98,7 @@ void PluginController::callEveryWithArgs( const QString &functionName, int count, std::function &pl, lua_State *L)> argCb) { - for (const auto &[name, plugin] : this->plugins) + for (const auto &[name, plugin] : this->plugins_) { lua_getglobal(plugin->state_, functionName.toStdString().c_str()); argCb(plugin, plugin->state_); @@ -89,7 +109,7 @@ void PluginController::callEveryWithArgs( QString PluginController::tryExecPluginCommand(const QString &commandName, const CommandContext &ctx) { - for (auto &[name, plugin] : this->plugins) + for (auto &[name, plugin] : this->plugins_) { if (auto it = plugin->ownedCommands.find(commandName); it != plugin->ownedCommands.end()) diff --git a/src/controllers/plugins/PluginController.hpp b/src/controllers/plugins/PluginController.hpp index 679fb0347..6aea67234 100644 --- a/src/controllers/plugins/PluginController.hpp +++ b/src/controllers/plugins/PluginController.hpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include @@ -20,12 +22,34 @@ struct lua_State; namespace chatterino { +struct PluginMeta { + QString name; + QString description; + QString authors; + QString homepage; + std::vector tags; + + explicit PluginMeta(const QJsonObject &obj) + : name(obj.value("name").toString()) + , description(obj.value("description").toString()) + , authors(obj.value("authors").toString()) + , homepage(obj.value("homepage").toString()) + { + for (const auto &t : obj.value("tags").toArray()) + { + tags.push_back(t.toString()); + } + } +}; + class Plugin { public: - QString name; - Plugin(QString name, lua_State *state) - : name(std::move(name)) + QString codename; + PluginMeta meta; + Plugin(QString codename, lua_State *state, PluginMeta meta) + : codename(std::move(codename)) + , meta(std::move(meta)) , state_(state) { } @@ -73,7 +97,7 @@ public: // This is required to be public because of c functions Plugin *getPluginByStatePtr(lua_State *L) { - for (auto &[name, plugin] : this->plugins) + for (auto &[name, plugin] : this->plugins_) { if (plugin->state_ == L) { @@ -83,10 +107,15 @@ public: return nullptr; } + const std::map> &plugins() const + { + return this->plugins_; + } + private: - void load(QFileInfo index, QDir pluginDir); + void load(QFileInfo index, QDir pluginDir, PluginMeta meta); void loadChatterinoLib(lua_State *l); - std::map> plugins; + std::map> plugins_; }; }; // namespace chatterino diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index d30365540..cd3a52107 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -20,6 +20,7 @@ #include "widgets/settingspages/ModerationPage.hpp" #include "widgets/settingspages/NicknamesPage.hpp" #include "widgets/settingspages/NotificationPage.hpp" +#include "widgets/settingspages/PluginsPage.hpp" #include #include @@ -215,6 +216,7 @@ void SettingsDialog::addTabs() this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg", SettingsTabId::Moderation); this->addTab([]{return new NotificationPage;}, "Live Notifications", ":/settings/notification2.svg"); this->addTab([]{return new ExternalToolsPage;}, "External tools", ":/settings/externaltools.svg"); + this->addTab([]{return new PluginsPage;}, "Plugins", ":/settings/externaltools.svg"); this->ui_.tabContainer->addStretch(1); this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", SettingsTabId(), Qt::AlignBottom); // clang-format on diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp new file mode 100644 index 000000000..a6ab81afc --- /dev/null +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -0,0 +1,52 @@ +#include "PluginsPage.hpp" + +#include "Application.hpp" +#include "controllers/plugins/PluginController.hpp" +#include "singletons/Settings.hpp" +#include "util/Helpers.hpp" +#include "util/LayoutCreator.hpp" +#include "util/RemoveScrollAreaBackground.hpp" + +#include +#include +#include + +namespace chatterino { + +PluginsPage::PluginsPage() +{ + LayoutCreator layoutCreator(this); + + auto scroll = layoutCreator.emplace(); + auto widget = scroll.emplaceScrollAreaWidget(); + removeScrollAreaBackground(scroll.getElement(), widget.getElement()); + + auto layout = widget.setLayoutType(); + auto group = layout.emplace("Plugins"); + auto groupLayout = group.setLayoutType(); + + auto *description = + new QLabel("You can load plugins by putting them into " + "/Plugins/. Each one is a " + "new directory."); + description->setWordWrap(true); + description->setStyleSheet("color: #bbb"); + groupLayout->addRow(description); + + for (const auto &[codename, plugin] : getApp()->plugins->plugins()) + { + auto plgroup = groupLayout.emplace(plugin->meta.name); + auto pl = plgroup.setLayoutType(); + auto *descrText = new QLabel(plugin->meta.description); + descrText->setTextFormat(Qt::TextFormat::MarkdownText); + descrText->setWordWrap(true); + descrText->setStyleSheet("color: #bbb"); + pl->addRow(descrText); + pl->addRow("Authors", new QLabel(plugin->meta.authors)); + auto *homepage = new QLabel(formatRichLink(plugin->meta.homepage)); + homepage->setOpenExternalLinks(true); + + pl->addRow("Homepage", homepage); + } +} +} // namespace chatterino diff --git a/src/widgets/settingspages/PluginsPage.hpp b/src/widgets/settingspages/PluginsPage.hpp new file mode 100644 index 000000000..ce6296ab1 --- /dev/null +++ b/src/widgets/settingspages/PluginsPage.hpp @@ -0,0 +1,9 @@ +#pragma once +#include "widgets/settingspages/SettingsPage.hpp" +namespace chatterino { +class PluginsPage : public SettingsPage +{ +public: + PluginsPage(); +}; +} // namespace chatterino