Add a Plugins settings page

This commit is contained in:
Mm2PL 2023-01-30 23:22:14 +01:00
parent cc0aa1aa82
commit 03de6869c4
No known key found for this signature in database
GPG key ID: 94AC9B80EFA15ED9
6 changed files with 128 additions and 14 deletions

View file

@ -542,6 +542,8 @@ set(SOURCE_FILES
widgets/settingspages/NicknamesPage.hpp widgets/settingspages/NicknamesPage.hpp
widgets/settingspages/NotificationPage.cpp widgets/settingspages/NotificationPage.cpp
widgets/settingspages/NotificationPage.hpp widgets/settingspages/NotificationPage.hpp
widgets/settingspages/PluginsPage.cpp
widgets/settingspages/PluginsPage.hpp
widgets/settingspages/SettingsPage.cpp widgets/settingspages/SettingsPage.cpp
widgets/settingspages/SettingsPage.hpp widgets/settingspages/SettingsPage.hpp

View file

@ -11,6 +11,8 @@
#include "widgets/splits/Split.hpp" #include "widgets/splits/Split.hpp"
#include "widgets/Window.hpp" #include "widgets/Window.hpp"
#include <QJsonDocument>
#include <memory> #include <memory>
#include <utility> #include <utility>
@ -43,14 +45,32 @@ void PluginController::initialize(Settings &settings, Paths &paths)
<< "Missing index.lua in plugin directory" << pluginDir; << "Missing index.lua in plugin directory" << pluginDir;
continue; 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; qCDebug(chatterinoLua) << "Running lua file" << index;
lua_State *l = luaL_newstate(); lua_State *l = luaL_newstate();
@ -58,8 +78,8 @@ void PluginController::load(QFileInfo index, QDir pluginDir)
this->loadChatterinoLib(l); this->loadChatterinoLib(l);
auto pluginName = pluginDir.dirName(); auto pluginName = pluginDir.dirName();
auto plugin = std::make_unique<Plugin>(pluginName, l); auto plugin = std::make_unique<Plugin>(pluginName, l, meta);
this->plugins.insert({pluginName, std::move(plugin)}); this->plugins_.insert({pluginName, std::move(plugin)});
luaL_dofile(l, index.absoluteFilePath().toStdString().c_str()); luaL_dofile(l, index.absoluteFilePath().toStdString().c_str());
qCInfo(chatterinoLua) << "Loaded" << pluginName << "plugin from" << index; qCInfo(chatterinoLua) << "Loaded" << pluginName << "plugin from" << index;
@ -67,7 +87,7 @@ void PluginController::load(QFileInfo index, QDir pluginDir)
void PluginController::callEvery(const QString &functionName) 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_getglobal(plugin->state_, functionName.toStdString().c_str());
lua_pcall(plugin->state_, 0, 0, 0); lua_pcall(plugin->state_, 0, 0, 0);
@ -78,7 +98,7 @@ void PluginController::callEveryWithArgs(
const QString &functionName, int count, const QString &functionName, int count,
std::function<void(const std::unique_ptr<Plugin> &pl, lua_State *L)> argCb) std::function<void(const std::unique_ptr<Plugin> &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()); lua_getglobal(plugin->state_, functionName.toStdString().c_str());
argCb(plugin, plugin->state_); argCb(plugin, plugin->state_);
@ -89,7 +109,7 @@ void PluginController::callEveryWithArgs(
QString PluginController::tryExecPluginCommand(const QString &commandName, QString PluginController::tryExecPluginCommand(const QString &commandName,
const CommandContext &ctx) const CommandContext &ctx)
{ {
for (auto &[name, plugin] : this->plugins) for (auto &[name, plugin] : this->plugins_)
{ {
if (auto it = plugin->ownedCommands.find(commandName); if (auto it = plugin->ownedCommands.find(commandName);
it != plugin->ownedCommands.end()) it != plugin->ownedCommands.end())

View file

@ -9,6 +9,8 @@
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QJsonArray>
#include <QJsonObject>
#include <QString> #include <QString>
#include <map> #include <map>
@ -20,12 +22,34 @@ struct lua_State;
namespace chatterino { namespace chatterino {
struct PluginMeta {
QString name;
QString description;
QString authors;
QString homepage;
std::vector<QString> 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 class Plugin
{ {
public: public:
QString name; QString codename;
Plugin(QString name, lua_State *state) PluginMeta meta;
: name(std::move(name)) Plugin(QString codename, lua_State *state, PluginMeta meta)
: codename(std::move(codename))
, meta(std::move(meta))
, state_(state) , state_(state)
{ {
} }
@ -73,7 +97,7 @@ public:
// This is required to be public because of c functions // This is required to be public because of c functions
Plugin *getPluginByStatePtr(lua_State *L) Plugin *getPluginByStatePtr(lua_State *L)
{ {
for (auto &[name, plugin] : this->plugins) for (auto &[name, plugin] : this->plugins_)
{ {
if (plugin->state_ == L) if (plugin->state_ == L)
{ {
@ -83,10 +107,15 @@ public:
return nullptr; return nullptr;
} }
const std::map<QString, std::unique_ptr<Plugin>> &plugins() const
{
return this->plugins_;
}
private: private:
void load(QFileInfo index, QDir pluginDir); void load(QFileInfo index, QDir pluginDir, PluginMeta meta);
void loadChatterinoLib(lua_State *l); void loadChatterinoLib(lua_State *l);
std::map<QString, std::unique_ptr<Plugin>> plugins; std::map<QString, std::unique_ptr<Plugin>> plugins_;
}; };
}; // namespace chatterino }; // namespace chatterino

View file

@ -20,6 +20,7 @@
#include "widgets/settingspages/ModerationPage.hpp" #include "widgets/settingspages/ModerationPage.hpp"
#include "widgets/settingspages/NicknamesPage.hpp" #include "widgets/settingspages/NicknamesPage.hpp"
#include "widgets/settingspages/NotificationPage.hpp" #include "widgets/settingspages/NotificationPage.hpp"
#include "widgets/settingspages/PluginsPage.hpp"
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QLineEdit> #include <QLineEdit>
@ -215,6 +216,7 @@ void SettingsDialog::addTabs()
this->addTab([]{return new ModerationPage;}, "Moderation", ":/settings/moderation.svg", SettingsTabId::Moderation); 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 NotificationPage;}, "Live Notifications", ":/settings/notification2.svg");
this->addTab([]{return new ExternalToolsPage;}, "External tools", ":/settings/externaltools.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->ui_.tabContainer->addStretch(1);
this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", SettingsTabId(), Qt::AlignBottom); this->addTab([]{return new AboutPage;}, "About", ":/settings/about.svg", SettingsTabId(), Qt::AlignBottom);
// clang-format on // clang-format on

View file

@ -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 <QFormLayout>
#include <QGroupBox>
#include <QLabel>
namespace chatterino {
PluginsPage::PluginsPage()
{
LayoutCreator<PluginsPage> layoutCreator(this);
auto scroll = layoutCreator.emplace<QScrollArea>();
auto widget = scroll.emplaceScrollAreaWidget();
removeScrollAreaBackground(scroll.getElement(), widget.getElement());
auto layout = widget.setLayoutType<QVBoxLayout>();
auto group = layout.emplace<QGroupBox>("Plugins");
auto groupLayout = group.setLayoutType<QFormLayout>();
auto *description =
new QLabel("You can load plugins by putting them into "
"<chatterino-app-data-folder>/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<QGroupBox>(plugin->meta.name);
auto pl = plgroup.setLayoutType<QFormLayout>();
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

View file

@ -0,0 +1,9 @@
#pragma once
#include "widgets/settingspages/SettingsPage.hpp"
namespace chatterino {
class PluginsPage : public SettingsPage
{
public:
PluginsPage();
};
} // namespace chatterino