diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index b8db803b..e8e2ebd9 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -717,6 +717,8 @@ SET(LAUNCHER_SOURCES ui/pages/BasePageProvider.h # GUI - instance pages + ui/pages/instance/ExternalResourcesPage.cpp + ui/pages/instance/ExternalResourcesPage.h ui/pages/instance/GameOptionsPage.cpp ui/pages/instance/GameOptionsPage.h ui/pages/instance/VersionPage.cpp @@ -924,7 +926,7 @@ qt5_wrap_ui(LAUNCHER_UI ui/pages/global/ProxyPage.ui ui/pages/global/MinecraftPage.ui ui/pages/global/ExternalToolsPage.ui - ui/pages/instance/ModFolderPage.ui + ui/pages/instance/ExternalResourcesPage.ui ui/pages/instance/NotesPage.ui ui/pages/instance/LogPage.ui ui/pages/instance/ServersPage.ui diff --git a/launcher/InstancePageProvider.h b/launcher/InstancePageProvider.h index 357157d0..78fb7016 100644 --- a/launcher/InstancePageProvider.h +++ b/launcher/InstancePageProvider.h @@ -33,10 +33,10 @@ public: values.append(new LogPage(inst)); std::shared_ptr onesix = std::dynamic_pointer_cast(inst); values.append(new VersionPage(onesix.get())); - auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods"); + auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList()); modsPage->setFilter("%1 (*.zip *.jar *.litemod)"); values.append(modsPage); - values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods")); + values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList())); values.append(new ResourcePackPage(onesix.get())); values.append(new TexturePackPage(onesix.get())); values.append(new ShaderPackPage(onesix.get())); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp new file mode 100644 index 00000000..02eeae3d --- /dev/null +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -0,0 +1,297 @@ +#include "ExternalResourcesPage.h" +#include "ui_ExternalResourcesPage.h" + +#include "DesktopServices.h" +#include "Version.h" +#include "minecraft/mod/ModFolderModel.h" +#include "ui/GuiUtil.h" + +#include +#include + +namespace { +// FIXME: wasteful +void RemoveThePrefix(QString& string) +{ + QRegularExpression regex(QStringLiteral("^(?:the|teh) +"), QRegularExpression::CaseInsensitiveOption); + string.remove(regex); + string = string.trimmed(); +} +} // namespace + +class SortProxy : public QSortFilterProxyModel { + public: + explicit SortProxy(QObject* parent = nullptr) : QSortFilterProxyModel(parent) {} + + protected: + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override + { + ModFolderModel* model = qobject_cast(sourceModel()); + if (!model) + return false; + + const auto& mod = model->at(source_row); + + if (mod.name().contains(filterRegExp())) + return true; + if (mod.description().contains(filterRegExp())) + return true; + + for (auto& author : mod.authors()) { + if (author.contains(filterRegExp())) { + return true; + } + } + + return false; + } + + bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override + { + ModFolderModel* model = qobject_cast(sourceModel()); + if (!model || !source_left.isValid() || !source_right.isValid() || source_left.column() != source_right.column()) { + return QSortFilterProxyModel::lessThan(source_left, source_right); + } + + // we are now guaranteed to have two valid indexes in the same column... we love the provided invariants unconditionally and + // proceed. + + auto column = (ModFolderModel::Columns) source_left.column(); + bool invert = false; + switch (column) { + // GH-2550 - sort by enabled/disabled + case ModFolderModel::ActiveColumn: { + auto dataL = source_left.data(Qt::CheckStateRole).toBool(); + auto dataR = source_right.data(Qt::CheckStateRole).toBool(); + if (dataL != dataR) + return dataL > dataR; + + // fallthrough + invert = sortOrder() == Qt::DescendingOrder; + } + // GH-2722 - sort mod names in a way that discards "The" prefixes + case ModFolderModel::NameColumn: { + auto dataL = model->data(model->index(source_left.row(), ModFolderModel::NameColumn)).toString(); + RemoveThePrefix(dataL); + auto dataR = model->data(model->index(source_right.row(), ModFolderModel::NameColumn)).toString(); + RemoveThePrefix(dataR); + + auto less = dataL.compare(dataR, sortCaseSensitivity()); + if (less != 0) + return invert ? (less > 0) : (less < 0); + + // fallthrough + invert = sortOrder() == Qt::DescendingOrder; + } + // GH-2762 - sort versions by parsing them as versions + case ModFolderModel::VersionColumn: { + auto dataL = Version(model->data(model->index(source_left.row(), ModFolderModel::VersionColumn)).toString()); + auto dataR = Version(model->data(model->index(source_right.row(), ModFolderModel::VersionColumn)).toString()); + return invert ? (dataL > dataR) : (dataL < dataR); + } + default: { + return QSortFilterProxyModel::lessThan(source_left, source_right); + } + } + } +}; + +ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared_ptr model, QWidget* parent) + : QMainWindow(parent), m_instance(instance), ui(new Ui::ExternalResourcesPage), m_model(model) +{ + ui->setupUi(this); + + runningStateChanged(m_instance && m_instance->isRunning()); + + ui->actionsToolbar->insertSpacer(ui->actionViewConfigs); + + m_filterModel = new SortProxy(this); + m_filterModel->setDynamicSortFilter(true); + m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive); + m_filterModel->setSourceModel(m_model.get()); + m_filterModel->setFilterKeyColumn(-1); + ui->treeView->setModel(m_filterModel); + + ui->treeView->installEventFilter(this); + ui->treeView->sortByColumn(1, Qt::AscendingOrder); + ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); + + // The default function names by Qt are pretty ugly, so let's just connect the actions manually, + // to make it easier to read :) + connect(ui->actionAddItem, &QAction::triggered, this, &ExternalResourcesPage::addItem); + connect(ui->actionRemoveItem, &QAction::triggered, this, &ExternalResourcesPage::removeItem); + connect(ui->actionEnableItem, &QAction::triggered, this, &ExternalResourcesPage::enableItem); + connect(ui->actionDisableItem, &QAction::triggered, this, &ExternalResourcesPage::disableItem); + connect(ui->actionViewConfigs, &QAction::triggered, this, &ExternalResourcesPage::viewConfigs); + connect(ui->actionViewFolder, &QAction::triggered, this, &ExternalResourcesPage::viewFolder); + + connect(ui->treeView, &ModListView::customContextMenuRequested, this, &ExternalResourcesPage::ShowContextMenu); + connect(ui->treeView, &ModListView::activated, this, &ExternalResourcesPage::itemActivated); + + auto selection_model = ui->treeView->selectionModel(); + connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current); + connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged); + connect(m_instance, &BaseInstance::runningStatusChanged, this, &ExternalResourcesPage::runningStateChanged); +} + +ExternalResourcesPage::~ExternalResourcesPage() +{ + m_model->stopWatching(); + delete ui; +} + +void ExternalResourcesPage::itemActivated(const QModelIndex&) +{ + if (!m_controlsEnabled) + return; + + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + m_model->setModStatus(selection.indexes(), ModFolderModel::Toggle); +} + +QMenu* ExternalResourcesPage::createPopupMenu() +{ + QMenu* filteredMenu = QMainWindow::createPopupMenu(); + filteredMenu->removeAction(ui->actionsToolbar->toggleViewAction()); + return filteredMenu; +} + +void ExternalResourcesPage::ShowContextMenu(const QPoint& pos) +{ + auto menu = ui->actionsToolbar->createContextMenu(this, tr("Context menu")); + menu->exec(ui->treeView->mapToGlobal(pos)); + delete menu; +} + +void ExternalResourcesPage::openedImpl() +{ + m_model->startWatching(); +} + +void ExternalResourcesPage::closedImpl() +{ + m_model->stopWatching(); +} + +void ExternalResourcesPage::retranslate() +{ + ui->retranslateUi(this); +} + +void ExternalResourcesPage::filterTextChanged(const QString& newContents) +{ + m_viewFilter = newContents; + m_filterModel->setFilterFixedString(m_viewFilter); +} + +void ExternalResourcesPage::runningStateChanged(bool running) +{ + if (m_controlsEnabled == !running) + return; + + m_controlsEnabled = !running; + ui->actionAddItem->setEnabled(m_controlsEnabled); + ui->actionDisableItem->setEnabled(m_controlsEnabled); + ui->actionEnableItem->setEnabled(m_controlsEnabled); + ui->actionRemoveItem->setEnabled(m_controlsEnabled); +} + +bool ExternalResourcesPage::shouldDisplay() const +{ + return true; +} + +bool ExternalResourcesPage::listFilter(QKeyEvent* keyEvent) +{ + switch (keyEvent->key()) { + case Qt::Key_Delete: + removeItem(); + return true; + case Qt::Key_Plus: + addItem(); + return true; + default: + break; + } + return QWidget::eventFilter(ui->treeView, keyEvent); +} + +bool ExternalResourcesPage::eventFilter(QObject* obj, QEvent* ev) +{ + if (ev->type() != QEvent::KeyPress) + return QWidget::eventFilter(obj, ev); + + QKeyEvent* keyEvent = static_cast(ev); + if (obj == ui->treeView) + return listFilter(keyEvent); + + return QWidget::eventFilter(obj, ev); +} + +void ExternalResourcesPage::addItem() +{ + if (!m_controlsEnabled) + return; + + + auto list = GuiUtil::BrowseForFiles( + helpPage(), tr("Select %1", "Select whatever type of files the page contains. Example: 'Loader Mods'").arg(displayName()), + m_fileSelectionFilter.arg(displayName()), APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget()); + + if (!list.isEmpty()) { + for (auto filename : list) { + m_model->installMod(filename); + } + } +} + +void ExternalResourcesPage::removeItem() +{ + if (!m_controlsEnabled) + return; + + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + m_model->deleteMods(selection.indexes()); +} + +void ExternalResourcesPage::enableItem() +{ + if (!m_controlsEnabled) + return; + + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + m_model->setModStatus(selection.indexes(), ModFolderModel::Enable); +} + +void ExternalResourcesPage::disableItem() +{ + if (!m_controlsEnabled) + return; + + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + m_model->setModStatus(selection.indexes(), ModFolderModel::Disable); +} + +void ExternalResourcesPage::viewConfigs() +{ + DesktopServices::openDirectory(m_instance->instanceConfigFolder(), true); +} + +void ExternalResourcesPage::viewFolder() +{ + DesktopServices::openDirectory(m_model->dir().absolutePath(), true); +} + +void ExternalResourcesPage::current(const QModelIndex& current, const QModelIndex& previous) +{ + if (!current.isValid()) { + ui->frame->clear(); + return; + } + + auto sourceCurrent = m_filterModel->mapToSource(current); + int row = sourceCurrent.row(); + Mod& m = m_model->operator[](row); + ui->frame->updateWithMod(m); +} diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h new file mode 100644 index 00000000..41237139 --- /dev/null +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include + +#include "Application.h" +#include "minecraft/MinecraftInstance.h" +#include "ui/pages/BasePage.h" + +class ModFolderModel; + +namespace Ui { +class ExternalResourcesPage; +} + +/* This page is used as a base for pages in which the user can manage external resources + * related to the game, such as mods, shaders or resource packs. */ +class ExternalResourcesPage : public QMainWindow, public BasePage { + Q_OBJECT + + public: + // FIXME: Switch to different model (or change the name of this one) + explicit ExternalResourcesPage(BaseInstance* instance, std::shared_ptr model, QWidget* parent = nullptr); + virtual ~ExternalResourcesPage(); + + virtual QString displayName() const override = 0; + virtual QIcon icon() const override = 0; + virtual QString id() const override = 0; + virtual QString helpPage() const override = 0; + + virtual bool shouldDisplay() const override = 0; + + void openedImpl() override; + void closedImpl() override; + + void retranslate() override; + + protected: + bool eventFilter(QObject* obj, QEvent* ev) override; + bool listFilter(QKeyEvent* ev); + QMenu* createPopupMenu() override; + + public slots: + void current(const QModelIndex& current, const QModelIndex& previous); + + protected slots: + void itemActivated(const QModelIndex& index); + void filterTextChanged(const QString& newContents); + void runningStateChanged(bool running); + + virtual void addItem(); + virtual void removeItem(); + + virtual void enableItem(); + virtual void disableItem(); + + virtual void viewFolder(); + virtual void viewConfigs(); + + void ShowContextMenu(const QPoint& pos); + + protected: + BaseInstance* m_instance = nullptr; + + Ui::ExternalResourcesPage* ui = nullptr; + std::shared_ptr m_model; + QSortFilterProxyModel* m_filterModel = nullptr; + + QString m_fileSelectionFilter; + QString m_viewFilter; + + bool m_controlsEnabled = true; +}; diff --git a/launcher/ui/pages/instance/ModFolderPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui similarity index 78% rename from launcher/ui/pages/instance/ModFolderPage.ui rename to launcher/ui/pages/instance/ExternalResourcesPage.ui index ab59b0df..17bf455a 100644 --- a/launcher/ui/pages/instance/ModFolderPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -1,7 +1,7 @@ - ModFolderPage - + ExternalResourcesPage + 0 @@ -53,7 +53,7 @@ - + 0 @@ -83,15 +83,15 @@ false - + - - - - - + + + + + - + &Add @@ -99,31 +99,31 @@ Add - + &Remove - Remove selected mods + Remove selected item - + &Enable - Enable selected mods + Enable selected item - + &Disable - Disable selected mods + Disable selected item - + View &Configs @@ -131,11 +131,22 @@ Open the 'config' folder in the system file manager. - + View &Folder + + + false + + + &Download + + + Download a new resource + + @@ -156,7 +167,7 @@ - modTreeView + treeView filterEdit diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index d929a0ea..8fd0f86e 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -35,368 +35,97 @@ */ #include "ModFolderPage.h" -#include "ui_ModFolderPage.h" +#include "ui_ExternalResourcesPage.h" -#include +#include #include #include -#include #include +#include #include #include "Application.h" +#include "ui/GuiUtil.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ModDownloadDialog.h" -#include "ui/GuiUtil.h" #include "DesktopServices.h" -#include "minecraft/mod/ModFolderModel.h" -#include "minecraft/mod/Mod.h" -#include "minecraft/VersionFilterData.h" #include "minecraft/PackProfile.h" +#include "minecraft/VersionFilterData.h" +#include "minecraft/mod/Mod.h" +#include "minecraft/mod/ModFolderModel.h" #include "modplatform/ModAPI.h" #include "Version.h" -#include "ui/dialogs/ProgressDialog.h" #include "tasks/SequentialTask.h" +#include "ui/dialogs/ProgressDialog.h" -namespace { - // FIXME: wasteful - void RemoveThePrefix(QString & string) { - QRegularExpression regex(QStringLiteral("^(([Tt][Hh][eE])|([Tt][eE][Hh])) +")); - string.remove(regex); - string = string.trimmed(); - } -} - -class ModSortProxy : public QSortFilterProxyModel +ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent) + : ExternalResourcesPage(inst, mods, parent) { -public: - explicit ModSortProxy(QObject *parent = 0) : QSortFilterProxyModel(parent) - { - } - -protected: - bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override { - ModFolderModel *model = qobject_cast(sourceModel()); - if(!model) { - return false; - } - const auto &mod = model->at(source_row); - if(mod.name().contains(filterRegExp())) { - return true; - } - if(mod.description().contains(filterRegExp())) { - return true; - } - for(auto & author: mod.authors()) { - if (author.contains(filterRegExp())) { - return true; - } - } - return false; - } - - bool lessThan(const QModelIndex & source_left, const QModelIndex & source_right) const override - { - ModFolderModel *model = qobject_cast(sourceModel()); - if( - !model || - !source_left.isValid() || - !source_right.isValid() || - source_left.column() != source_right.column() - ) { - return QSortFilterProxyModel::lessThan(source_left, source_right); - } - - // we are now guaranteed to have two valid indexes in the same column... we love the provided invariants unconditionally and proceed. - - auto column = (ModFolderModel::Columns) source_left.column(); - bool invert = false; - switch(column) { - // GH-2550 - sort by enabled/disabled - case ModFolderModel::ActiveColumn: { - auto dataL = source_left.data(Qt::CheckStateRole).toBool(); - auto dataR = source_right.data(Qt::CheckStateRole).toBool(); - if(dataL != dataR) { - return dataL > dataR; - } - // fallthrough - invert = sortOrder() == Qt::DescendingOrder; - } - // GH-2722 - sort mod names in a way that discards "The" prefixes - case ModFolderModel::NameColumn: { - auto dataL = model->data(model->index(source_left.row(), ModFolderModel::NameColumn)).toString(); - RemoveThePrefix(dataL); - auto dataR = model->data(model->index(source_right.row(), ModFolderModel::NameColumn)).toString(); - RemoveThePrefix(dataR); - - auto less = dataL.compare(dataR, sortCaseSensitivity()); - if(less != 0) { - return invert ? (less > 0) : (less < 0); - } - // fallthrough - invert = sortOrder() == Qt::DescendingOrder; - } - // GH-2762 - sort versions by parsing them as versions - case ModFolderModel::VersionColumn: { - auto dataL = Version(model->data(model->index(source_left.row(), ModFolderModel::VersionColumn)).toString()); - auto dataR = Version(model->data(model->index(source_right.row(), ModFolderModel::VersionColumn)).toString()); - return invert ? (dataL > dataR) : (dataL < dataR); - } - default: { - return QSortFilterProxyModel::lessThan(source_left, source_right); - } - } - } -}; - -ModFolderPage::ModFolderPage( - BaseInstance *inst, - std::shared_ptr mods, - QString id, - QString iconName, - QString displayName, - QString helpPage, - QWidget *parent -) : - QMainWindow(parent), - ui(new Ui::ModFolderPage) -{ - ui->setupUi(this); - // This is structured like that so that these changes - // do not affect the Resouce pack and Shader pack tabs - if(id == "mods") { - auto act = new QAction(tr("Download mods"), this); - act->setToolTip(tr("Download mods from online mod platforms")); - ui->actionsToolbar->insertActionBefore(ui->actionAdd, act); - connect(act, &QAction::triggered, this, &ModFolderPage::on_actionInstall_mods_triggered); + // do not affect the Resource pack and Shader pack tabs + { + ui->actionDownloadItem->setText(tr("Download mods")); + ui->actionDownloadItem->setToolTip(tr("Download mods from online mod platforms")); + ui->actionDownloadItem->setEnabled(true); + ui->actionAddItem->setText(tr("Add file")); + ui->actionAddItem->setToolTip(tr("Add a locally downloaded file")); - ui->actionAdd->setText(tr("Add .jar")); - ui->actionAdd->setToolTip(tr("Add mods via local file")); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); + + connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::installMods); } - - ui->actionsToolbar->insertSpacer(ui->actionView_configs); - - m_inst = inst; - on_RunningState_changed(m_inst && m_inst->isRunning()); - m_mods = mods; - m_id = id; - m_displayName = displayName; - m_iconName = iconName; - m_helpName = helpPage; - m_fileSelectionFilter = "%1 (*.zip *.jar)"; - m_filterModel = new ModSortProxy(this); - m_filterModel->setDynamicSortFilter(true); - m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive); - m_filterModel->setSourceModel(m_mods.get()); - m_filterModel->setFilterKeyColumn(-1); - ui->modTreeView->setModel(m_filterModel); - ui->modTreeView->installEventFilter(this); - ui->modTreeView->sortByColumn(1, Qt::AscendingOrder); - ui->modTreeView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->modTreeView, &ModListView::customContextMenuRequested, this, &ModFolderPage::ShowContextMenu); - connect(ui->modTreeView, &ModListView::activated, this, &ModFolderPage::modItemActivated); - - auto smodel = ui->modTreeView->selectionModel(); - connect(smodel, &QItemSelectionModel::currentChanged, this, &ModFolderPage::modCurrent); - connect(ui->filterEdit, &QLineEdit::textChanged, this, &ModFolderPage::on_filterTextChanged); - connect(m_inst, &BaseInstance::runningStatusChanged, this, &ModFolderPage::on_RunningState_changed); } -void ModFolderPage::modItemActivated(const QModelIndex&) -{ - if(!m_controlsEnabled) { - return; - } - auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); - m_mods->setModStatus(selection.indexes(), ModFolderModel::Toggle); -} - -QMenu * ModFolderPage::createPopupMenu() -{ - QMenu* filteredMenu = QMainWindow::createPopupMenu(); - filteredMenu->removeAction(ui->actionsToolbar->toggleViewAction() ); - return filteredMenu; -} - -void ModFolderPage::ShowContextMenu(const QPoint& pos) -{ - auto menu = ui->actionsToolbar->createContextMenu(this, tr("Context menu")); - menu->exec(ui->modTreeView->mapToGlobal(pos)); - delete menu; -} - -void ModFolderPage::openedImpl() -{ - m_mods->startWatching(); -} - -void ModFolderPage::closedImpl() -{ - m_mods->stopWatching(); -} - -void ModFolderPage::on_filterTextChanged(const QString& newContents) -{ - m_viewFilter = newContents; - m_filterModel->setFilterFixedString(m_viewFilter); -} - - -CoreModFolderPage::CoreModFolderPage(BaseInstance *inst, std::shared_ptr mods, - QString id, QString iconName, QString displayName, - QString helpPage, QWidget *parent) - : ModFolderPage(inst, mods, id, iconName, displayName, helpPage, parent) -{ -} - -ModFolderPage::~ModFolderPage() -{ - m_mods->stopWatching(); - delete ui; -} - -void ModFolderPage::on_RunningState_changed(bool running) -{ - if(m_controlsEnabled == !running) { - return; - } - m_controlsEnabled = !running; - ui->actionsToolbar->setEnabled(m_controlsEnabled); -} +CoreModFolderPage::CoreModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent) + : ModFolderPage(inst, mods, parent) +{} bool ModFolderPage::shouldDisplay() const { return true; } -void ModFolderPage::retranslate() -{ - ui->retranslateUi(this); -} - bool CoreModFolderPage::shouldDisplay() const { - if (ModFolderPage::shouldDisplay()) - { - auto inst = dynamic_cast(m_inst); + if (ModFolderPage::shouldDisplay()) { + auto inst = dynamic_cast(m_instance); if (!inst) return true; + auto version = inst->getPackProfile(); + if (!version) return true; - if(!version->getComponent("net.minecraftforge")) - { + if (!version->getComponent("net.minecraftforge")) return false; - } - if(!version->getComponent("net.minecraft")) - { + if (!version->getComponent("net.minecraft")) return false; - } - if(version->getComponent("net.minecraft")->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate) - { + if (version->getComponent("net.minecraft")->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate) return true; - } + } return false; } -bool ModFolderPage::modListFilter(QKeyEvent *keyEvent) +void ModFolderPage::installMods() { - switch (keyEvent->key()) - { - case Qt::Key_Delete: - on_actionRemove_triggered(); - return true; - case Qt::Key_Plus: - on_actionAdd_triggered(); - return true; - default: - break; - } - return QWidget::eventFilter(ui->modTreeView, keyEvent); -} - -bool ModFolderPage::eventFilter(QObject *obj, QEvent *ev) -{ - if (ev->type() != QEvent::KeyPress) - { - return QWidget::eventFilter(obj, ev); - } - QKeyEvent *keyEvent = static_cast(ev); - if (obj == ui->modTreeView) - return modListFilter(keyEvent); - return QWidget::eventFilter(obj, ev); -} - -void ModFolderPage::on_actionAdd_triggered() -{ - if(!m_controlsEnabled) { + if (!m_controlsEnabled) return; - } - auto list = GuiUtil::BrowseForFiles( - m_helpName, - tr("Select %1", - "Select whatever type of files the page contains. Example: 'Loader Mods'") - .arg(m_displayName), - m_fileSelectionFilter.arg(m_displayName), APPLICATION->settings()->get("CentralModsDir").toString(), - this->parentWidget()); - if (!list.empty()) - { - for (auto filename : list) - { - m_mods->installMod(filename); - } - } -} - -void ModFolderPage::on_actionEnable_triggered() -{ - if(!m_controlsEnabled) { - return; - } - auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); - m_mods->setModStatus(selection.indexes(), ModFolderModel::Enable); -} - -void ModFolderPage::on_actionDisable_triggered() -{ - if(!m_controlsEnabled) { - return; - } - auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); - m_mods->setModStatus(selection.indexes(), ModFolderModel::Disable); -} - -void ModFolderPage::on_actionRemove_triggered() -{ - if(!m_controlsEnabled) { - return; - } - auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); - m_mods->deleteMods(selection.indexes()); -} - -void ModFolderPage::on_actionInstall_mods_triggered() -{ - if(!m_controlsEnabled) { - return; - } - if(m_inst->typeName() != "Minecraft"){ - return; //this is a null instance or a legacy instance - } - auto profile = ((MinecraftInstance *)m_inst)->getPackProfile(); + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast(m_instance)->getPackProfile(); if (profile->getModLoaders() == ModAPI::Unspecified) { - QMessageBox::critical(this,tr("Error"),tr("Please install a mod loader first!")); + QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!")); return; } - ModDownloadDialog mdownload(m_mods, this, m_inst); + + ModDownloadDialog mdownload(m_model, this, m_instance); if (mdownload.exec()) { SequentialTask* tasks = new SequentialTask(this); connect(tasks, &Task::failed, [this, tasks](QString reason) { @@ -409,40 +138,20 @@ void ModFolderPage::on_actionInstall_mods_triggered() }); connect(tasks, &Task::succeeded, [this, tasks]() { QStringList warnings = tasks->warnings(); - if (warnings.count()) { CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); } + if (warnings.count()) + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + tasks->deleteLater(); }); - for (auto task : mdownload.getTasks()) { + for (auto& task : mdownload.getTasks()) { tasks->addTask(task); } ProgressDialog loadDialog(this); loadDialog.setSkipButton(true, tr("Abort")); loadDialog.execWithTask(tasks); - m_mods->update(); + + m_model->update(); } } - -void ModFolderPage::on_actionView_configs_triggered() -{ - DesktopServices::openDirectory(m_inst->instanceConfigFolder(), true); -} - -void ModFolderPage::on_actionView_Folder_triggered() -{ - DesktopServices::openDirectory(m_mods->dir().absolutePath(), true); -} - -void ModFolderPage::modCurrent(const QModelIndex ¤t, const QModelIndex &previous) -{ - if (!current.isValid()) - { - ui->frame->clear(); - return; - } - auto sourceCurrent = m_filterModel->mapToSource(current); - int row = sourceCurrent.row(); - Mod &m = m_mods->operator[](row); - ui->frame->updateWithMod(m); -} diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index 2dd44e85..1a9ed7db 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -35,109 +35,31 @@ #pragma once -#include +#include "ExternalResourcesPage.h" -#include "minecraft/MinecraftInstance.h" -#include "ui/pages/BasePage.h" - -#include -#include - -class ModFolderModel; -namespace Ui -{ -class ModFolderPage; -} - -class ModFolderPage : public QMainWindow, public BasePage -{ +class ModFolderPage : public ExternalResourcesPage { Q_OBJECT -public: - explicit ModFolderPage( - BaseInstance *inst, - std::shared_ptr mods, - QString id, - QString iconName, - QString displayName, - QString helpPage = "", - QWidget *parent = 0 - ); - virtual ~ModFolderPage(); + public: + explicit ModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent = nullptr); + virtual ~ModFolderPage() = default; - void setFilter(const QString & filter) - { - m_fileSelectionFilter = filter; - } + void setFilter(const QString& filter) { m_fileSelectionFilter = filter; } + + virtual QString displayName() const override { return tr("Mods"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("loadermods"); } + virtual QString id() const override { return "mods"; } + virtual QString helpPage() const override { return "Loader-mods"; } - virtual QString displayName() const override - { - return m_displayName; - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon(m_iconName); - } - virtual QString id() const override - { - return m_id; - } - virtual QString helpPage() const override - { - return m_helpName; - } virtual bool shouldDisplay() const override; - void retranslate() override; - virtual void openedImpl() override; - virtual void closedImpl() override; -protected: - bool eventFilter(QObject *obj, QEvent *ev) override; - bool modListFilter(QKeyEvent *ev); - QMenu * createPopupMenu() override; - -protected: - BaseInstance *m_inst = nullptr; - -protected: - Ui::ModFolderPage *ui = nullptr; - std::shared_ptr m_mods; - QSortFilterProxyModel *m_filterModel = nullptr; - QString m_iconName; - QString m_id; - QString m_displayName; - QString m_helpName; - QString m_fileSelectionFilter; - QString m_viewFilter; - bool m_controlsEnabled = true; - -public -slots: - void modCurrent(const QModelIndex ¤t, const QModelIndex &previous); - -private -slots: - void modItemActivated(const QModelIndex &index); - void on_filterTextChanged(const QString & newContents); - void on_RunningState_changed(bool running); - void on_actionAdd_triggered(); - void on_actionRemove_triggered(); - void on_actionEnable_triggered(); - void on_actionDisable_triggered(); - void on_actionInstall_mods_triggered(); - void on_actionView_Folder_triggered(); - void on_actionView_configs_triggered(); - void ShowContextMenu(const QPoint &pos); + private slots: + void installMods(); }; -class CoreModFolderPage : public ModFolderPage -{ -public: - explicit CoreModFolderPage(BaseInstance *inst, std::shared_ptr mods, QString id, - QString iconName, QString displayName, QString helpPage = "", - QWidget *parent = 0); - virtual ~CoreModFolderPage() - { - } +class CoreModFolderPage : public ModFolderPage { + public: + explicit CoreModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent = 0); + virtual ~CoreModFolderPage() = default; virtual bool shouldDisplay() const; }; diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index 8054926c..a6c9fdd3 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -35,24 +35,28 @@ #pragma once -#include "ModFolderPage.h" -#include "ui_ModFolderPage.h" +#include "ExternalResourcesPage.h" +#include "ui_ExternalResourcesPage.h" -class ResourcePackPage : public ModFolderPage +class ResourcePackPage : public ExternalResourcesPage { Q_OBJECT public: explicit ResourcePackPage(MinecraftInstance *instance, QWidget *parent = 0) - : ModFolderPage(instance, instance->resourcePackList(), "resourcepacks", - "resourcepacks", tr("Resource packs"), "Resource-packs", parent) + : ExternalResourcesPage(instance, instance->resourcePackList(), parent) { - ui->actionView_configs->setVisible(false); + ui->actionViewConfigs->setVisible(false); } virtual ~ResourcePackPage() {} + QString displayName() const override { return tr("Resource packs"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("resourcepacks"); } + QString id() const override { return "resourcepacks"; } + QString helpPage() const override { return "Resource-packs"; } + virtual bool shouldDisplay() const override { - return !m_inst->traits().contains("no-texturepacks") && - !m_inst->traits().contains("texturepacks"); + return !m_instance->traits().contains("no-texturepacks") && + !m_instance->traits().contains("texturepacks"); } }; diff --git a/launcher/ui/pages/instance/ShaderPackPage.h b/launcher/ui/pages/instance/ShaderPackPage.h index 7d4f5074..2cc056c8 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.h +++ b/launcher/ui/pages/instance/ShaderPackPage.h @@ -35,21 +35,25 @@ #pragma once -#include "ModFolderPage.h" -#include "ui_ModFolderPage.h" +#include "ExternalResourcesPage.h" +#include "ui_ExternalResourcesPage.h" -class ShaderPackPage : public ModFolderPage +class ShaderPackPage : public ExternalResourcesPage { Q_OBJECT public: explicit ShaderPackPage(MinecraftInstance *instance, QWidget *parent = 0) - : ModFolderPage(instance, instance->shaderPackList(), "shaderpacks", - "shaderpacks", tr("Shader packs"), "Resource-packs", parent) + : ExternalResourcesPage(instance, instance->shaderPackList(), parent) { - ui->actionView_configs->setVisible(false); + ui->actionViewConfigs->setVisible(false); } virtual ~ShaderPackPage() {} + QString displayName() const override { return tr("Shader packs"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("shaderpacks"); } + QString id() const override { return "shaderpacks"; } + QString helpPage() const override { return "Resource-packs"; } + virtual bool shouldDisplay() const override { return true; diff --git a/launcher/ui/pages/instance/TexturePackPage.h b/launcher/ui/pages/instance/TexturePackPage.h index e8cefe6e..f550a5bc 100644 --- a/launcher/ui/pages/instance/TexturePackPage.h +++ b/launcher/ui/pages/instance/TexturePackPage.h @@ -35,23 +35,27 @@ #pragma once -#include "ModFolderPage.h" -#include "ui_ModFolderPage.h" +#include "ExternalResourcesPage.h" +#include "ui_ExternalResourcesPage.h" -class TexturePackPage : public ModFolderPage +class TexturePackPage : public ExternalResourcesPage { Q_OBJECT public: explicit TexturePackPage(MinecraftInstance *instance, QWidget *parent = 0) - : ModFolderPage(instance, instance->texturePackList(), "texturepacks", "resourcepacks", - tr("Texture packs"), "Texture-packs", parent) + : ExternalResourcesPage(instance, instance->texturePackList(), parent) { - ui->actionView_configs->setVisible(false); + ui->actionViewConfigs->setVisible(false); } virtual ~TexturePackPage() {} + QString displayName() const override { return tr("Texture packs"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("resourcepacks"); } + QString id() const override { return "texturepacks"; } + QString helpPage() const override { return "Texture-packs"; } + virtual bool shouldDisplay() const override { - return m_inst->traits().contains("texturepacks"); + return m_instance->traits().contains("texturepacks"); } };