From adf414e986c4d74b546ee817aeab62c18213f9a4 Mon Sep 17 00:00:00 2001 From: Eirik <60672612+Tapawingo@users.noreply.github.com> Date: Mon, 16 Feb 2026 13:24:19 +0100 Subject: [PATCH 1/2] Remove ide settings --- .claude/settings.local.json | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 994cb5a..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(git stash:*)", - "Bash(git checkout:*)", - "Bash(grep:*)", - "Bash(git diff:*)" - ] - } -} From d35e10cb0c284906f3c8a7ce2c2da7c2fafb3bfb Mon Sep 17 00:00:00 2001 From: Eirik <60672612+Tapawingo@users.noreply.github.com> Date: Mon, 16 Feb 2026 13:36:03 +0100 Subject: [PATCH 2/2] Implement "Enable All Mods" feature in ModManager and ModListWidget --- src/core/managers/ModManager.cpp | 110 +++++++++++++++++------- src/core/managers/ModManager.h | 1 + src/views/mod_manager/ModListWidget.cpp | 63 +++++++++++++- src/views/mod_manager/ModListWidget.h | 3 + 4 files changed, 145 insertions(+), 32 deletions(-) diff --git a/src/core/managers/ModManager.cpp b/src/core/managers/ModManager.cpp index 306a468..b7c4815 100644 --- a/src/core/managers/ModManager.cpp +++ b/src/core/managers/ModManager.cpp @@ -317,6 +317,49 @@ bool ModManager::disableMod(const QString &modId) { return true; } +bool ModManager::setAllModsEnabled(bool enabled) { + QList modsToProcess; + { + QMutexLocker locker(&m_modsMutex); + for (const ModInfo &mod : m_mods) { + if (mod.enabled != enabled) { + modsToProcess.append(mod); + } + } + } + + if (modsToProcess.isEmpty()) { + return true; + } + + bool anyFailed = false; + for (const ModInfo &mod : modsToProcess) { + bool ok = enabled ? copyModToPaks(mod) : removeModFromPaks(mod); + if (!ok) { + emit errorOccurred(tr("Failed to %1 mod: %2") + .arg(enabled ? tr("enable") : tr("disable"), mod.name)); + anyFailed = true; + continue; + } + + QMutexLocker locker(&m_modsMutex); + auto it = std::find_if(m_mods.begin(), m_mods.end(), + [&mod](const ModInfo &item) { return item.id == mod.id; }); + if (it != m_mods.end()) { + it->enabled = enabled; + } + } + + if (enabled) { + renumberEnabledMods(); + } + + saveMods(); + emit modsChanged(); + + return !anyFailed; +} + bool ModManager::setModPriority(const QString &modId, int priority) { QMutexLocker locker(&m_modsMutex); auto it = std::find_if(m_mods.begin(), m_mods.end(), @@ -626,53 +669,58 @@ void ModManager::renumberEnabledMods() { return; } - QMutexLocker locker(&m_modsMutex); - for (auto &mod : m_mods) { - if (!mod.enabled) { - continue; - } + struct EnabledModSnapshot { + QString id; + QString fileName; + QString numberedFileName; + int priority; + }; - QString oldNumberedName = mod.numberedFileName; - QString newNumberedName = generateNumberedFileName(mod.priority, mod.fileName); - - if (oldNumberedName == newNumberedName) { - QString expectedPath = paksPath + "/" + newNumberedName; - if (QFile::exists(expectedPath)) { + QList enabledMods; + { + QMutexLocker locker(&m_modsMutex); + enabledMods.reserve(m_mods.size()); + for (const ModInfo &mod : m_mods) { + if (!mod.enabled) { continue; } + enabledMods.append({mod.id, mod.fileName, mod.numberedFileName, mod.priority}); + } + } - QString sourcePath = m_modsStoragePath + "/" + mod.fileName; - locker.unlock(); + for (const auto &mod : enabledMods) { + QString newNumberedName = generateNumberedFileName(mod.priority, mod.fileName); + QString expectedPath = paksPath + "/" + newNumberedName; + QString sourcePath = m_modsStoragePath + "/" + mod.fileName; - if (QFile::exists(sourcePath)) { + if (mod.numberedFileName == newNumberedName) { + if (!QFile::exists(expectedPath) && QFile::exists(sourcePath)) { QFile::copy(sourcePath, expectedPath); - locker.relock(); - mod.numberedFileName = newNumberedName; + QMutexLocker locker(&m_modsMutex); + auto it = std::find_if(m_mods.begin(), m_mods.end(), + [&mod](const ModInfo &item) { return item.id == mod.id; }); + if (it != m_mods.end()) { + it->numberedFileName = newNumberedName; + } qDebug() << "Restored mod to paks:" << newNumberedName; - locker.unlock(); - } else { - locker.relock(); } continue; } - QString oldPath = paksPath + "/" + oldNumberedName; - QString newPath = paksPath + "/" + newNumberedName; - locker.unlock(); - + QString oldPath = paksPath + "/" + mod.numberedFileName; if (QFile::exists(oldPath)) { QFile::remove(oldPath); } - QString sourcePath = m_modsStoragePath + "/" + mod.fileName; if (QFile::exists(sourcePath)) { - QFile::copy(sourcePath, newPath); - locker.relock(); - mod.numberedFileName = newNumberedName; - qDebug() << "Renumbered mod:" << oldNumberedName << "->" << newNumberedName; - locker.unlock(); - } else { - locker.relock(); + QFile::copy(sourcePath, expectedPath); + QMutexLocker locker(&m_modsMutex); + auto it = std::find_if(m_mods.begin(), m_mods.end(), + [&mod](const ModInfo &item) { return item.id == mod.id; }); + if (it != m_mods.end()) { + it->numberedFileName = newNumberedName; + } + qDebug() << "Renumbered mod:" << mod.numberedFileName << "->" << newNumberedName; } } } diff --git a/src/core/managers/ModManager.h b/src/core/managers/ModManager.h index 3dfc284..c96bc27 100644 --- a/src/core/managers/ModManager.h +++ b/src/core/managers/ModManager.h @@ -31,6 +31,7 @@ class ModManager : public QObject { const QDateTime &uploadDate = QDateTime()); bool enableMod(const QString &modId); bool disableMod(const QString &modId); + bool setAllModsEnabled(bool enabled); bool setModPriority(const QString &modId, int priority); bool batchSetModPriorities(const QMap &priorityMap); bool updateModMetadata(const ModInfo &updatedMod); diff --git a/src/views/mod_manager/ModListWidget.cpp b/src/views/mod_manager/ModListWidget.cpp index 084ca8e..da5cc07 100644 --- a/src/views/mod_manager/ModListWidget.cpp +++ b/src/views/mod_manager/ModListWidget.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include ModListWidget::ModListWidget(QWidget *parent) : QWidget(parent) @@ -84,7 +86,13 @@ void ModListWidget::setupUi() { auto *titleLayout = new QHBoxLayout(); titleLayout->setSpacing(Theme::Spacing::MOD_LIST_TITLE_SPACING); - titleLayout->setContentsMargins(0, 0, 0, 0); + titleLayout->setContentsMargins(Theme::Spacing::MOD_ROW_PADDING_HORIZONTAL, 0, 0, 0); + + m_enableAllCheckBox = new QCheckBox(this); + m_enableAllCheckBox->setObjectName("modEnableAllCheckBox"); + m_enableAllCheckBox->setTristate(true); + m_enableAllCheckBox->setCursor(Qt::PointingHandCursor); + m_enableAllCheckBox->setToolTip(tr("Enable or disable all mods")); m_titleLabel = new QLabel(tr("Installed Mods:"), this); m_titleLabel->setObjectName("modListTitle"); @@ -96,6 +104,7 @@ void ModListWidget::setupUi() { m_checkUpdatesButton->setObjectName("checkUpdatesButton"); m_checkUpdatesButton->setCursor(Qt::PointingHandCursor); + titleLayout->addWidget(m_enableAllCheckBox); titleLayout->addWidget(m_titleLabel); titleLayout->addWidget(m_modCountLabel); titleLayout->addStretch(); @@ -103,6 +112,8 @@ void ModListWidget::setupUi() { connect(m_checkUpdatesButton, &QPushButton::clicked, this, &ModListWidget::onCheckUpdatesClicked); + connect(m_enableAllCheckBox, &QCheckBox::clicked, + this, &ModListWidget::onEnableAllClicked); m_searchContainer = new QWidget(this); auto *searchLayout = new QHBoxLayout(m_searchContainer); @@ -174,6 +185,12 @@ void ModListWidget::refreshModList() { m_modList->clear(); QList mods = m_modManager->getMods(); + int enabledCount = 0; + for (const ModInfo &mod : mods) { + if (mod.enabled) { + enabledCount++; + } + } QList filteredMods; filteredMods.reserve(mods.size()); for (const ModInfo &mod : mods) { @@ -198,6 +215,23 @@ void ModListWidget::refreshModList() { m_modCountLabel->setText(QString::number(mods.size()) + " Total"); } + if (m_enableAllCheckBox) { + QSignalBlocker blocker(m_enableAllCheckBox); + if (mods.isEmpty()) { + m_enableAllCheckBox->setEnabled(false); + m_enableAllCheckBox->setCheckState(Qt::Unchecked); + } else { + m_enableAllCheckBox->setEnabled(true); + if (enabledCount == 0) { + m_enableAllCheckBox->setCheckState(Qt::Unchecked); + } else if (enabledCount == mods.size()) { + m_enableAllCheckBox->setCheckState(Qt::Checked); + } else { + m_enableAllCheckBox->setCheckState(Qt::PartiallyChecked); + } + } + } + for (int i = 0; i < filteredMods.size(); ++i) { const ModInfo &mod = filteredMods[i]; auto *modRow = new ModRowWidget(mod, this); @@ -275,6 +309,10 @@ void ModListWidget::changeEvent(QEvent *event) { void ModListWidget::retranslateUi() { if (m_titleLabel) m_titleLabel->setText(tr("Installed Mods:")); if (m_checkUpdatesButton) m_checkUpdatesButton->setText(tr("Check for Updates")); + if (m_enableAllCheckBox) { + m_enableAllCheckBox->setText(QString()); + m_enableAllCheckBox->setToolTip(tr("Enable or disable all mods")); + } } void ModListWidget::resizeEvent(QResizeEvent *event) { @@ -318,6 +356,29 @@ void ModListWidget::onModEnabledChanged(const QString &modId, bool enabled) { } } +void ModListWidget::onEnableAllClicked() { + if (m_updating || !m_modManager || !m_enableAllCheckBox) { + return; + } + + QList mods = m_modManager->getMods(); + if (mods.isEmpty()) { + return; + } + + int enabledCount = 0; + for (const ModInfo &mod : mods) { + if (mod.enabled) { + enabledCount++; + } + } + + bool enableAll = enabledCount < mods.size(); + m_enableAllCheckBox->setEnabled(false); + m_modManager->setAllModsEnabled(enableAll); + m_enableAllCheckBox->setEnabled(true); +} + void ModListWidget::onAddModClicked() { if (!m_modManager || !m_modalManager) { return; diff --git a/src/views/mod_manager/ModListWidget.h b/src/views/mod_manager/ModListWidget.h index 3fe8e81..2a07766 100644 --- a/src/views/mod_manager/ModListWidget.h +++ b/src/views/mod_manager/ModListWidget.h @@ -9,6 +9,7 @@ #include #include #include +#include class NexusModsClient; class NexusModsAuth; @@ -63,6 +64,7 @@ public slots: private slots: void onModsChanged(); void onModEnabledChanged(const QString &modId, bool enabled); + void onEnableAllClicked(); void onItemsReordered(); void updateLoadingAnimation(); void onRenameRequested(const QString &modId); @@ -107,6 +109,7 @@ private slots: QLabel *m_loadingLabel; QLabel *m_modCountLabel; QPushButton *m_checkUpdatesButton; + QCheckBox *m_enableAllCheckBox = nullptr; QTimer *m_loadingTimer; QWidget *m_searchContainer = nullptr; QLineEdit *m_searchEdit = nullptr;