mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Improve plugin settings
This commit is contained in:
parent
1616db56b1
commit
03ad993ab4
5 changed files with 185 additions and 32 deletions
|
@ -26,9 +26,37 @@ namespace chatterino {
|
||||||
|
|
||||||
void PluginController::initialize(Settings &settings, Paths &paths)
|
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;
|
qCDebug(chatterinoLua) << "loading plugins from " << dir;
|
||||||
for (const auto &info : dir.entryInfoList())
|
for (const auto &info : dir.entryInfoList())
|
||||||
{
|
{
|
||||||
|
@ -110,6 +138,15 @@ void PluginController::load(QFileInfo index, QDir pluginDir, PluginMeta meta)
|
||||||
|
|
||||||
auto pluginName = pluginDir.dirName();
|
auto pluginName = pluginDir.dirName();
|
||||||
auto plugin = std::make_unique<Plugin>(pluginName, l, meta, pluginDir);
|
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)});
|
this->plugins_.insert({pluginName, std::move(plugin)});
|
||||||
|
|
||||||
int err = luaL_dofile(l, index.absoluteFilePath().toStdString().c_str());
|
int err = luaL_dofile(l, index.absoluteFilePath().toStdString().c_str());
|
||||||
|
@ -130,14 +167,22 @@ bool PluginController::reload(const QString &codename)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (it->second->state_ != nullptr)
|
||||||
|
{
|
||||||
lua_close(it->second->state_);
|
lua_close(it->second->state_);
|
||||||
|
it->second->state_ = nullptr;
|
||||||
|
}
|
||||||
for (const auto &[cmd, _] : it->second->ownedCommands)
|
for (const auto &[cmd, _] : it->second->ownedCommands)
|
||||||
{
|
{
|
||||||
getApp()->commands->unregisterPluginCommand(cmd);
|
getApp()->commands->unregisterPluginCommand(cmd);
|
||||||
}
|
}
|
||||||
|
it->second->ownedCommands.clear();
|
||||||
|
if (this->isEnabled(codename))
|
||||||
|
{
|
||||||
QDir loadDir = it->second->loadDirectory_;
|
QDir loadDir = it->second->loadDirectory_;
|
||||||
this->plugins_.erase(codename);
|
this->plugins_.erase(codename);
|
||||||
this->tryLoadFromDir(loadDir);
|
this->tryLoadFromDir(loadDir);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,4 +336,15 @@ void PluginController::loadChatterinoLib(lua_State *L)
|
||||||
luaL_setfuncs(L, C2LIB, 0);
|
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
|
}; // namespace chatterino
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <semver/semver.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -27,16 +29,33 @@ struct PluginMeta {
|
||||||
QString description;
|
QString description;
|
||||||
QString authors;
|
QString authors;
|
||||||
QString homepage;
|
QString homepage;
|
||||||
|
|
||||||
|
QString license;
|
||||||
|
semver::version version;
|
||||||
|
|
||||||
std::vector<QString> tags;
|
std::vector<QString> tags;
|
||||||
|
|
||||||
std::set<QString> libraryPermissions;
|
std::set<QString> libraryPermissions;
|
||||||
|
|
||||||
explicit PluginMeta(const QJsonObject &obj)
|
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())
|
, description(obj.value("description").toString())
|
||||||
, authors(obj.value("authors").toString())
|
, authors(obj.value("authors").toString())
|
||||||
, homepage(obj.value("homepage").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())
|
for (const auto &t : obj.value("tags").toArray())
|
||||||
{
|
{
|
||||||
this->tags.push_back(t.toString());
|
this->tags.push_back(t.toString());
|
||||||
|
@ -53,6 +72,7 @@ class Plugin
|
||||||
public:
|
public:
|
||||||
QString codename;
|
QString codename;
|
||||||
PluginMeta meta;
|
PluginMeta meta;
|
||||||
|
bool isDupeName{};
|
||||||
|
|
||||||
Plugin(QString codename, lua_State *state, PluginMeta meta,
|
Plugin(QString codename, lua_State *state, PluginMeta meta,
|
||||||
const QDir &loadDirectory)
|
const QDir &loadDirectory)
|
||||||
|
@ -133,8 +153,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool reload(const QString &codename);
|
bool reload(const QString &codename);
|
||||||
|
bool isEnabled(const QString &codename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void actuallyInitialize();
|
||||||
void load(QFileInfo index, QDir pluginDir, PluginMeta meta);
|
void load(QFileInfo index, QDir pluginDir, PluginMeta meta);
|
||||||
void loadChatterinoLib(lua_State *l);
|
void loadChatterinoLib(lua_State *l);
|
||||||
|
|
||||||
|
|
|
@ -520,6 +520,10 @@ public:
|
||||||
{"d", 1},
|
{"d", 1},
|
||||||
{"w", 1}}};
|
{"w", 1}}};
|
||||||
|
|
||||||
|
BoolSetting enableAnyPlugins = {"/plugins/load", false};
|
||||||
|
ChatterinoSetting<std::vector<QString>> enabledPlugins = {
|
||||||
|
"/plugins/enabled", {}};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateModerationActions();
|
void updateModerationActions();
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,50 +8,84 @@
|
||||||
#include "util/LayoutCreator.hpp"
|
#include "util/LayoutCreator.hpp"
|
||||||
#include "util/RemoveScrollAreaBackground.hpp"
|
#include "util/RemoveScrollAreaBackground.hpp"
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
#include <QFormLayout>
|
#include <QFormLayout>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <QObject>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <qwidget.h>
|
#include <QWidget>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
PluginsPage::PluginsPage()
|
PluginsPage::PluginsPage()
|
||||||
: scrollArea_(nullptr)
|
: scrollAreaWidget_(nullptr)
|
||||||
|
, dataFrame_(nullptr)
|
||||||
{
|
{
|
||||||
|
qDebug() << "plugins page created";
|
||||||
LayoutCreator<PluginsPage> layoutCreator(this);
|
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();
|
this->rebuildContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginsPage::rebuildContent()
|
void PluginsPage::rebuildContent()
|
||||||
{
|
{
|
||||||
auto widget = this->scrollArea_.emplaceScrollAreaWidget();
|
if (this->dataFrame_ != nullptr)
|
||||||
removeScrollAreaBackground(this->scrollArea_.getElement(),
|
{
|
||||||
widget.getElement());
|
this->dataFrame_->deleteLater();
|
||||||
|
this->dataFrame_ = nullptr;
|
||||||
auto layout = widget.setLayoutType<QVBoxLayout>();
|
}
|
||||||
auto group = layout.emplace<QGroupBox>("Plugins");
|
auto frame = LayoutCreator<QFrame>(new QFrame(this));
|
||||||
auto groupLayout = group.setLayoutType<QFormLayout>();
|
this->dataFrame_ = frame.getElement();
|
||||||
|
this->scrollAreaWidget_.append(this->dataFrame_);
|
||||||
auto *description = new QLabel(
|
auto layout = frame.setLayoutType<QVBoxLayout>();
|
||||||
"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);
|
|
||||||
|
|
||||||
for (const auto &[codename, plugin] : getApp()->plugins->plugins())
|
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 pl = plgroup.setLayoutType<QFormLayout>();
|
||||||
auto *descrText = new QLabel(plugin->meta.description);
|
auto *descrText = new QLabel(plugin->meta.description);
|
||||||
//descrText->setTextFormat(Qt::TextFormat::MarkdownText);
|
|
||||||
descrText->setWordWrap(true);
|
descrText->setWordWrap(true);
|
||||||
descrText->setStyleSheet("color: #bbb");
|
descrText->setStyleSheet("color: #bbb");
|
||||||
pl->addRow(descrText);
|
pl->addRow(descrText);
|
||||||
|
@ -60,6 +94,7 @@ void PluginsPage::rebuildContent()
|
||||||
homepage->setOpenExternalLinks(true);
|
homepage->setOpenExternalLinks(true);
|
||||||
|
|
||||||
pl->addRow("Homepage", homepage);
|
pl->addRow("Homepage", homepage);
|
||||||
|
pl->addRow("License", new QLabel(plugin->meta.license));
|
||||||
|
|
||||||
QString libString;
|
QString libString;
|
||||||
bool hasDangerous = false;
|
bool hasDangerous = false;
|
||||||
|
@ -99,6 +134,32 @@ void PluginsPage::rebuildContent()
|
||||||
}
|
}
|
||||||
pl->addRow("Commands", new QLabel(cmds));
|
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");
|
auto *reload = new QPushButton("Reload");
|
||||||
QObject::connect(reload, &QPushButton::pressed,
|
QObject::connect(reload, &QPushButton::pressed,
|
||||||
[name = codename, this]() {
|
[name = codename, this]() {
|
||||||
|
@ -108,4 +169,5 @@ void PluginsPage::rebuildContent()
|
||||||
pl->addRow(reload);
|
pl->addRow(reload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -2,8 +2,11 @@
|
||||||
#include "util/LayoutCreator.hpp"
|
#include "util/LayoutCreator.hpp"
|
||||||
#include "widgets/settingspages/SettingsPage.hpp"
|
#include "widgets/settingspages/SettingsPage.hpp"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
#include <QFormLayout>
|
#include <QFormLayout>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
class Plugin;
|
class Plugin;
|
||||||
|
|
||||||
|
@ -11,10 +14,16 @@ class PluginsPage : public SettingsPage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PluginsPage();
|
PluginsPage();
|
||||||
|
~PluginsPage() override
|
||||||
|
{
|
||||||
|
qDebug() << "plugins page deleted";
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void rebuildContent();
|
void rebuildContent();
|
||||||
|
|
||||||
LayoutCreator<QScrollArea> scrollArea_;
|
LayoutCreator<QWidget> scrollAreaWidget_;
|
||||||
|
QGroupBox *generalGroup;
|
||||||
|
QFrame *dataFrame_;
|
||||||
};
|
};
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
Loading…
Reference in a new issue