diff --git a/CMakeLists.txt b/CMakeLists.txt index bb8591a4..92f7b906 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,8 @@ set(MO2_CMAKE_DEPRECATED_UIBASE_INCLUDE ON) project(organizer) +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) + # if MO2_INSTALL_IS_BIN is set, this means that we should install directly into the # installation prefix, without the bin/ subfolder, typically for a standalone build # to update an existing install diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d24a9c6e..53ba50cf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -121,7 +121,7 @@ mo2_add_filter(NAME src/categories GROUPS mo2_add_filter(NAME src/core GROUPS archivefiletree - githubpp + github inibakery installationmanager nexusinterface @@ -308,6 +308,7 @@ mo2_add_filter(NAME src/settingsdialog GROUPS settingsdialogpaths settingsdialogextensions settingsdialogextensionrow + settingsdialogextensioninfo settingsdialogworkarounds settingsdialogmodlist settingsdialogtheme diff --git a/src/extensionmanager.cpp b/src/extensionmanager.cpp index e894abe9..3df56f7d 100644 --- a/src/extensionmanager.cpp +++ b/src/extensionmanager.cpp @@ -24,9 +24,8 @@ void ExtensionManager::loadExtensions(fs::path const& directory) continue; } - log::debug("extension correctly loaded from '{}': {}, {}", - entry.path().native(), extension->metadata().identifier(), - extension->metadata().type()); + log::debug("extension data loaded from '{}': {}, {}", entry.path().native(), + extension->metadata().identifier(), extension->metadata().type()); triggerWatchers(*extension); m_extensions.push_back(std::move(extension)); diff --git a/src/organizer_en.ts b/src/organizer_en.ts index b25ece0a..35d281d5 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -1758,6 +1758,54 @@ Right now the only case I know of where this needs to be overwritten is for the + + ExtensionListInfoWidget + + + Form + + + + + Author: + + + + + Version: + + + + + Description: + + + + + Enabled + + + + + Key + + + + + Value + + + + + No plugin found. + + + + + Translation and theme extensions cannot be disabled. + + + ExtensionListItemWidget @@ -8520,7 +8568,7 @@ If you disable this feature, MO will only display official DLCs this way. Please - + ... @@ -8657,7 +8705,7 @@ If you disable this feature, MO will only display official DLCs this way. Please - + Options @@ -8718,119 +8766,84 @@ If you disable this feature, MO will only display official DLCs this way. Please - - Author: - - - - - Version: - - - - - Description: - - - - - Enabled - - - - - Key - - - - - Value - - - - - No plugin found. - - - - + Blacklisted Plugins (use <del> to remove): - + Workarounds - + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) - + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) Uncheck this if you want to use Mod Organizer with total conversions (like Nehrim) but be aware that the game will crash if required files are not enabled. - + Force-enable game files - + Enable parsing of Archives. This is an Experimental Feature. Has negative effects on performance and known incorrectness. - + <html><head/><body><p>By default, MO will parse archive files (BSA, BA2) to calculate conflicts between the contents of the archive files and other loose files. This process has a noticeable cost in performance.</p><p>This feature should not be confused with the archive management feature offered by MO1. MO2 will only show conflicts with archives and will NOT load them into the game or program.</p><p>If you disable this feature, MO will only display conflicts between loose files.</p></body></html> - + Enable archives parsing (experimental) - - + + Disable this to prevent the GUI from being locked when running an executable. This may result in abnormal behavior. - + Lock GUI when running executable - + Steam - + Password - + Username - + Steam App ID - + The Steam AppID for your game - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -8846,69 +8859,69 @@ p, li { white-space: pre-wrap; } - + Network - + Disable automatic internet features - + Disable automatic internet features. This does not affect features that are explicitly invoked by the user (like checking mods for updates, endorsing, opening the web browser) - + Offline Mode - + Use a proxy for network connections. - + Use a proxy for network connections. This uses the system-wide settings which can be configured in Internet Explorer. Please note that MO will start up a few seconds slower on some systems when using a proxy. - + Use System HTTP Proxy - - - - - - + + + + + + Use "%1" as a placeholder for the URL. - + Custom browser - - + + Resets the window geometries for all windows. This can be useful if a window becomes too small or too large, if a column becomes too thin or too wide, and in similar situations. - + Reset Window Geometries - - + + For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles. For the other games this is not a sufficient replacement for AI! @@ -8916,7 +8929,7 @@ p, li { white-space: pre-wrap; } - + Add executables to the blacklist to prevent them from accessing the virtual file system. This is useful to prevent unintended programs from being hooked. Hooking unintended @@ -8925,17 +8938,17 @@ programs you are intentionally running. - + Add executables to the blacklist to prevent them from accessing the virtual file system. This is useful to prevent unintended programs from being hooked. Hooking unintended programs may affect the execution of these programs or the programs you are intentionally running. - + Executables Blacklist - + Back-date BSAs @@ -8945,54 +8958,54 @@ programs you are intentionally running. - - + + Files to skip or ignore from the virtual file system. - + Skip File Suffixes - - + + Directories to skip or ignore from the virtual file system. - + Skip Directories - + These are workarounds for problems with Mod Organizer. Please make sure you read the help text before changing anything here. - + Diagnostics - + Logs and Crashes - + Log Level - + Decides the amount of data printed to "ModOrganizer.log" - + Decides the amount of data printed to "ModOrganizer.log". "Debug" produces very useful information for finding problems. There is usually no noteworthy performance impact but the file may become rather large. If this is a problem you may prefer the "Info" level for regular use. On the "Error" level the log file usually remains empty. @@ -9000,17 +9013,17 @@ programs you are intentionally running. - + Crash Dumps - + Decides which type of crash dumps are collected when injected processes crash. - + Decides which type of crash dumps are collected when injected processes crash. "None" Disables the generation of crash dumps by MO. @@ -9021,17 +9034,17 @@ programs you are intentionally running. - + Max Dumps To Keep - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. Set "Crash Dumps" above to None to disable crash dump collection. @@ -9039,22 +9052,22 @@ programs you are intentionally running. - + Integrated LOOT - + LOOT Log Level - + Click a link to open the location - + Logs and crash dumps are stored under your current instance in the <a href="LOGS_FULL_PATH">LOGS_DIR</a> and <a href="DUMPS_FULL_PATH">DUMPS_DIR</a> folders. diff --git a/src/settings.cpp b/src/settings.cpp index a0f6d34c..71f72b28 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1345,32 +1345,32 @@ void PluginSettings::registerPlugin(IPlugin* plugin) m_PluginSettings.insert(plugin->name(), QVariantMap()); m_PluginDescriptions.insert(plugin->name(), QVariantMap()); - for (const PluginSetting& setting : plugin->settings()) { - const QString settingName = plugin->name() + "/" + setting.key; + for (const auto& setting : plugin->settings()) { + const auto settingPath = plugin->name() + "/" + setting.name(); - QVariant temp = get(m_Settings, "Plugins", settingName, QVariant()); + QVariant temp = get(m_Settings, "Plugins", settingPath, QVariant()); // No previous enabled? Skip. - if (setting.key == "enabled" && (!temp.isValid() || !temp.canConvert())) { + if (setting.name() == "enabled" && (!temp.isValid() || !temp.canConvert())) { continue; } if (!temp.isValid()) { - temp = setting.defaultValue; - } else if (!temp.convert(setting.defaultValue.type())) { + temp = setting.defaultValue(); + } else if (!temp.convert(setting.defaultValue().metaType())) { log::warn("failed to interpret \"{}\" as correct type for \"{}\" in plugin " "\"{}\", using default", - temp.toString(), setting.key, plugin->name()); + temp.toString(), setting.name(), plugin->name()); - temp = setting.defaultValue; + temp = setting.defaultValue(); } - m_PluginSettings[plugin->name()][setting.key] = temp; + m_PluginSettings[plugin->name()][setting.name()] = temp; - m_PluginDescriptions[plugin->name()][setting.key] = + m_PluginDescriptions[plugin->name()][setting.name()] = QString("%1 (default: %2)") - .arg(setting.description) - .arg(setting.defaultValue.toString()); + .arg(setting.description()) + .arg(setting.defaultValue().toString()); } // Handle previous "enabled" settings: diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 330087c7..80c4fffe 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -7,7 +7,7 @@ 0 0 820 - 592 + 607 @@ -17,7 +17,7 @@ - 0 + 5 @@ -44,8 +44,8 @@ 0 0 - 761 - 550 + 766 + 611 @@ -1052,8 +1052,8 @@ If you disable this feature, MO will only display official DLCs this way. Please 0 0 - 761 - 515 + 766 + 548 @@ -1537,7 +1537,7 @@ If you disable this feature, MO will only display official DLCs this way. Please - + @@ -1547,8 +1547,8 @@ If you disable this feature, MO will only display official DLCs this way. Please - - + + 0 @@ -1561,130 +1561,6 @@ If you disable this feature, MO will only display official DLCs this way. Please 0 - - - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - 6 - - - - - Author: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - Version: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - Description: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - Enabled - - - - - - - - - - 0 - - - false - - - false - - - false - - - false - - - 170 - - - - Key - - - - - Value - - - - - - - - No plugin found. - - - Qt::AlignCenter - - - @@ -1736,8 +1612,8 @@ If you disable this feature, MO will only display official DLCs this way. Please 0 0 - 778 - 475 + 780 + 489 @@ -2035,9 +1911,6 @@ p, li { white-space: pre-wrap; } - - Qt::Orientation::Horizontal - 40 @@ -2330,6 +2203,12 @@ programs you are intentionally running. QTableWidget
colortable.h
+ + ExtensionListInfoWidget + QWidget +
settingsdialogextensioninfo.h
+ 1 +
languageBox @@ -2348,7 +2227,6 @@ programs you are intentionally running. browseOverwriteDirBtn managedGameDirEdit browseGameDirBtn - pluginSettingsList lockGUIBox diff --git a/src/settingsdialogextensioninfo.cpp b/src/settingsdialogextensioninfo.cpp new file mode 100644 index 00000000..9a98745e --- /dev/null +++ b/src/settingsdialogextensioninfo.cpp @@ -0,0 +1,48 @@ +#include "settingsdialogextensioninfo.h" + +#include "ui_settingsdialogextensioninfo.h" + +#include + +#include + +using namespace MOBase; + +ExtensionListInfoWidget::ExtensionListInfoWidget(QWidget* parent) + : QWidget(parent), ui{new Ui::ExtensionListInfoWidget()} +{ + ui->setupUi(this); + + ui->authorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + ui->authorLabel->setOpenExternalLinks(true); +} + +void ExtensionListInfoWidget::setExtension(const IExtension& extension) +{ + m_extension = &extension; + + const auto& metadata = m_extension->metadata(); + const auto& author = metadata.author(); + + if (author.homepage().isEmpty()) { + ui->authorLabel->setText(metadata.author().name()); + } else { + ui->authorLabel->setText(QString::fromStdString( + std::format("{}", author.homepage(), author.name()))); + } + ui->descriptionLabel->setText(metadata.description()); + ui->versionLabel->setText(metadata.version().string(Version::FormatCondensed)); + + if (metadata.type() == ExtensionType::THEME || + metadata.type() == ExtensionType::TRANSLATION) { + ui->enabledCheckbox->setChecked(true); + ui->enabledCheckbox->setEnabled(false); + ui->enabledCheckbox->setToolTip( + tr("Translation and theme extensions cannot be disabled.")); + } else { + // TODO: + // ui->enabledCheckbox->setChecked(); + ui->enabledCheckbox->setEnabled(true); + ui->enabledCheckbox->setToolTip(QString()); + } +} diff --git a/src/settingsdialogextensioninfo.h b/src/settingsdialogextensioninfo.h new file mode 100644 index 00000000..3c013a9f --- /dev/null +++ b/src/settingsdialogextensioninfo.h @@ -0,0 +1,29 @@ +#ifndef SETTINGSDIALOGEXTENSIONINFO_H +#define SETTINGSDIALOGEXTENSIONINFO_H + +#include + +#include + +namespace Ui +{ +class ExtensionListInfoWidget; +} + +class ExtensionListInfoWidget : public QWidget +{ +public: + ExtensionListInfoWidget(QWidget* parent = nullptr); + + // set the extension to display + // + void setExtension(const MOBase::IExtension& extension); + +private: + Ui::ExtensionListInfoWidget* ui; + + // currently displayed extension (default to nullptr) + const MOBase::IExtension* m_extension{nullptr}; +}; + +#endif diff --git a/src/settingsdialogextensioninfo.ui b/src/settingsdialogextensioninfo.ui new file mode 100644 index 00000000..3951e303 --- /dev/null +++ b/src/settingsdialogextensioninfo.ui @@ -0,0 +1,145 @@ + + + ExtensionListInfoWidget + + + + 0 + 0 + 400 + 410 + + + + Form + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 6 + + + + + Author: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Version: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Description: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Enabled + + + + + + + + + + 0 + + + false + + + false + + + false + + + false + + + 170 + + + + Key + + + + + Value + + + + + + + + No plugin found. + + + Qt::AlignCenter + + + + + + + + diff --git a/src/settingsdialogextensionrow.h b/src/settingsdialogextensionrow.h index 80a489f7..ce65278d 100644 --- a/src/settingsdialogextensionrow.h +++ b/src/settingsdialogextensionrow.h @@ -15,6 +15,10 @@ class ExtensionListItemWidget : public QWidget public: ExtensionListItemWidget(MOBase::IExtension const& extension); + // retrieve the extension associated with this widget + // + const auto& extension() const { return *m_extension; } + private: Ui::ExtensionListItemWidget* ui; const MOBase::IExtension* m_extension; diff --git a/src/settingsdialogextensions.cpp b/src/settingsdialogextensions.cpp index 300655e8..9a86e76f 100644 --- a/src/settingsdialogextensions.cpp +++ b/src/settingsdialogextensions.cpp @@ -10,14 +10,6 @@ using namespace MOBase; -struct PluginExtensionComparator -{ - bool operator()(const PluginExtension* lhs, const PluginExtension* rhs) const - { - return lhs->metadata().name().compare(rhs->metadata().name(), Qt::CaseInsensitive); - } -}; - ExtensionsSettingsTab::ExtensionsSettingsTab(Settings& s, ExtensionManager& extensionManager, PluginManager& pluginManager, @@ -45,6 +37,14 @@ ExtensionsSettingsTab::ExtensionsSettingsTab(Settings& s, ui->extensionsList->setItemWidget(item, widget); } + QObject::connect(ui->extensionsList, &QListWidget::currentItemChanged, + [this](QListWidgetItem* current, QListWidgetItem*) { + if (auto* widget = dynamic_cast( + ui->extensionsList->itemWidget(current))) { + extensionSelected(widget->extension()); + } + }); + // ui->pluginSettingsList->setStyleSheet("QTreeWidget::item {padding-right: 10px;}"); // ui->pluginsList->setHeaderHidden(true); @@ -83,11 +83,6 @@ ExtensionsSettingsTab::ExtensionsSettingsTab(Settings& s, // m_filter.setEdit(ui->pluginFilterEdit); - // QObject::connect(ui->pluginsList, &QTreeWidget::currentItemChanged, - // [&](auto* current, auto* previous) { - // on_pluginsList_currentItemChanged(current, previous); - // }); - // QShortcut* delShortcut = // new QShortcut(QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); // QObject::connect(delShortcut, &QShortcut::activated, &dialog(), [&] { @@ -205,6 +200,17 @@ void ExtensionsSettingsTab::closing() { // storeSettings(ui->pluginsList->currentItem()); } + +void ExtensionsSettingsTab::extensionSelected(IExtension const& extension) +{ + // TODO: store current settings in-memory for save later OR save live when modifying? + if (m_currentExtension) { + } + + m_currentExtension = &extension; + ui->infoWidget->setExtension(extension); +} + // // void PluginsSettingsTab::on_pluginsList_currentItemChanged(QTreeWidgetItem* current, // QTreeWidgetItem* previous) diff --git a/src/settingsdialogextensions.h b/src/settingsdialogextensions.h index 96eb6ceb..70760d0b 100644 --- a/src/settingsdialogextensions.h +++ b/src/settingsdialogextensions.h @@ -43,6 +43,8 @@ private slots: // */ // MOBase::IPlugin* plugin(QListWidgetItem* pluginItem) const; + void extensionSelected(MOBase::IExtension const& extension); + enum { PluginRole = Qt::UserRole, @@ -54,6 +56,9 @@ private slots: ExtensionManager* m_extensionManager; PluginManager* m_pluginManager; + // the currently selected extension + const MOBase::IExtension* m_currentExtension{nullptr}; + MOBase::FilterWidget m_filter; };