mirror of
https://github.com/Chatterino/chatterino2.git
synced 2026-08-24 10:04:53 -05:00
fix(plugins): don't crash on invalid plugin meta (#7166)
This makes our plugin storage use a variant for the different plugin states: - UnloadedPlugin (used if the plugin's meta was invalid) - PluginPtr (a normal valid plugin, which may still contain lua errors) How to test the crash that doesn't crash with this PR: Add an invalid permission in your `info.json` file Co-authored-by: Mm2PL <mm2pl+gh@kotmisia.pl>
This commit is contained in:
@@ -18,10 +18,13 @@
|
|||||||
# include <semver/semver.hpp>
|
# include <semver/semver.hpp>
|
||||||
# include <sol/forward.hpp>
|
# include <sol/forward.hpp>
|
||||||
|
|
||||||
|
# include <cassert>
|
||||||
# include <memory>
|
# include <memory>
|
||||||
# include <optional>
|
# include <optional>
|
||||||
# include <unordered_map>
|
# include <unordered_map>
|
||||||
# include <unordered_set>
|
# include <unordered_set>
|
||||||
|
# include <utility>
|
||||||
|
# include <variant>
|
||||||
# include <vector>
|
# include <vector>
|
||||||
|
|
||||||
struct lua_State;
|
struct lua_State;
|
||||||
@@ -37,6 +40,25 @@ struct SignalCallback;
|
|||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
/// A plugin that hasn't been loaded.
|
||||||
|
///
|
||||||
|
/// Most likely, its metadata is invalid.
|
||||||
|
class UnloadedPlugin
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString id;
|
||||||
|
PluginMeta meta;
|
||||||
|
QDir loadDirectory;
|
||||||
|
|
||||||
|
UnloadedPlugin(QString id_, PluginMeta meta_, const QDir &loadDirectory_)
|
||||||
|
: id(std::move(id_))
|
||||||
|
, meta(std::move(meta_))
|
||||||
|
, loadDirectory(loadDirectory_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A plugin with a valid lua state
|
||||||
class Plugin
|
class Plugin
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -49,8 +71,12 @@ public:
|
|||||||
, meta(std::move(meta))
|
, meta(std::move(meta))
|
||||||
, loadDirectory_(loadDirectory)
|
, loadDirectory_(loadDirectory)
|
||||||
, state_(state)
|
, state_(state)
|
||||||
, selfRef_(state ? this : nullptr)
|
, selfRef_(this)
|
||||||
{
|
{
|
||||||
|
// The PluginMeta here must be valid, otherwise it should be initialized
|
||||||
|
// as an UnloadedPlugin
|
||||||
|
assert(this->meta.isValid());
|
||||||
|
assert(this->state_ != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Plugin();
|
~Plugin();
|
||||||
@@ -148,5 +174,10 @@ private:
|
|||||||
friend class PluginController;
|
friend class PluginController;
|
||||||
friend class PluginControllerAccess; // this is for tests
|
friend class PluginControllerAccess; // this is for tests
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using PluginPtr = std::unique_ptr<Plugin>;
|
||||||
|
using AnyPlugin = std::variant<PluginPtr, UnloadedPlugin>;
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
# include "singletons/Paths.hpp"
|
# include "singletons/Paths.hpp"
|
||||||
# include "singletons/Settings.hpp"
|
# include "singletons/Settings.hpp"
|
||||||
# include "singletons/WindowManager.hpp"
|
# include "singletons/WindowManager.hpp"
|
||||||
|
# include "util/Variant.hpp"
|
||||||
# include "widgets/splits/SplitContainer.hpp"
|
# include "widgets/splits/SplitContainer.hpp"
|
||||||
# include "widgets/Window.hpp"
|
# include "widgets/Window.hpp"
|
||||||
|
|
||||||
@@ -131,9 +132,10 @@ bool PluginController::tryLoadFromDir(const QDir &pluginDir)
|
|||||||
{
|
{
|
||||||
qCWarning(chatterinoLua) << "- " << why;
|
qCWarning(chatterinoLua) << "- " << why;
|
||||||
}
|
}
|
||||||
auto plugin = std::make_unique<Plugin>(pluginDir.dirName(), nullptr,
|
|
||||||
meta, pluginDir);
|
this->plugins_.insert(
|
||||||
this->plugins_.insert({pluginDir.dirName(), std::move(plugin)});
|
{pluginDir.dirName(),
|
||||||
|
UnloadedPlugin{pluginDir.dirName(), meta, pluginDir}});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this->load(index, pluginDir, meta);
|
this->load(index, pluginDir, meta);
|
||||||
@@ -357,11 +359,26 @@ bool PluginController::reload(const QString &id)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &[cmd, _] : it->second->ownedCommands)
|
if (const auto *oPlugin = std::get_if<PluginPtr>(&it->second))
|
||||||
{
|
{
|
||||||
getApp()->getCommands()->unregisterPluginCommand(cmd);
|
const auto &plugin = *oPlugin;
|
||||||
|
|
||||||
|
for (const auto &[cmd, _] : plugin->ownedCommands)
|
||||||
|
{
|
||||||
|
getApp()->getCommands()->unregisterPluginCommand(cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
QDir loadDir = it->second->loadDirectory_;
|
|
||||||
|
const auto loadDir = std::visit(variant::Overloaded{
|
||||||
|
[&](const PluginPtr &plugin) {
|
||||||
|
return plugin->loadDirectory_;
|
||||||
|
},
|
||||||
|
[&](const UnloadedPlugin &plugin) {
|
||||||
|
return plugin.loadDirectory;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
it->second);
|
||||||
|
|
||||||
// Since Plugin owns the state, it will clean up everything related to it
|
// Since Plugin owns the state, it will clean up everything related to it
|
||||||
this->plugins_.erase(id);
|
this->plugins_.erase(id);
|
||||||
this->queueChangeNotification();
|
this->queueChangeNotification();
|
||||||
@@ -372,8 +389,15 @@ bool PluginController::reload(const QString &id)
|
|||||||
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 (const auto &[name, anyPlugin] : this->allPlugins())
|
||||||
{
|
{
|
||||||
|
const auto *oPl = std::get_if<PluginPtr>(&anyPlugin);
|
||||||
|
if (oPl == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto &plugin = *oPl;
|
||||||
|
|
||||||
if (auto it = plugin->ownedCommands.find(commandName);
|
if (auto it = plugin->ownedCommands.find(commandName);
|
||||||
it != plugin->ownedCommands.end())
|
it != plugin->ownedCommands.end())
|
||||||
{
|
{
|
||||||
@@ -421,8 +445,15 @@ Plugin *PluginController::getPluginByStatePtr(lua_State *L)
|
|||||||
auto *mainL = lua_tothread(L, -1);
|
auto *mainL = lua_tothread(L, -1);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
L = mainL;
|
L = mainL;
|
||||||
for (auto &[name, plugin] : this->plugins_)
|
for (const auto &[name, anyPlugin] : this->allPlugins())
|
||||||
{
|
{
|
||||||
|
const auto *oPl = std::get_if<PluginPtr>(&anyPlugin);
|
||||||
|
if (oPl == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto &plugin = *oPl;
|
||||||
|
|
||||||
if (plugin->state_ == L)
|
if (plugin->state_ == L)
|
||||||
{
|
{
|
||||||
return plugin.get();
|
return plugin.get();
|
||||||
@@ -431,8 +462,19 @@ Plugin *PluginController::getPluginByStatePtr(lua_State *L)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::map<QString, std::unique_ptr<Plugin>> &PluginController::plugins()
|
void PluginController::forEachPlugin(
|
||||||
const
|
FunctionRef<void(const std::unique_ptr<Plugin> &)> cb) const
|
||||||
|
{
|
||||||
|
for (const auto &[_, anyPlugin] : this->allPlugins())
|
||||||
|
{
|
||||||
|
if (const auto *plugin = std::get_if<PluginPtr>(&anyPlugin))
|
||||||
|
{
|
||||||
|
cb(*plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::map<QString, AnyPlugin> &PluginController::allPlugins() const
|
||||||
{
|
{
|
||||||
return this->plugins_;
|
return this->plugins_;
|
||||||
}
|
}
|
||||||
@@ -443,8 +485,16 @@ std::pair<bool, QStringList> PluginController::updateCustomCompletions(
|
|||||||
{
|
{
|
||||||
QStringList results;
|
QStringList results;
|
||||||
|
|
||||||
for (const auto &[name, pl] : this->plugins())
|
for (const auto &[name, anyPlugin] : this->allPlugins())
|
||||||
{
|
{
|
||||||
|
const auto *oPl = std::get_if<PluginPtr>(&anyPlugin);
|
||||||
|
if (oPl == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &pl = *oPl;
|
||||||
|
|
||||||
if (!pl->error().isNull() || pl->state_ == nullptr)
|
if (!pl->error().isNull() || pl->state_ == nullptr)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
# include "common/websockets/WebSocketPool.hpp"
|
# include "common/websockets/WebSocketPool.hpp"
|
||||||
# include "controllers/commands/CommandContext.hpp"
|
# include "controllers/commands/CommandContext.hpp"
|
||||||
# include "controllers/plugins/Plugin.hpp"
|
# include "controllers/plugins/Plugin.hpp"
|
||||||
|
# include "util/FunctionRef.hpp"
|
||||||
|
|
||||||
# include <pajlada/signals/signal.hpp>
|
# include <pajlada/signals/signal.hpp>
|
||||||
# include <QDir>
|
# include <QDir>
|
||||||
@@ -45,8 +46,11 @@ 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);
|
||||||
|
|
||||||
// TODO: make a function that iterates plugins that aren't errored/enabled
|
/// Run `cb` on every loaded plugin, including those with load errors
|
||||||
const std::map<QString, std::unique_ptr<Plugin>> &plugins() const;
|
void forEachPlugin(
|
||||||
|
FunctionRef<void(const std::unique_ptr<Plugin> &)>) const;
|
||||||
|
|
||||||
|
const std::map<QString, AnyPlugin> &allPlugins() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reload plugin given by id
|
* @brief Reload plugin given by id
|
||||||
@@ -86,7 +90,7 @@ private:
|
|||||||
|
|
||||||
void queueChangeNotification();
|
void queueChangeNotification();
|
||||||
|
|
||||||
std::map<QString, std::unique_ptr<Plugin>> plugins_;
|
std::map<QString, AnyPlugin> plugins_;
|
||||||
WebSocketPool webSocketPool_;
|
WebSocketPool webSocketPool_;
|
||||||
|
|
||||||
std::vector<
|
std::vector<
|
||||||
|
|||||||
@@ -665,16 +665,24 @@ void PluginRepl::log(std::optional<lua::api::LogLevel> level,
|
|||||||
|
|
||||||
void PluginRepl::tryUpdate()
|
void PluginRepl::tryUpdate()
|
||||||
{
|
{
|
||||||
auto it = getApp()->getPlugins()->plugins().find(this->id);
|
auto it = getApp()->getPlugins()->allPlugins().find(this->id);
|
||||||
if (it == getApp()->getPlugins()->plugins().end())
|
if (it == getApp()->getPlugins()->allPlugins().end())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto *oPl = std::get_if<PluginPtr>(&it->second);
|
||||||
|
if (oPl == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &pl = *oPl;
|
||||||
|
|
||||||
if (!PluginController::isPluginEnabled(this->id))
|
if (!PluginController::isPluginEnabled(this->id))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->setPlugin(it->second.get());
|
this->setPlugin(pl.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginRepl::setPlugin(Plugin *plugin)
|
void PluginRepl::setPlugin(Plugin *plugin)
|
||||||
|
|||||||
@@ -8,12 +8,14 @@
|
|||||||
# include "Application.hpp"
|
# include "Application.hpp"
|
||||||
# include "common/Args.hpp"
|
# include "common/Args.hpp"
|
||||||
# include "controllers/accounts/AccountController.hpp"
|
# include "controllers/accounts/AccountController.hpp"
|
||||||
|
# include "controllers/plugins/Plugin.hpp"
|
||||||
# include "controllers/plugins/PluginController.hpp"
|
# include "controllers/plugins/PluginController.hpp"
|
||||||
# include "singletons/Paths.hpp"
|
# include "singletons/Paths.hpp"
|
||||||
# include "singletons/Settings.hpp"
|
# include "singletons/Settings.hpp"
|
||||||
# include "util/Helpers.hpp"
|
# include "util/Helpers.hpp"
|
||||||
# include "util/LayoutCreator.hpp"
|
# include "util/LayoutCreator.hpp"
|
||||||
# include "util/RemoveScrollAreaBackground.hpp"
|
# include "util/RemoveScrollAreaBackground.hpp"
|
||||||
|
# include "util/Variant.hpp"
|
||||||
# include "widgets/PluginRepl.hpp"
|
# include "widgets/PluginRepl.hpp"
|
||||||
# include "widgets/settingspages/SettingWidget.hpp"
|
# include "widgets/settingspages/SettingWidget.hpp"
|
||||||
|
|
||||||
@@ -121,54 +123,53 @@ void PluginsPage::rebuildContent()
|
|||||||
this->scrollAreaWidget_.append(this->dataFrame_);
|
this->scrollAreaWidget_.append(this->dataFrame_);
|
||||||
auto layout = frame.setLayoutType<QVBoxLayout>();
|
auto layout = frame.setLayoutType<QVBoxLayout>();
|
||||||
layout->setParent(this->dataFrame_);
|
layout->setParent(this->dataFrame_);
|
||||||
for (const auto &[id, plugin] : getApp()->getPlugins()->plugins())
|
for (const auto &[id, plugin] : getApp()->getPlugins()->allPlugins())
|
||||||
{
|
{
|
||||||
|
const auto &meta = std::visit(variant::Overloaded{
|
||||||
|
[&](const PluginPtr &plugin) {
|
||||||
|
return plugin->meta;
|
||||||
|
},
|
||||||
|
[&](const UnloadedPlugin &plugin) {
|
||||||
|
return plugin.meta;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugin);
|
||||||
|
|
||||||
auto groupHeaderText =
|
auto groupHeaderText =
|
||||||
QString("%1 (%2, from %3)")
|
QString("%1 (%2, from %3)")
|
||||||
.arg(plugin->meta.name,
|
.arg(meta.name,
|
||||||
QString::fromStdString(plugin->meta.version.to_string()),
|
QString::fromStdString(meta.version.to_string()), id);
|
||||||
id);
|
|
||||||
auto groupBox = layout.emplace<QGroupBox>(groupHeaderText);
|
auto groupBox = layout.emplace<QGroupBox>(groupHeaderText);
|
||||||
groupBox->setParent(this->dataFrame_);
|
groupBox->setParent(this->dataFrame_);
|
||||||
|
|
||||||
auto pluginEntry = groupBox.setLayoutType<QFormLayout>();
|
auto pluginEntry = groupBox.setLayoutType<QFormLayout>();
|
||||||
pluginEntry->setParent(groupBox.getElement());
|
pluginEntry->setParent(groupBox.getElement());
|
||||||
|
|
||||||
if (!plugin->meta.isValid())
|
if (!meta.isValid())
|
||||||
{
|
{
|
||||||
QString errors = "<ul>";
|
QString errors = "<ul>";
|
||||||
for (const auto &err : plugin->meta.errors)
|
for (const auto &err : meta.errors)
|
||||||
{
|
{
|
||||||
errors += "<li>" + err.toHtmlEscaped() + "</li>";
|
errors += "<li>" + err.toHtmlEscaped() + "</li>";
|
||||||
}
|
}
|
||||||
errors += "</ul>";
|
errors += "</ul>";
|
||||||
|
|
||||||
auto *warningLabel = new QLabel(
|
auto *warningLabel = new QLabel("There were errors while loading "
|
||||||
"There were errors while loading metadata for this plugin:" +
|
"metadata for this plugin:" +
|
||||||
errors,
|
errors,
|
||||||
this->dataFrame_);
|
this->dataFrame_);
|
||||||
warningLabel->setTextFormat(Qt::RichText);
|
warningLabel->setTextFormat(Qt::RichText);
|
||||||
warningLabel->setStyleSheet("color: #f00");
|
warningLabel->setStyleSheet("color: #f00");
|
||||||
pluginEntry->addRow(warningLabel);
|
pluginEntry->addRow(warningLabel);
|
||||||
}
|
}
|
||||||
if (!plugin->error().isNull())
|
|
||||||
{
|
|
||||||
auto *errorLabel =
|
|
||||||
new QLabel("There was an error while loading this plugin: " +
|
|
||||||
plugin->error(),
|
|
||||||
this->dataFrame_);
|
|
||||||
errorLabel->setStyleSheet("color: #f00");
|
|
||||||
errorLabel->setWordWrap(true);
|
|
||||||
pluginEntry->addRow(errorLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *description =
|
auto *description = new QLabel(meta.description, this->dataFrame_);
|
||||||
new QLabel(plugin->meta.description, this->dataFrame_);
|
|
||||||
description->setWordWrap(true);
|
description->setWordWrap(true);
|
||||||
description->setStyleSheet("color: #bbb");
|
description->setStyleSheet("color: #bbb");
|
||||||
pluginEntry->addRow(description);
|
pluginEntry->addRow(description);
|
||||||
|
|
||||||
QString authorsTxt;
|
QString authorsTxt;
|
||||||
for (const auto &author : plugin->meta.authors)
|
for (const auto &author : meta.authors)
|
||||||
{
|
{
|
||||||
if (!authorsTxt.isEmpty())
|
if (!authorsTxt.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -180,32 +181,49 @@ void PluginsPage::rebuildContent()
|
|||||||
pluginEntry->addRow("Authors",
|
pluginEntry->addRow("Authors",
|
||||||
new QLabel(authorsTxt, this->dataFrame_));
|
new QLabel(authorsTxt, this->dataFrame_));
|
||||||
|
|
||||||
if (!plugin->meta.homepage.isEmpty())
|
if (!meta.homepage.isEmpty())
|
||||||
{
|
{
|
||||||
auto *homepage = new QLabel(formatRichLink(plugin->meta.homepage),
|
auto *homepage =
|
||||||
this->dataFrame_);
|
new QLabel(formatRichLink(meta.homepage), this->dataFrame_);
|
||||||
homepage->setOpenExternalLinks(true);
|
homepage->setOpenExternalLinks(true);
|
||||||
pluginEntry->addRow("Homepage", homepage);
|
pluginEntry->addRow("Homepage", homepage);
|
||||||
}
|
}
|
||||||
pluginEntry->addRow("License",
|
pluginEntry->addRow("License",
|
||||||
new QLabel(plugin->meta.license, this->dataFrame_));
|
new QLabel(meta.license, this->dataFrame_));
|
||||||
|
|
||||||
QString commandsTxt;
|
if (const auto *oValidPlugin = std::get_if<PluginPtr>(&plugin))
|
||||||
for (const auto &cmdName : plugin->listRegisteredCommands())
|
|
||||||
{
|
{
|
||||||
if (!commandsTxt.isEmpty())
|
const auto &validPlugin = *oValidPlugin;
|
||||||
|
|
||||||
|
if (!validPlugin->error().isNull())
|
||||||
{
|
{
|
||||||
commandsTxt += ", ";
|
auto *errorLabel = new QLabel(
|
||||||
|
"There was an error while loading this plugin: " +
|
||||||
|
validPlugin->error(),
|
||||||
|
this->dataFrame_);
|
||||||
|
errorLabel->setStyleSheet("color: #f00");
|
||||||
|
errorLabel->setWordWrap(true);
|
||||||
|
pluginEntry->addRow(errorLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
commandsTxt += cmdName;
|
QString commandsTxt;
|
||||||
|
for (const auto &cmdName : validPlugin->listRegisteredCommands())
|
||||||
|
{
|
||||||
|
if (!commandsTxt.isEmpty())
|
||||||
|
{
|
||||||
|
commandsTxt += ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
commandsTxt += cmdName;
|
||||||
|
}
|
||||||
|
pluginEntry->addRow("Commands",
|
||||||
|
new QLabel(commandsTxt, this->dataFrame_));
|
||||||
}
|
}
|
||||||
pluginEntry->addRow("Commands",
|
|
||||||
new QLabel(commandsTxt, this->dataFrame_));
|
if (!meta.permissions.empty())
|
||||||
if (!plugin->meta.permissions.empty())
|
|
||||||
{
|
{
|
||||||
QString perms = "<ul>";
|
QString perms = "<ul>";
|
||||||
for (const auto &perm : plugin->meta.permissions)
|
for (const auto &perm : meta.permissions)
|
||||||
{
|
{
|
||||||
perms += "<li>" + perm.toHtml() + "</li>";
|
perms += "<li>" + perm.toHtml() + "</li>";
|
||||||
}
|
}
|
||||||
@@ -217,30 +235,34 @@ void PluginsPage::rebuildContent()
|
|||||||
pluginEntry->addRow(lbl);
|
pluginEntry->addRow(lbl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin->meta.isValid())
|
QString toggleTxt = "Enable";
|
||||||
|
if (PluginController::isPluginEnabled(id))
|
||||||
{
|
{
|
||||||
QString toggleTxt = "Enable";
|
toggleTxt = "Disable";
|
||||||
if (PluginController::isPluginEnabled(id))
|
}
|
||||||
{
|
|
||||||
toggleTxt = "Disable";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *toggleButton = new QPushButton(toggleTxt, this->dataFrame_);
|
auto *toggleButton = new QPushButton(toggleTxt, this->dataFrame_);
|
||||||
QObject::connect(
|
QObject::connect(toggleButton, &QPushButton::pressed, [name = id]() {
|
||||||
toggleButton, &QPushButton::pressed, [name = id, this]() {
|
QStringList val = getSettings()->enabledPlugins;
|
||||||
QStringList val = getSettings()->enabledPlugins;
|
if (PluginController::isPluginEnabled(name))
|
||||||
if (PluginController::isPluginEnabled(name))
|
{
|
||||||
{
|
val.removeAll(name);
|
||||||
val.removeAll(name);
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
val.push_back(name);
|
||||||
val.push_back(name);
|
}
|
||||||
}
|
getSettings()->enabledPlugins.setValue(val);
|
||||||
getSettings()->enabledPlugins.setValue(val);
|
getApp()->getPlugins()->reload(name);
|
||||||
getApp()->getPlugins()->reload(name);
|
});
|
||||||
});
|
pluginEntry->addRow(toggleButton);
|
||||||
pluginEntry->addRow(toggleButton);
|
if (meta.isValid())
|
||||||
|
{
|
||||||
|
toggleButton->setEnabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toggleButton->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *reloadButton = new QPushButton("Reload", this->dataFrame_);
|
auto *reloadButton = new QPushButton("Reload", this->dataFrame_);
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ public:
|
|||||||
getApp()->getPlugins()->openLibrariesFor(plugin);
|
getApp()->getPlugins()->openLibrariesFor(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<QString, std::unique_ptr<Plugin>> &plugins()
|
static std::map<QString, AnyPlugin> &plugins()
|
||||||
{
|
{
|
||||||
return getApp()->getPlugins()->plugins_;
|
return getApp()->getPlugins()->plugins_;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user