Improve plugin settings

This commit is contained in:
Mm2PL 2023-02-01 00:03:09 +01:00
parent 1616db56b1
commit 03ad993ab4
No known key found for this signature in database
GPG key ID: 94AC9B80EFA15ED9
5 changed files with 185 additions and 32 deletions

View file

@ -26,9 +26,37 @@ namespace chatterino {
void PluginController::initialize(Settings &settings, Paths &paths)
{
(void)(settings);
(void)paths;
auto dir = QDir(paths.pluginsDirectory);
settings.enableAnyPlugins.connect([this](bool enabled) {
if (enabled)
{
this->actuallyInitialize();
}
else
{
// uninitialize plugins
for (const auto &[codename, plugin] : this->plugins_)
{
this->reload(codename);
}
// can safely delete them now, after lua freed its stuff
this->plugins_.clear();
}
});
this->actuallyInitialize();
}
// this function exists to allow for connecting to enableAnyPlugins option
void PluginController::actuallyInitialize()
{
if (!getSettings()->enableAnyPlugins)
{
qCDebug(chatterinoLua)
<< "Loading plugins disabled via Setting, skipping";
return;
}
auto dir = QDir(getPaths()->pluginsDirectory);
qCDebug(chatterinoLua) << "loading plugins from " << dir;
for (const auto &info : dir.entryInfoList())
{
@ -110,6 +138,15 @@ void PluginController::load(QFileInfo index, QDir pluginDir, PluginMeta meta)
auto pluginName = pluginDir.dirName();
auto plugin = std::make_unique<Plugin>(pluginName, l, meta, pluginDir);
for (const auto &[codename, other] : this->plugins_)
{
if (other->meta.name == meta.name)
{
plugin->isDupeName = true;
other->isDupeName = true;
}
}
this->plugins_.insert({pluginName, std::move(plugin)});
int err = luaL_dofile(l, index.absoluteFilePath().toStdString().c_str());
@ -130,14 +167,22 @@ bool PluginController::reload(const QString &codename)
{
return false;
}
lua_close(it->second->state_);
if (it->second->state_ != nullptr)
{
lua_close(it->second->state_);
it->second->state_ = nullptr;
}
for (const auto &[cmd, _] : it->second->ownedCommands)
{
getApp()->commands->unregisterPluginCommand(cmd);
}
QDir loadDir = it->second->loadDirectory_;
this->plugins_.erase(codename);
this->tryLoadFromDir(loadDir);
it->second->ownedCommands.clear();
if (this->isEnabled(codename))
{
QDir loadDir = it->second->loadDirectory_;
this->plugins_.erase(codename);
this->tryLoadFromDir(loadDir);
}
return true;
}
@ -291,4 +336,15 @@ void PluginController::loadChatterinoLib(lua_State *L)
luaL_setfuncs(L, C2LIB, 0);
}
bool PluginController::isEnabled(const QString &codename)
{
if (!getSettings()->enableAnyPlugins)
{
return false;
}
auto vec = getSettings()->enabledPlugins.getValue();
auto it = std::find(vec.begin(), vec.end(), codename);
return it != vec.end();
}
}; // namespace chatterino

View file

@ -12,7 +12,9 @@
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <semver/semver.hpp>
#include <algorithm>
#include <map>
#include <memory>
#include <utility>
@ -27,16 +29,33 @@ struct PluginMeta {
QString description;
QString authors;
QString homepage;
QString license;
semver::version version;
std::vector<QString> tags;
std::set<QString> libraryPermissions;
explicit PluginMeta(const QJsonObject &obj)
: name(obj.value("name").toString())
: name(obj.value("name").toString("A Plugin with no name"))
, description(obj.value("description").toString())
, authors(obj.value("authors").toString())
, homepage(obj.value("homepage").toString())
, license(obj.value("license").toString("[unknown]"))
{
auto v = semver::from_string_noexcept(
obj.value("version").toString().toStdString());
if (v.has_value())
{
this->version = v.value();
}
else
{
this->version = semver::version(0, 0, 0);
description.append("\nWarning: invalid version");
}
for (const auto &t : obj.value("tags").toArray())
{
this->tags.push_back(t.toString());
@ -53,6 +72,7 @@ class Plugin
public:
QString codename;
PluginMeta meta;
bool isDupeName{};
Plugin(QString codename, lua_State *state, PluginMeta meta,
const QDir &loadDirectory)
@ -133,8 +153,10 @@ public:
}
bool reload(const QString &codename);
bool isEnabled(const QString &codename);
private:
void actuallyInitialize();
void load(QFileInfo index, QDir pluginDir, PluginMeta meta);
void loadChatterinoLib(lua_State *l);

View file

@ -520,6 +520,10 @@ public:
{"d", 1},
{"w", 1}}};
BoolSetting enableAnyPlugins = {"/plugins/load", false};
ChatterinoSetting<std::vector<QString>> enabledPlugins = {
"/plugins/enabled", {}};
private:
void updateModerationActions();
};

View file

