From 69625912b822f5e7630648d76af2408f013f23ba Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Mon, 28 Sep 2020 22:56:12 +0200 Subject: Add a method to retrieve the localized names of the implemented plugin interfaces. --- src/plugincontainer.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'src/plugincontainer.h') diff --git a/src/plugincontainer.h b/src/plugincontainer.h index d405198a..7eb59a0f 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -48,6 +48,16 @@ private: public: + /** + * @brief Retrieved the (localized) names of interfaces implemented by the given + * plugin. + * + * @param plugin The plugin to retrieve interface for. + * + * @return the (localized) names of interfaces implemented by this plugin. + */ + QStringList implementedInterfaces(const MOBase::IPlugin *plugin) const; + PluginContainer(OrganizerCore *organizer); virtual ~PluginContainer(); @@ -56,16 +66,38 @@ public: void loadPlugins(); void unloadPlugins(); + /** + * @brief Find the game plugin corresponding to the given name. + * + * @param name The name of the game to find a plugin for (as returned by + * IPluginGame::gameName()). + * + * @return the game plugin for the given name, or a null pointer if no + * plugin exists for this game. + */ MOBase::IPluginGame *managedGame(const QString &name) const; + /** + * @brief Retrieve the list of plugins of the given type. + * + * @return the list of plugins of the specified type. + * + * @tparam T The type of plugin to retrieve. + */ template const std::vector &plugins() const { typename boost::fusion::result_of::at_key::type temp = boost::fusion::at_key(m_Plugins); return temp; } + /** + * @return the preview generator. + */ const PreviewGenerator &previewGenerator() const; + /** + * @return the list of plugin file names, including proxied plugins. + */ QStringList pluginFileNames() const; public: // IPluginDiagnose interface -- cgit v1.3.1 From e9be288f7ce6ed7861a5ed381e2e16ae730524ad Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 29 Sep 2020 21:55:44 +0200 Subject: Add topImplementedInterface(). Minor cleaning. --- src/plugincontainer.cpp | 67 +++++++++++++++++++++++++++++++++++++------------ src/plugincontainer.h | 47 +++++++++++++++++++++++++++++++++- 2 files changed, 97 insertions(+), 17 deletions(-) (limited to 'src/plugincontainer.h') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 1af8487f..90a7e95f 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -24,7 +24,6 @@ namespace bf = boost::fusion; template struct PluginTypeName; -template <> struct PluginTypeName { static QString value() { return {}; } }; template <> struct PluginTypeName { static QString value() { return QT_TR_NOOP("Plugin"); } }; template <> struct PluginTypeName { static QString value() { return QT_TR_NOOP("Diagnose"); } }; template <> struct PluginTypeName { static QString value() { return QT_TR_NOOP("Game"); } }; @@ -35,28 +34,24 @@ template <> struct PluginTypeName { static QString value() template <> struct PluginTypeName { static QString value() { return QT_TR_NOOP("Proxy"); } }; template <> struct PluginTypeName { static QString value() { return QT_TR_NOOP("File Mapper"); } }; -QStringList PluginContainer::implementedInterfaces(const IPlugin* plugin) const +QStringList PluginContainer::implementedInterfaces(IPlugin* plugin) const { - // Find the correspond QObject - Can this be done safely with a cast? - auto& objects = bf::at_key(m_Plugins); - auto it = std::find_if(std::begin(objects), std::end(objects), [plugin](QObject *obj) { - return qobject_cast(obj) == plugin; - }); + // We need a QObject to be able to qobject_cast<> to the plugin types: + QObject* oPlugin = as_qobject(plugin); - if (it == std::end(objects)) { + if (!oPlugin) { return {}; } - // We need a QObject to be able to qobject_cast<> to the plugin types: - const QObject* oPlugin = *it; - // Find all the names: QStringList names; - bf::for_each(m_Plugins, [oPlugin, &names](auto const& p) { - using key_type = typename std::decay_t::first_type; - auto name = PluginTypeName::value(); - if (!name.isEmpty()) { - names.append(name); + boost::mp11::mp_for_each([oPlugin, &names](const auto *p) { + using plugin_type = std::decay_t; + if (qobject_cast(oPlugin)) { + auto name = PluginTypeName::value(); + if (!name.isEmpty()) { + names.append(name); + } } }); @@ -68,6 +63,30 @@ QStringList PluginContainer::implementedInterfaces(const IPlugin* plugin) const return names; } +QString PluginContainer::topImplementedInterface(IPlugin* plugin) const +{ + // We need a QObject to be able to qobject_cast<> to the plugin types: + QObject* oPlugin = as_qobject(plugin); + + if (!oPlugin) { + return {}; + } + + // Find all the names: + QString name; + boost::mp11::mp_for_each([oPlugin, &name](auto *p) { + using plugin_type = std::decay_t; + if (name.isEmpty() && qobject_cast(oPlugin)) { + auto tname = PluginTypeName::value(); + if (!tname.isEmpty()) { + name = tname; + } + } + }); + + return name; +} + PluginContainer::PluginContainer(OrganizerCore *organizer) : m_Organizer(organizer) , m_UserInterface(nullptr) @@ -79,6 +98,7 @@ PluginContainer::~PluginContainer() { unloadPlugins(); } + void PluginContainer::setUserInterface(IUserInterface *userInterface, QWidget *widget) { for (IPluginProxy *proxy : bf::at_key(m_Plugins)) { @@ -115,6 +135,21 @@ QStringList PluginContainer::pluginFileNames() const } +QObject* PluginContainer::as_qobject(MOBase::IPlugin* plugin) const +{ + // Find the correspond QObject - Can this be done safely with a cast? + auto& objects = bf::at_key(m_Plugins); + auto it = std::find_if(std::begin(objects), std::end(objects), [plugin](QObject* obj) { + return qobject_cast(obj) == plugin; + }); + + if (it == std::end(objects)) { + return nullptr; + } + + return *it; +} + bool PluginContainer::verifyPlugin(IPlugin *plugin) { if (plugin == nullptr) { diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 7eb59a0f..8672606b 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -19,6 +19,7 @@ class IUserInterface; #ifndef Q_MOC_RUN #include #include +#include #endif // Q_MOC_RUN #include @@ -46,6 +47,28 @@ private: static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1; + /** + * This typedefs defines the order of plugin interface. This is increasing order of + * importance". + * + * @note IPlugin is the less important interface, followed by IPluginDiagnose and + * IPluginFileMapper as those are usually implemented together with another interface. + * Other interfaces are in a alphabetical order since it is unlikely a plugin will + * implement multiple ones. + */ + using PluginTypeOrder = + boost::mp11::mp_transform< + std::add_pointer_t, + boost::mp11::mp_list< + MOBase::IPluginGame, MOBase::IPluginInstaller, MOBase::IPluginModPage, MOBase::IPluginPreview, + MOBase::IPluginProxy, MOBase::IPluginTool, MOBase::IPluginDiagnose, MOBase::IPluginFileMapper, + MOBase::IPlugin + > + >; + + static_assert( + boost::mp11::mp_size::value == boost::mp11::mp_size::value - 1); + public: /** @@ -56,7 +79,19 @@ public: * * @return the (localized) names of interfaces implemented by this plugin. */ - QStringList implementedInterfaces(const MOBase::IPlugin *plugin) const; + QStringList implementedInterfaces(MOBase::IPlugin *plugin) const; + + /** + * @brief Return the (localized) name of the most important interface implemented by + * the given plugin. + * + * The order of interfaces is defined in X. + * + * @param plugin The plugin to retrieve the interface for. + * + * @return the (localized) name of the most important interface implemented by this plugin. + */ + QString topImplementedInterface(MOBase::IPlugin* plugin) const; PluginContainer(OrganizerCore *organizer); virtual ~PluginContainer(); @@ -114,6 +149,16 @@ signals: private: + /** + * @brief Find the QObject* corresponding to the given plugin. + * + * @param plugin The plugin to find the QObject* for. + * + * @return a QObject* for the given plugin. + */ + QObject* as_qobject(MOBase::IPlugin* plugin) const; + + bool verifyPlugin(MOBase::IPlugin *plugin); void registerGame(MOBase::IPluginGame *game); bool registerPlugin(QObject *pluginObj, const QString &fileName); -- cgit v1.3.1 From 4b1a89648fffea98b447e4289dbc559d717d0b52 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Tue, 29 Sep 2020 22:47:38 +0200 Subject: Display plugins in a tree instead of a list in the settings dialog. --- src/plugincontainer.cpp | 89 ++++++++++++++++++++++++++----------------- src/plugincontainer.h | 43 ++++++++++++--------- src/settingsdialog.cpp | 2 +- src/settingsdialog.ui | 12 +++--- src/settingsdialogplugins.cpp | 73 +++++++++++++++++++++++++---------- src/settingsdialogplugins.h | 8 ++-- 6 files changed, 144 insertions(+), 83 deletions(-) (limited to 'src/plugincontainer.h') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 90a7e95f..60acc63c 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -34,6 +34,55 @@ template <> struct PluginTypeName { static QString value() template <> struct PluginTypeName { static QString value() { return QT_TR_NOOP("Proxy"); } }; template <> struct PluginTypeName { static QString value() { return QT_TR_NOOP("File Mapper"); } }; + +QStringList PluginContainer::pluginInterfaces() +{ + // Find all the names: + QStringList names; + boost::mp11::mp_for_each([&names](const auto* p) { + using plugin_type = std::decay_t; + auto name = PluginTypeName::value(); + if (!name.isEmpty()) { + names.append(name); + } + }); + + return names; +} + + +PluginContainer::PluginContainer(OrganizerCore *organizer) + : m_Organizer(organizer) + , m_UserInterface(nullptr) +{ +} + +PluginContainer::~PluginContainer() { + m_Organizer = nullptr; + unloadPlugins(); +} + + +void PluginContainer::setUserInterface(IUserInterface *userInterface, QWidget *widget) +{ + for (IPluginProxy *proxy : bf::at_key(m_Plugins)) { + proxy->setParentWidget(widget); + } + + if (userInterface != nullptr) { + for (IPluginModPage *modPage : bf::at_key(m_Plugins)) { + userInterface->registerModPage(modPage); + } + + for (IPluginTool *tool : bf::at_key(m_Plugins)) { + userInterface->registerPluginTool(tool); + } + } + + m_UserInterface = userInterface; +} + + QStringList PluginContainer::implementedInterfaces(IPlugin* plugin) const { // We need a QObject to be able to qobject_cast<> to the plugin types: @@ -45,7 +94,7 @@ QStringList PluginContainer::implementedInterfaces(IPlugin* plugin) const // Find all the names: QStringList names; - boost::mp11::mp_for_each([oPlugin, &names](const auto *p) { + boost::mp11::mp_for_each([oPlugin, &names](const auto* p) { using plugin_type = std::decay_t; if (qobject_cast(oPlugin)) { auto name = PluginTypeName::value(); @@ -53,7 +102,7 @@ QStringList PluginContainer::implementedInterfaces(IPlugin* plugin) const names.append(name); } } - }); + }); // If the plugin implements at least one interface other than IPlugin, remove IPlugin: if (names.size() > 1) { @@ -63,6 +112,7 @@ QStringList PluginContainer::implementedInterfaces(IPlugin* plugin) const return names; } + QString PluginContainer::topImplementedInterface(IPlugin* plugin) const { // We need a QObject to be able to qobject_cast<> to the plugin types: @@ -74,7 +124,7 @@ QString PluginContainer::topImplementedInterface(IPlugin* plugin) const // Find all the names: QString name; - boost::mp11::mp_for_each([oPlugin, &name](auto *p) { + boost::mp11::mp_for_each([oPlugin, &name](auto* p) { using plugin_type = std::decay_t; if (name.isEmpty() && qobject_cast(oPlugin)) { auto tname = PluginTypeName::value(); @@ -82,42 +132,11 @@ QString PluginContainer::topImplementedInterface(IPlugin* plugin) const name = tname; } } - }); + }); return name; } -PluginContainer::PluginContainer(OrganizerCore *organizer) - : m_Organizer(organizer) - , m_UserInterface(nullptr) -{ -} - -PluginContainer::~PluginContainer() { - m_Organizer = nullptr; - unloadPlugins(); -} - - -void PluginContainer::setUserInterface(IUserInterface *userInterface, QWidget *widget) -{ - for (IPluginProxy *proxy : bf::at_key(m_Plugins)) { - proxy->setParentWidget(widget); - } - - if (userInterface != nullptr) { - for (IPluginModPage *modPage : bf::at_key(m_Plugins)) { - userInterface->registerModPage(modPage); - } - - for (IPluginTool *tool : bf::at_key(m_Plugins)) { - userInterface->registerPluginTool(tool); - } - } - - m_UserInterface = userInterface; -} - QStringList PluginContainer::pluginFileNames() const { diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 8672606b..07363ee7 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -72,26 +72,13 @@ private: public: /** - * @brief Retrieved the (localized) names of interfaces implemented by the given - * plugin. + * @brief Retrieved the (localized) names of the various plugin interfaces. * - * @param plugin The plugin to retrieve interface for. - * - * @return the (localized) names of interfaces implemented by this plugin. + * @return the (localized) names of the various plugin interfaces. */ - QStringList implementedInterfaces(MOBase::IPlugin *plugin) const; + static QStringList pluginInterfaces(); - /** - * @brief Return the (localized) name of the most important interface implemented by - * the given plugin. - * - * The order of interfaces is defined in X. - * - * @param plugin The plugin to retrieve the interface for. - * - * @return the (localized) name of the most important interface implemented by this plugin. - */ - QString topImplementedInterface(MOBase::IPlugin* plugin) const; +public: PluginContainer(OrganizerCore *organizer); virtual ~PluginContainer(); @@ -125,6 +112,28 @@ public: return temp; } + /** + * @brief Retrieved the (localized) names of interfaces implemented by the given + * plugin. + * + * @param plugin The plugin to retrieve interface for. + * + * @return the (localized) names of interfaces implemented by this plugin. + */ + QStringList implementedInterfaces(MOBase::IPlugin* plugin) const; + + /** + * @brief Return the (localized) name of the most important interface implemented by + * the given plugin. + * + * The order of interfaces is defined in X. + * + * @param plugin The plugin to retrieve the interface for. + * + * @return the (localized) name of the most important interface implemented by this plugin. + */ + QString topImplementedInterface(MOBase::IPlugin* plugin) const; + /** * @return the preview generator. */ diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index daccfba9..87c7201d 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -43,7 +43,7 @@ SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings& setti m_tabs.push_back(std::unique_ptr(new DiagnosticsSettingsTab(settings, *this))); m_tabs.push_back(std::unique_ptr(new NexusSettingsTab(settings, *this))); m_tabs.push_back(std::unique_ptr(new SteamSettingsTab(settings, *this))); - m_tabs.push_back(std::unique_ptr(new PluginsSettingsTab(settings, *this))); + m_tabs.push_back(std::unique_ptr(new PluginsSettingsTab(settings, m_pluginContainer, *this))); m_tabs.push_back(std::unique_ptr(new WorkaroundsSettingsTab(settings, *this))); } diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 2ceb91db..97fa01bd 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -17,7 +17,7 @@ - 0 + 4 @@ -1042,16 +1042,18 @@ - + 0 0 - - true - + + + 1 + + diff --git a/src/settingsdialogplugins.cpp b/src/settingsdialogplugins.cpp index 41430f1f..cb306f36 100644 --- a/src/settingsdialogplugins.cpp +++ b/src/settingsdialogplugins.cpp @@ -3,33 +3,57 @@ #include "noeditdelegate.h" #include +#include "plugincontainer.h" + using MOBase::IPlugin; -PluginsSettingsTab::PluginsSettingsTab(Settings& s, SettingsDialog& d) +PluginsSettingsTab::PluginsSettingsTab(Settings& s, PluginContainer* pluginContainer, SettingsDialog& d) : SettingsTab(s, d) { ui->pluginSettingsList->setStyleSheet("QTreeWidget::item {padding-right: 10px;}"); + // Create top-level tree widget: + QStringList pluginInterfaces = pluginContainer->pluginInterfaces(); + pluginInterfaces.sort(Qt::CaseInsensitive); + std::map topItems; + for (QString interfaceName : pluginInterfaces) { + auto *item = new QTreeWidgetItem(ui->pluginsList, { interfaceName }); + item->setFlags(item->flags() & ~Qt::ItemIsSelectable); + auto font = item->font(0); + font.setBold(true); + item->setFont(0, font); + topItems[interfaceName] = item; + item->setExpanded(true); + } + ui->pluginsList->setHeaderHidden(true); + // display plugin settings QSet handledNames; for (IPlugin *plugin : settings().plugins().plugins()) { if (handledNames.contains(plugin->name())) continue; - QListWidgetItem *listItem = new QListWidgetItem(plugin->localizedName(), ui->pluginsList); - listItem->setData(ROLE_PLUGIN, QVariant::fromValue((void*)plugin)); - listItem->setData(ROLE_SETTINGS, settings().plugins().settings(plugin->name())); - listItem->setData(ROLE_DESCRIPTIONS, settings().plugins().descriptions(plugin->name())); - ui->pluginsList->addItem(listItem); + QTreeWidgetItem* listItem = new QTreeWidgetItem( + topItems.at(pluginContainer->topImplementedInterface(plugin)), { plugin->localizedName() }); + listItem->setData(0, ROLE_PLUGIN, QVariant::fromValue((void*)plugin)); + listItem->setData(0, ROLE_SETTINGS, settings().plugins().settings(plugin->name())); + listItem->setData(0, ROLE_DESCRIPTIONS, settings().plugins().descriptions(plugin->name())); + + if (handledNames.isEmpty()) { + listItem->setSelected(true); + } + handledNames.insert(plugin->name()); } + ui->pluginsList->sortByColumn(0, Qt::AscendingOrder); + // display plugin blacklist for (const QString &pluginName : settings().plugins().blacklist()) { ui->pluginBlacklist->addItem(pluginName); } QObject::connect( - ui->pluginsList, &QListWidget::currentItemChanged, + ui->pluginsList, &QTreeWidget::currentItemChanged, [&](auto* current, auto* previous) { on_pluginsList_currentItemChanged(current, previous); }); QShortcut *delShortcut = new QShortcut( @@ -37,18 +61,21 @@ PluginsSettingsTab::PluginsSettingsTab(Settings& s, SettingsDialog& d) QObject::connect(delShortcut, &QShortcut::activated, &dialog(), [&]{ deleteBlacklistItem(); }); } -IPlugin* PluginsSettingsTab::plugin(QListWidgetItem* pluginItem) const +IPlugin* PluginsSettingsTab::plugin(QTreeWidgetItem* pluginItem) const { - return static_cast(qvariant_cast(pluginItem->data(ROLE_PLUGIN))); + return static_cast(qvariant_cast(pluginItem->data(0, ROLE_PLUGIN))); } void PluginsSettingsTab::update() { // transfer plugin settings to in-memory structure - for (int i = 0; i < ui->pluginsList->count(); ++i) { - QListWidgetItem *item = ui->pluginsList->item(i); - settings().plugins().setSettings( - plugin(item)->name(), item->data(ROLE_SETTINGS).toMap()); + for (int i = 0; i < ui->pluginsList->topLevelItemCount(); ++i) { + auto *topLevelItem = ui->pluginsList->topLevelItem(i); + for (int j = 0; j < topLevelItem->childCount(); ++j) { + auto* item = topLevelItem->child(j); + settings().plugins().setSettings( + plugin(item)->name(), item->data(0, ROLE_SETTINGS).toMap()); + } } // set plugin blacklist @@ -67,18 +94,22 @@ void PluginsSettingsTab::closing() storeSettings(ui->pluginsList->currentItem()); } -void PluginsSettingsTab::on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +void PluginsSettingsTab::on_pluginsList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) { storeSettings(previous); + if (!current->data(0, ROLE_PLUGIN).isValid()) { + return; + } + ui->pluginSettingsList->clear(); - IPlugin *plugin = static_cast(current->data(Qt::UserRole).value()); + IPlugin* plugin = this->plugin(current); ui->authorLabel->setText(plugin->author()); ui->versionLabel->setText(plugin->version().canonicalString()); ui->descriptionLabel->setText(plugin->description()); - QVariantMap settings = current->data(ROLE_SETTINGS).toMap(); - QVariantMap descriptions = current->data(ROLE_DESCRIPTIONS).toMap(); + QVariantMap settings = current->data(0, ROLE_SETTINGS).toMap(); + QVariantMap descriptions = current->data(0, ROLE_DESCRIPTIONS).toMap(); ui->pluginSettingsList->setEnabled(settings.count() != 0); for (auto iter = settings.begin(); iter != settings.end(); ++iter) { QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList(iter.key())); @@ -109,16 +140,16 @@ void PluginsSettingsTab::deleteBlacklistItem() ui->pluginBlacklist->takeItem(ui->pluginBlacklist->currentIndex().row()); } -void PluginsSettingsTab::storeSettings(QListWidgetItem *pluginItem) +void PluginsSettingsTab::storeSettings(QTreeWidgetItem *pluginItem) { - if (pluginItem != nullptr) { - QVariantMap settings = pluginItem->data(ROLE_SETTINGS).toMap(); + if (pluginItem != nullptr && pluginItem->data(0, ROLE_PLUGIN).isValid()) { + QVariantMap settings = pluginItem->data(0, ROLE_SETTINGS).toMap(); for (int i = 0; i < ui->pluginSettingsList->topLevelItemCount(); ++i) { const QTreeWidgetItem *item = ui->pluginSettingsList->topLevelItem(i); settings[item->text(0)] = item->data(1, Qt::DisplayRole); } - pluginItem->setData(ROLE_SETTINGS, settings); + pluginItem->setData(0, ROLE_SETTINGS, settings); } } diff --git a/src/settingsdialogplugins.h b/src/settingsdialogplugins.h index b793e5fd..f9069be1 100644 --- a/src/settingsdialogplugins.h +++ b/src/settingsdialogplugins.h @@ -7,21 +7,21 @@ class PluginsSettingsTab : public SettingsTab { public: - PluginsSettingsTab(Settings& settings, SettingsDialog& dialog); + PluginsSettingsTab(Settings& settings, PluginContainer* pluginContainer, SettingsDialog& dialog); void update(); void closing() override; private: - void on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + void on_pluginsList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); void deleteBlacklistItem(); - void storeSettings(QListWidgetItem *pluginItem); + void storeSettings(QTreeWidgetItem *pluginItem); /** * @brief Retrieve the plugin associated to the given item in the list. * */ - MOBase::IPlugin* plugin(QListWidgetItem *pluginItem) const; + MOBase::IPlugin* plugin(QTreeWidgetItem *pluginItem) const; constexpr static int ROLE_PLUGIN = Qt::UserRole; constexpr static int ROLE_SETTINGS = Qt::UserRole + 1; -- cgit v1.3.1