@ -8,50 +8,84 @@
#include "util/LayoutCreator.hpp"
#include "util/RemoveScrollAreaBackground.hpp"
#include <QCheckBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <qobject.h>
#include <QObject>
#include <QPushButton>
#include <qwidget.h>
#include <QWidget>
namespace chatterino {
PluginsPage::PluginsPage()
: scrollArea_(nullptr)
: scrollAreaWidget_(nullptr)
, dataFrame_(nullptr)
{
qDebug() << "plugins page created";
LayoutCreator<PluginsPage> layoutCreator(this);
this->scrollArea_ = layoutCreator.emplace<QScrollArea>();
auto scrollArea = layoutCreator.emplace<QScrollArea>();
auto widget = scrollArea.emplaceScrollAreaWidget();
this->scrollAreaWidget_ = widget;
removeScrollAreaBackground(scrollArea.getElement(), widget.getElement());
auto layout = widget.setLayoutType<QVBoxLayout>();
{
auto group = layout.emplace<QGroupBox>("General plugin settings");
this->generalGroup = group.getElement();
auto groupLayout = group.setLayoutType<QFormLayout>();
auto *description = new QLabel(
"You can load plugins by putting them into " +
formatRichNamedLink("file:///" + getPaths()->pluginsDirectory,
"the Plugins directory") +
". Each one is a new directory.");
description->setOpenExternalLinks(true);
description->setWordWrap(true);
description->setStyleSheet("color: #bbb");
groupLayout->addRow(description);
auto *box = this->createCheckBox("Enable plugins",
getSettings()->enableAnyPlugins);
QObject::connect(box, &QCheckBox::released, [this, box]() {
//using namespace std::chrono_literals;
//QTimer::singleShot(10000ms, [this]() {
this->rebuildContent();
//});
});
groupLayout->addRow(box);
}
this->rebuildContent();
}
void PluginsPage::rebuildContent()
{
auto widget = this->scrollArea_.emplaceScrollAreaWidget();
removeScrollAreaBackground(this->scrollArea_.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 " +
formatRichNamedLink("file:///" + getPaths()->pluginsDirectory,
"the Plugins directory") +
". Each one is a "
"new directory.");
description->setOpenExternalLinks(true);
description->setWordWrap(true);
description->setStyleSheet("color: #bbb");
groupLayout->addRow(description);
if (this->dataFrame_ != nullptr)
{
this->dataFrame_->deleteLater();
this->dataFrame_ = nullptr;
}
auto frame = LayoutCreator<QFrame>(new QFrame(this));
this->dataFrame_ = frame.getElement();
this->scrollAreaWidget_.append(this->dataFrame_);
auto layout = frame.setLayoutType<QVBoxLayout>();
for (const auto &[codename, plugin] : getApp()->plugins->plugins())
{
auto plgroup = groupLayout.emplace<QGroupBox>(plugin->meta.name);
auto headerText = QString("%1 (%2)").arg(
plugin->meta.name,
QString::fromStdString(plugin->meta.version.to_string()));
if (plugin->isDupeName)
{
// add ", from <folder name>)" in place of ")"
headerText.chop(1);
headerText += ", from " + codename + ")";
}
auto plgroup = layout.emplace<QGroupBox>(headerText);
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);
@ -60,6 +94,7 @@ void PluginsPage::rebuildContent()
homepage->setOpenExternalLinks(true);
pl->addRow("Homepage", homepage);
pl->addRow("License", new QLabel(plugin->meta.license));
QString libString;
bool hasDangerous = false;
@ -99,6 +134,32 @@ void PluginsPage::rebuildContent()
}
pl->addRow("Commands", new QLabel(cmds));
QString enableOrDisableStr = "Enable";
if (getApp()->plugins->isEnabled(codename))
{
enableOrDisableStr = "Disable";
}
auto *enableDisable = new QPushButton(enableOrDisableStr);
QObject::connect(
enableDisable, &QPushButton::pressed, [name = codename, this]() {
std::vector<QString> val =
getSettings()->enabledPlugins.getValue();
if (getApp()->plugins->isEnabled(name))
{
val.erase(std::remove(val.begin(), val.end(), name),
val.end());
}
else
{
val.push_back(name);
}
getSettings()->enabledPlugins.setValue(val);
getApp()->plugins->reload(name);
this->rebuildContent();
});
pl->addRow(enableDisable);
auto *reload = new QPushButton("Reload");
QObject::connect(reload, &QPushButton::pressed,
[name = codename, this]() {
@ -108,4 +169,5 @@ void PluginsPage::rebuildContent()
pl->addRow(reload);
}
}
} // namespace chatterino

View file

@ -2,8 +2,11 @@
#include "util/LayoutCreator.hpp"
#include "widgets/settingspages/SettingsPage.hpp"
#include <QDebug>
#include <QFormLayout>
#include <QGroupBox>
#include <QWidget>
namespace chatterino {
class Plugin;
@ -11,10 +14,16 @@ class PluginsPage : public SettingsPage
{
public:
PluginsPage();
~PluginsPage() override
{
qDebug() << "plugins page deleted";
}
private:
void rebuildContent();
LayoutCreator<QScrollArea> scrollArea_;
LayoutCreator<QWidget> scrollAreaWidget_;
QGroupBox *generalGroup;
QFrame *dataFrame_;
};
} // namespace chatterino