From 9913080a829acb4ca921c3a68e0caefad0ebcaa1 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 4 May 2023 23:44:28 -0700 Subject: feat(modpage): mod icon in description and column Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/widgets/InfoFrame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index fdc581b4..6f4036a2 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -88,7 +88,7 @@ void InfoFrame::updateWithMod(Mod const& m) setDescription(m.description()); } - setImage(); + setImage(m.icon({64,64})); } void InfoFrame::updateWithResource(const Resource& resource) -- cgit From 74e7c13a177afdb503a642cb9c97d71e72249291 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 5 May 2023 13:46:38 -0700 Subject: feat: display license and issue tracker Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/minecraft/mod/Mod.cpp | 9 ++ launcher/minecraft/mod/Mod.h | 2 + launcher/minecraft/mod/ModDetails.h | 11 +- launcher/minecraft/mod/ModFolderModel.cpp | 1 - launcher/minecraft/mod/tasks/LocalModParseTask.cpp | 3 +- launcher/ui/widgets/InfoFrame.cpp | 120 ++++++++++++++++++++- launcher/ui/widgets/InfoFrame.h | 4 + launcher/ui/widgets/InfoFrame.ui | 92 +++++++++++----- 8 files changed, 209 insertions(+), 33 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index f236d2ac..e613ddeb 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -215,6 +215,15 @@ auto Mod::provider() const -> std::optional return {}; } +auto Mod::licenses() const -> const QList& +{ + return details().licenses; +} + + auto Mod::issueTracker() const -> QString +{ + return details().issue_tracker; +} void Mod::setIcon(QImage new_image) const { diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 4be0842f..d4e419f4 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -68,6 +68,8 @@ public: auto authors() const -> QStringList; auto status() const -> ModStatus; auto provider() const -> std::optional; + auto licenses() const -> const QList&; + auto issueTracker() const -> QString; /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; }; diff --git a/launcher/minecraft/mod/ModDetails.h b/launcher/minecraft/mod/ModDetails.h index eb3770d6..b4e59d52 100644 --- a/launcher/minecraft/mod/ModDetails.h +++ b/launcher/minecraft/mod/ModDetails.h @@ -64,8 +64,11 @@ struct ModLicense { auto parts = license.split(' '); QStringList notNameParts = {}; for (auto part : parts) { - auto url = QUrl::fromUserInput(part); - if (url.isValid()) { + auto url = QUrl(part); + if (part.startsWith("(") && part.endsWith(")")) + url = QUrl(part.mid(1, part.size() - 2)); + + if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { this->url = url.toString(); notNameParts.append(part); continue; @@ -119,6 +122,10 @@ struct ModLicense { return *this; } + + bool isEmpty() { + return this->name.isEmpty() && this->id.isEmpty() && this->url.isEmpty() && this->description.isEmpty(); + } }; struct ModDetails diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index f1c26e68..8843f79f 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -52,7 +52,6 @@ #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h" -#include "modplatform/ModIndex.h" ModFolderModel::ModFolderModel(const QString& dir, std::shared_ptr instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index f045bde3..264019f8 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -184,7 +184,8 @@ ModDetails ReadMCModTOML(QByteArray contents) } else if (auto licenseDatum =(*modsTable)["license"].as_string()) { license = QString::fromStdString(licenseDatum->get()); } - details.licenses.push_back(ModLicense(license)); + if (!license.isEmpty()) + details.licenses.append(ModLicense(license)); QString logoFile = ""; if (auto logoFileDatum = tomlData["logoFile"].as_string()) { diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 6f4036a2..9c041bfe 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -47,6 +47,8 @@ InfoFrame::InfoFrame(QWidget *parent) : ui->setupUi(this); ui->descriptionLabel->setHidden(true); ui->nameLabel->setHidden(true); + ui->licenseLabel->setHidden(true); + ui->issueTrackerLabel->setHidden(true); updateHiddenState(); } @@ -89,6 +91,40 @@ void InfoFrame::updateWithMod(Mod const& m) } setImage(m.icon({64,64})); + + auto licenses = m.licenses(); + QString licenseText = ""; + if (!licenses.empty()) { + for (auto l : licenses) { + if (!licenseText.isEmpty()) { + licenseText += "\n"; // add newline between licenses + } + if (!l.name.isEmpty()) { + if (l.url.isEmpty()) { + licenseText += l.name; + } else { + licenseText += "" + l.name + ""; + } + } else if (!l.url.isEmpty()) { + licenseText += "" + l.url + ""; + } + if (!l.description.isEmpty() && l.description != l.name) { + licenseText += " " + l.description; + } + } + } + if (!licenseText.isEmpty()) { + setLicense(tr("License: %1").arg(licenseText)); + } else { + setLicense(); + } + + QString issueTracker = ""; + if (!m.issueTracker().isEmpty()) { + issueTracker += tr("Report issues to: "); + issueTracker += "" + m.issueTracker() + ""; + } + setIssueTracker(issueTracker); } void InfoFrame::updateWithResource(const Resource& resource) @@ -177,16 +213,16 @@ void InfoFrame::clear() setName(); setDescription(); setImage(); + setLicense(); + setIssueTracker(); } void InfoFrame::updateHiddenState() { - if(ui->descriptionLabel->isHidden() && ui->nameLabel->isHidden()) - { + if (ui->descriptionLabel->isHidden() && ui->nameLabel->isHidden() && ui->licenseLabel->isHidden() && + ui->issueTrackerLabel->isHidden()) { setHidden(true); - } - else - { + } else { setHidden(false); } } @@ -251,6 +287,66 @@ void InfoFrame::setDescription(QString text) ui->descriptionLabel->setText(labeltext); } +void InfoFrame::setLicense(QString text) +{ + if(text.isEmpty()) + { + ui->licenseLabel->setHidden(true); + updateHiddenState(); + return; + } + else + { + ui->licenseLabel->setHidden(false); + updateHiddenState(); + } + ui->licenseLabel->setToolTip(""); + QString intermediatetext = text.trimmed(); + bool prev(false); + QChar rem('\n'); + QString finaltext; + finaltext.reserve(intermediatetext.size()); + foreach(const QChar& c, intermediatetext) + { + if(c == rem && prev){ + continue; + } + prev = c == rem; + finaltext += c; + } + QString labeltext; + labeltext.reserve(300); + if(finaltext.length() > 290) + { + ui->licenseLabel->setOpenExternalLinks(false); + ui->licenseLabel->setTextFormat(Qt::TextFormat::RichText); + m_description = text; + // This allows injecting HTML here. + labeltext.append("" + finaltext.left(287) + "..."); + QObject::connect(ui->licenseLabel, &QLabel::linkActivated, this, &InfoFrame::licenseEllipsisHandler); + } + else + { + ui->licenseLabel->setTextFormat(Qt::TextFormat::AutoText); + labeltext.append(finaltext); + } + ui->licenseLabel->setText(labeltext); +} + +void InfoFrame::setIssueTracker(QString text) +{ + if(text.isEmpty()) + { + ui->issueTrackerLabel->setHidden(true); + } + else + { + ui->issueTrackerLabel->setText(text); + ui->issueTrackerLabel->setHidden(false); + } + updateHiddenState(); +} + void InfoFrame::setImage(QPixmap img) { if (img.isNull()) { @@ -275,6 +371,20 @@ void InfoFrame::descriptionEllipsisHandler(QString link) } } +void InfoFrame::licenseEllipsisHandler(QString link) +{ + if(!m_current_box) + { + m_current_box = CustomMessageBox::selectable(this, "", m_license); + connect(m_current_box, &QMessageBox::finished, this, &InfoFrame::boxClosed); + m_current_box->show(); + } + else + { + m_current_box->setText(m_license); + } +} + void InfoFrame::boxClosed(int result) { m_current_box = nullptr; diff --git a/launcher/ui/widgets/InfoFrame.h b/launcher/ui/widgets/InfoFrame.h index 84523e28..7eb679a9 100644 --- a/launcher/ui/widgets/InfoFrame.h +++ b/launcher/ui/widgets/InfoFrame.h @@ -36,6 +36,8 @@ class InfoFrame : public QFrame { void setName(QString text = {}); void setDescription(QString text = {}); void setImage(QPixmap img = {}); + void setLicense(QString text = {}); + void setIssueTracker(QString text = {}); void clear(); @@ -48,6 +50,7 @@ class InfoFrame : public QFrame { public slots: void descriptionEllipsisHandler(QString link); + void licenseEllipsisHandler(QString link); void boxClosed(int result); private: @@ -56,5 +59,6 @@ class InfoFrame : public QFrame { private: Ui::InfoFrame* ui; QString m_description; + QString m_license; class QMessageBox* m_current_box = nullptr; }; diff --git a/launcher/ui/widgets/InfoFrame.ui b/launcher/ui/widgets/InfoFrame.ui index 9e407ce9..c4d8c83d 100644 --- a/launcher/ui/widgets/InfoFrame.ui +++ b/launcher/ui/widgets/InfoFrame.ui @@ -35,8 +35,36 @@ 0 - - + + + + + 0 + 0 + + + + + 64 + 64 + + + + + + + false + + + 0 + + + + + + + + @@ -57,11 +85,8 @@ - - - - - + + @@ -82,28 +107,47 @@ - - - - - 0 - 0 - + + + + - - - 64 - 64 - + + Qt::RichText + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + - + - - false + + Qt::RichText - - 0 + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse -- cgit From 086a7e19f099c6c9b9529afb2360300e534876bf Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 24 May 2023 20:15:34 -0700 Subject: feat: Column on left, hideable - columns are hideable (saves to settings) - image column moved to left - datamodals can provide resize modes Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/minecraft/mod/ModFolderModel.cpp | 6 +- launcher/minecraft/mod/ModFolderModel.h | 4 +- launcher/minecraft/mod/ResourceFolderModel.cpp | 66 ++++++++++++++++++++++ launcher/minecraft/mod/ResourceFolderModel.h | 12 ++++ launcher/minecraft/mod/ResourcePackFolderModel.cpp | 6 +- launcher/minecraft/mod/ResourcePackFolderModel.h | 4 +- launcher/minecraft/mod/ShaderPackFolderModel.h | 2 + launcher/minecraft/mod/TexturePackFolderModel.cpp | 9 ++- launcher/minecraft/mod/TexturePackFolderModel.h | 4 +- .../ui/pages/instance/ExternalResourcesPage.cpp | 15 +++++ launcher/ui/pages/instance/ExternalResourcesPage.h | 1 + launcher/ui/widgets/ModListView.cpp | 9 +++ launcher/ui/widgets/ModListView.h | 2 + 13 files changed, 131 insertions(+), 9 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 8843f79f..59e078f1 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -37,6 +37,7 @@ #include "ModFolderModel.h" #include +#include #include #include #include @@ -56,7 +57,8 @@ ModFolderModel::ModFolderModel(const QString& dir, std::shared_ptr instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER, SortType::NAME }; + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME , SortType::VERSION, SortType::DATE, SortType::PROVIDER}; + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents}; } QVariant ModFolderModel::data(const QModelIndex &index, int role) const @@ -143,7 +145,7 @@ QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, in switch (section) { case ActiveColumn: - return QString(); + return tr("Enable"); case NameColumn: return tr("Name"); case VersionColumn: diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 20018e9c..c1f9a2b6 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -64,11 +64,11 @@ public: enum Columns { ActiveColumn = 0, + ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, - ImageColumn, NUM_COLUMNS }; enum ModStatusAction { @@ -78,6 +78,8 @@ public: }; ModFolderModel(const QString &dir, std::shared_ptr instance, bool is_indexed = false, bool create_dir = true); + virtual QString id() const override { return "mods"; } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index e1973468..ba64497b 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -8,12 +8,15 @@ #include #include #include +#include #include "Application.h" #include "FileSystem.h" +#include "QVariantUtils.h" #include "minecraft/mod/tasks/BasicFolderLoadTask.h" +#include "settings/Setting.h" #include "tasks/Task.h" ResourceFolderModel::ResourceFolderModel(QDir dir, std::shared_ptr instance, QObject* parent, bool create_dir) @@ -471,6 +474,8 @@ QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientatio switch (role) { case Qt::DisplayRole: switch (section) { + case ACTIVE_COLUMN: + return tr("Enable"); case NAME_COLUMN: return tr("Name"); case DATE_COLUMN: @@ -500,6 +505,67 @@ QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientatio return {}; } +void ResourceFolderModel::setupHeaderAction(QAction* act, int column) +{ + Q_ASSERT(act); + + act->setText(headerData(column, Qt::Orientation::Horizontal).toString()); +} + +void ResourceFolderModel::saveHiddenColumn(int column, bool hidden) +{ + auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); + auto setting = (APPLICATION->settings()->contains(setting_name)) ? + APPLICATION->settings()->getSetting(setting_name) : APPLICATION->settings()->registerSetting(setting_name); + + auto hiddenColumns = QVariantUtils::toList(setting->get()); + auto index = hiddenColumns.indexOf(column); + if (index >= 0 && !hidden) { + hiddenColumns.removeAt(index); + } else if ( index < 0 && hidden) { + hiddenColumns.append(column); + } + setting->set(QVariantUtils::fromList(hiddenColumns)); +} + +void ResourceFolderModel::loadHiddenColumns(QTreeView *tree) +{ + auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); + auto setting = (APPLICATION->settings()->contains(setting_name)) ? + APPLICATION->settings()->getSetting(setting_name) : APPLICATION->settings()->registerSetting(setting_name); + + auto hiddenColumns = QVariantUtils::toList(setting->get().toList()); + for (auto col : hiddenColumns) { + tree->setColumnHidden(col, true); + } + +} + +std::unique_ptr ResourceFolderModel::createHeaderContextMenu(QWidget* parent, QTreeView* tree) +{ + auto menu = std::make_unique(parent); + + menu->addSeparator()->setText(tr("Show / Hide Columns")); + + for (int col = 0; col < columnCount(); ++col) { + auto act = new QAction(); + setupHeaderAction(act, col); + + act->setCheckable(true); + act->setChecked(!tree->isColumnHidden(col)); + + connect(act, &QAction::toggled, tree, [this, col, tree](bool toggled){ + tree->setColumnHidden(col, !toggled); + saveHiddenColumn(col, !toggled); + }); + + menu->addAction(act); + + } + + return menu; +} + QSortFilterProxyModel* ResourceFolderModel::createFilterProxyModel(QObject* parent) { return new ProxyModel(parent); diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index fdf5f331..54627c80 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -1,5 +1,8 @@ #pragma once +#include +#include +#include #include #include #include @@ -29,6 +32,8 @@ class ResourceFolderModel : public QAbstractListModel { ResourceFolderModel(QDir, std::shared_ptr, QObject* parent = nullptr, bool create_dir = true); ~ResourceFolderModel() override; + virtual QString id() const { return "resource"; } + /** Starts watching the paths for changes. * * Returns whether starting to watch all the paths was successful. @@ -110,6 +115,11 @@ class ResourceFolderModel : public QAbstractListModel { [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + void setupHeaderAction(QAction* act, int column); + void saveHiddenColumn(int column, bool hidden); + void loadHiddenColumns(QTreeView* tree); + std::unique_ptr createHeaderContextMenu(QWidget* parent, QTreeView* tree); + /** This creates a proxy model to filter / sort the model for a UI. * * The actual comparisons and filtering are done directly by the Resource, so to modify behavior go there instead! @@ -117,6 +127,7 @@ class ResourceFolderModel : public QAbstractListModel { QSortFilterProxyModel* createFilterProxyModel(QObject* parent = nullptr); [[nodiscard]] SortType columnToSortKey(size_t column) const; + [[nodiscard]] QList columnResizeModes() const { return m_column_resize_modes; } class ProxyModel : public QSortFilterProxyModel { public: @@ -187,6 +198,7 @@ class ResourceFolderModel : public QAbstractListModel { // Represents the relationship between a column's index (represented by the list index), and it's sorting key. // As such, the order in with they appear is very important! QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; + QList m_column_resize_modes = { QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; bool m_can_interact = true; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index b11e2262..7ec5668c 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -50,7 +50,9 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, std::shared_ptr instance) : ResourceFolderModel(QDir(dir), instance) { - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::NAME }; + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE}; + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents}; + } QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const @@ -130,7 +132,7 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient case Qt::DisplayRole: switch (section) { case ActiveColumn: - return QString(); + return tr("Enable"); case NameColumn: return tr("Name"); case PackFormatColumn: diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h index 71532f30..bbec9619 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.h +++ b/launcher/minecraft/mod/ResourcePackFolderModel.h @@ -11,15 +11,17 @@ public: enum Columns { ActiveColumn = 0, + ImageColumn, NameColumn, PackFormatColumn, DateColumn, - ImageColumn, NUM_COLUMNS }; explicit ResourcePackFolderModel(const QString &dir, std::shared_ptr instance); + virtual QString id() const override { return "resourcepacks"; } + [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h index 6f3f2811..e010f6ed 100644 --- a/launcher/minecraft/mod/ShaderPackFolderModel.h +++ b/launcher/minecraft/mod/ShaderPackFolderModel.h @@ -9,4 +9,6 @@ class ShaderPackFolderModel : public ResourceFolderModel { explicit ShaderPackFolderModel(const QString& dir, std::shared_ptr instance) : ResourceFolderModel(QDir(dir), instance) {} + + virtual QString id() const override { return "shaderpacks"; } }; diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index e115cce6..c88f8f37 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -45,7 +45,9 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, std::shared_ptr instance) : ResourceFolderModel(QDir(dir), instance) { - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::NAME }; + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents}; + } Task* TexturePackFolderModel::createUpdateTask() @@ -115,6 +117,8 @@ QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orienta switch (role) { case Qt::DisplayRole: switch (section) { + case ActiveColumn: + return tr("Enable"); case NameColumn: return tr("Name"); case DateColumn: @@ -149,4 +153,5 @@ QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orienta int TexturePackFolderModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; -} \ No newline at end of file +} + diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h index 4467691a..ce9c06c4 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.h +++ b/launcher/minecraft/mod/TexturePackFolderModel.h @@ -49,14 +49,16 @@ public: enum Columns { ActiveColumn = 0, + ImageColumn, NameColumn, DateColumn, - ImageColumn, NUM_COLUMNS }; explicit TexturePackFolderModel(const QString &dir, std::shared_ptr instance); + virtual QString id() const override { return "texturepacks"; } + [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 6c11b85b..bee11d9a 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -24,6 +24,8 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared m_filterModel->setSourceModel(m_model.get()); m_filterModel->setFilterKeyColumn(-1); ui->treeView->setModel(m_filterModel); + // must come after setModel + ui->treeView->setResizeModes(m_model->columnResizeModes()); ui->treeView->installEventFilter(this); ui->treeView->sortByColumn(1, Qt::AscendingOrder); @@ -44,6 +46,13 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared auto selection_model = ui->treeView->selectionModel(); connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current); connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged); + + auto viewHeader = ui->treeView->header(); + viewHeader->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(viewHeader, &QHeaderView::customContextMenuRequested, this, &ExternalResourcesPage::ShowHeaderContextMenu); + + m_model->loadHiddenColumns(ui->treeView); } ExternalResourcesPage::~ExternalResourcesPage() @@ -65,6 +74,12 @@ void ExternalResourcesPage::ShowContextMenu(const QPoint& pos) delete menu; } +void ExternalResourcesPage::ShowHeaderContextMenu(const QPoint& pos) +{ + auto menu = m_model->createHeaderContextMenu(this, ui->treeView); + menu->exec(ui->treeView->mapToGlobal(pos)); +} + void ExternalResourcesPage::openedImpl() { m_model->startWatching(); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h index d17fbb7f..906e6df7 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.h +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -60,6 +60,7 @@ class ExternalResourcesPage : public QMainWindow, public BasePage { virtual void viewConfigs(); void ShowContextMenu(const QPoint& pos); + void ShowHeaderContextMenu(const QPoint& pos); protected: BaseInstance* m_instance = nullptr; diff --git a/launcher/ui/widgets/ModListView.cpp b/launcher/ui/widgets/ModListView.cpp index 09b03a76..893cd120 100644 --- a/launcher/ui/widgets/ModListView.cpp +++ b/launcher/ui/widgets/ModListView.cpp @@ -79,3 +79,12 @@ void ModListView::setModel ( QAbstractItemModel* model ) }); } } + +void ModListView::setResizeModes(const QList &modes) +{ + auto head = header(); + for(int i = 0; i < modes.count(); i++) { + head->setSectionResizeMode(i, modes[i]); + } +} + diff --git a/launcher/ui/widgets/ModListView.h b/launcher/ui/widgets/ModListView.h index 881e092f..3f0b3b0e 100644 --- a/launcher/ui/widgets/ModListView.h +++ b/launcher/ui/widgets/ModListView.h @@ -14,6 +14,7 @@ */ #pragma once +#include #include class ModListView: public QTreeView @@ -22,4 +23,5 @@ class ModListView: public QTreeView public: explicit ModListView ( QWidget* parent = 0 ); virtual void setModel ( QAbstractItemModel* model ); + virtual void setResizeModes (const QList& modes); }; -- cgit From a04a6f1e0d0d551506a86964c51e5ce6af5587b4 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 28 May 2023 02:15:39 -0700 Subject: fix(memory leak): don't give shared pointers out to foldermodels (causes cyclic refrence) Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/minecraft/MinecraftInstance.cpp | 30 +++++++++++----------- launcher/minecraft/MinecraftInstance.h | 16 ++++++------ launcher/minecraft/WorldList.cpp | 2 +- launcher/minecraft/WorldList.h | 4 +-- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/ModFolderModel.h | 2 +- launcher/minecraft/mod/ResourceFolderModel.cpp | 2 +- launcher/minecraft/mod/ResourceFolderModel.h | 4 +-- launcher/minecraft/mod/ResourcePackFolderModel.cpp | 2 +- launcher/minecraft/mod/ResourcePackFolderModel.h | 2 +- launcher/minecraft/mod/ShaderPackFolderModel.h | 2 +- launcher/minecraft/mod/TexturePackFolderModel.cpp | 2 +- launcher/minecraft/mod/TexturePackFolderModel.h | 2 +- launcher/ui/dialogs/NewInstanceDialog.cpp | 2 +- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 2 +- launcher/ui/widgets/PageContainer.cpp | 4 ++- tests/ResourceFolderModel_test.cpp | 17 ++++-------- 17 files changed, 46 insertions(+), 51 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 35bef05e..2c624a36 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1109,79 +1109,79 @@ JavaVersion MinecraftInstance::getJavaVersion() return JavaVersion(settings()->get("JavaVersion").toString()); } -std::shared_ptr MinecraftInstance::loaderModList() const +std::shared_ptr MinecraftInstance::loaderModList() { if (!m_loader_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); - m_loader_mod_list.reset(new ModFolderModel(modsRoot(), shared_from_this(), is_indexed)); + m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed)); m_loader_mod_list->disableInteraction(isRunning()); connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction); } return m_loader_mod_list; } -std::shared_ptr MinecraftInstance::coreModList() const +std::shared_ptr MinecraftInstance::coreModList() { if (!m_core_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); - m_core_mod_list.reset(new ModFolderModel(coreModsDir(), shared_from_this(), is_indexed)); + m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed)); m_core_mod_list->disableInteraction(isRunning()); connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction); } return m_core_mod_list; } -std::shared_ptr MinecraftInstance::nilModList() const +std::shared_ptr MinecraftInstance::nilModList() { if (!m_nil_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); - m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), shared_from_this(), is_indexed, false)); + m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), this, is_indexed, false)); m_nil_mod_list->disableInteraction(isRunning()); connect(this, &BaseInstance::runningStatusChanged, m_nil_mod_list.get(), &ModFolderModel::disableInteraction); } return m_nil_mod_list; } -std::shared_ptr MinecraftInstance::resourcePackList() const +std::shared_ptr MinecraftInstance::resourcePackList() { if (!m_resource_pack_list) { - m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), shared_from_this())); + m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this)); } return m_resource_pack_list; } -std::shared_ptr MinecraftInstance::texturePackList() const +std::shared_ptr MinecraftInstance::texturePackList() { if (!m_texture_pack_list) { - m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), shared_from_this())); + m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this)); } return m_texture_pack_list; } -std::shared_ptr MinecraftInstance::shaderPackList() const +std::shared_ptr MinecraftInstance::shaderPackList() { if (!m_shader_pack_list) { - m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), shared_from_this())); + m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this)); } return m_shader_pack_list; } -std::shared_ptr MinecraftInstance::worldList() const +std::shared_ptr MinecraftInstance::worldList() { if (!m_world_list) { - m_world_list.reset(new WorldList(worldDir(), shared_from_this())); + m_world_list.reset(new WorldList(worldDir(), this)); } return m_world_list; } -std::shared_ptr MinecraftInstance::gameOptionsModel() const +std::shared_ptr MinecraftInstance::gameOptionsModel() { if (!m_game_options) { diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index a75fa481..068b3008 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -115,14 +115,14 @@ public: std::shared_ptr getPackProfile() const; ////// Mod Lists ////// - std::shared_ptr loaderModList() const; - std::shared_ptr coreModList() const; - std::shared_ptr nilModList() const; - std::shared_ptr resourcePackList() const; - std::shared_ptr texturePackList() const; - std::shared_ptr shaderPackList() const; - std::shared_ptr worldList() const; - std::shared_ptr gameOptionsModel() const; + std::shared_ptr loaderModList(); + std::shared_ptr coreModList(); + std::shared_ptr nilModList(); + std::shared_ptr resourcePackList(); + std::shared_ptr texturePackList(); + std::shared_ptr shaderPackList(); + std::shared_ptr worldList(); + std::shared_ptr gameOptionsModel(); ////// Launch stuff ////// Task::Ptr createUpdateTask(Net::Mode mode) override; diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp index df6b4ecc..0feee299 100644 --- a/launcher/minecraft/WorldList.cpp +++ b/launcher/minecraft/WorldList.cpp @@ -45,7 +45,7 @@ #include #include -WorldList::WorldList(const QString &dir, std::shared_ptr instance) +WorldList::WorldList(const QString &dir, BaseInstance* instance) : QAbstractListModel(), m_instance(instance), m_dir(dir) { FS::ensureFolderPathExists(m_dir.absolutePath()); diff --git a/launcher/minecraft/WorldList.h b/launcher/minecraft/WorldList.h index 10fb4e3c..96b64193 100644 --- a/launcher/minecraft/WorldList.h +++ b/launcher/minecraft/WorldList.h @@ -50,7 +50,7 @@ public: IconFileRole }; - WorldList(const QString &dir, std::shared_ptr instance); + WorldList(const QString &dir, BaseInstance* instance); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; @@ -128,7 +128,7 @@ signals: void changed(); protected: - std::shared_ptr m_instance; + BaseInstance* m_instance; QFileSystemWatcher *m_watcher; bool is_watching; QDir m_dir; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 6ae25d33..5e3b31e0 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -54,7 +54,7 @@ #include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "modplatform/ModIndex.h" -ModFolderModel::ModFolderModel(const QString& dir, std::shared_ptr instance, bool is_indexed, bool create_dir) +ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 46f5087f..d337fe29 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -75,7 +75,7 @@ public: Enable, Toggle }; - ModFolderModel(const QString &dir, std::shared_ptr instance, bool is_indexed = false, bool create_dir = true); + ModFolderModel(const QString &dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index e1973468..d2d875e4 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -16,7 +16,7 @@ #include "tasks/Task.h" -ResourceFolderModel::ResourceFolderModel(QDir dir, std::shared_ptr instance, QObject* parent, bool create_dir) +ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir) : QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this) { if (create_dir) { diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index fdf5f331..0a35e1bc 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -26,7 +26,7 @@ class QSortFilterProxyModel; class ResourceFolderModel : public QAbstractListModel { Q_OBJECT public: - ResourceFolderModel(QDir, std::shared_ptr, QObject* parent = nullptr, bool create_dir = true); + ResourceFolderModel(QDir, BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true); ~ResourceFolderModel() override; /** Starts watching the paths for changes. @@ -191,7 +191,7 @@ class ResourceFolderModel : public QAbstractListModel { bool m_can_interact = true; QDir m_dir; - std::shared_ptr m_instance; + BaseInstance* m_instance; QFileSystemWatcher m_watcher; bool m_is_watching = false; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 6eba4e2e..c12d1f23 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -45,7 +45,7 @@ #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalResourcePackParseTask.h" -ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, std::shared_ptr instance) +ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) { m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h index 66d5a074..db4b14fb 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.h +++ b/launcher/minecraft/mod/ResourcePackFolderModel.h @@ -17,7 +17,7 @@ public: NUM_COLUMNS }; - explicit ResourcePackFolderModel(const QString &dir, std::shared_ptr instance); + explicit ResourcePackFolderModel(const QString &dir, BaseInstance* instance); [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h index 6f3f2811..dc5acf80 100644 --- a/launcher/minecraft/mod/ShaderPackFolderModel.h +++ b/launcher/minecraft/mod/ShaderPackFolderModel.h @@ -6,7 +6,7 @@ class ShaderPackFolderModel : public ResourceFolderModel { Q_OBJECT public: - explicit ShaderPackFolderModel(const QString& dir, std::shared_ptr instance) + explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) {} }; diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 1e218537..c6609ed1 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -39,7 +39,7 @@ #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalTexturePackParseTask.h" -TexturePackFolderModel::TexturePackFolderModel(const QString& dir, std::shared_ptr instance) +TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) {} diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h index 246997bd..425a71e4 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.h +++ b/launcher/minecraft/mod/TexturePackFolderModel.h @@ -43,7 +43,7 @@ class TexturePackFolderModel : public ResourceFolderModel Q_OBJECT public: - explicit TexturePackFolderModel(const QString &dir, std::shared_ptr instance); + explicit TexturePackFolderModel(const QString &dir, BaseInstance* instance); [[nodiscard]] Task* createUpdateTask() override; [[nodiscard]] Task* createParseTask(Resource&) override; }; diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 64ed7673..aafaf220 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -99,7 +99,7 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString // NOTE: m_buttons must be initialized before PageContainer, because it indirectly accesses m_buttons through setSuggestedPack! Do not move this below. m_buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - m_container = new PageContainer(this); + m_container = new PageContainer(this, {}, this); m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding); m_container->layout()->setContentsMargins(0, 0, 0, 0); ui->verticalLayout->insertWidget(2, m_container); diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index edb7d063..d2a8d33e 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -89,7 +89,7 @@ void ResourceDownloadDialog::reject() // won't work with subclasses if we put it in this ctor. void ResourceDownloadDialog::initializeContainer() { - m_container = new PageContainer(this); + m_container = new PageContainer(this, {}, this); m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding); m_container->layout()->setContentsMargins(0, 0, 0, 0); m_vertical_layout.addWidget(m_container); diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp index 0a06a351..b9b17b42 100644 --- a/launcher/ui/widgets/PageContainer.cpp +++ b/launcher/ui/widgets/PageContainer.cpp @@ -87,7 +87,9 @@ PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId, auto pages = pageProvider->getPages(); for (auto page : pages) { - page->stackIndex = m_pageStack->addWidget(dynamic_cast(page)); + auto widget = dynamic_cast(page); + widget->setParent(this); + page->stackIndex = m_pageStack->addWidget(widget); page->listIndex = counter; page->setParentContainer(this); counter++; diff --git a/tests/ResourceFolderModel_test.cpp b/tests/ResourceFolderModel_test.cpp index 054d81c4..962d89f1 100644 --- a/tests/ResourceFolderModel_test.cpp +++ b/tests/ResourceFolderModel_test.cpp @@ -90,9 +90,7 @@ slots: QEventLoop loop; - InstancePtr instance; - - ModFolderModel m(tempDir.path(), instance, true); + ModFolderModel m(tempDir.path(), nullptr, true); connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); @@ -116,8 +114,7 @@ slots: QString folder = source + '/'; QTemporaryDir tempDir; QEventLoop loop; - InstancePtr instance; - ModFolderModel m(tempDir.path(), instance, true); + ModFolderModel m(tempDir.path(), nullptr, true); connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); @@ -140,8 +137,7 @@ slots: void test_addFromWatch() { QString source = QFINDTESTDATA("testdata/ResourceFolderModel"); - InstancePtr instance; - ModFolderModel model(source, instance); + ModFolderModel model(source, nullptr); QCOMPARE(model.size(), 0); @@ -161,9 +157,7 @@ slots: QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar"); QTemporaryDir tmp; - InstancePtr instance; - - ResourceFolderModel model(QDir(tmp.path()), instance); + ResourceFolderModel model(QDir(tmp.path()), nullptr); QCOMPARE(model.size(), 0); @@ -214,8 +208,7 @@ slots: QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar"); QTemporaryDir tmp; - InstancePtr instance; - ResourceFolderModel model(tmp.path(), instance); + ResourceFolderModel model(tmp.path(), nullptr); QCOMPARE(model.size(), 0); -- cgit From 1840505a0f887ebfc2c719113873ea3345b133fb Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sat, 3 Jun 2023 00:04:06 +0300 Subject: Fix crash when selecting same mod from different providers (#1029) --- launcher/ResourceDownloadTask.cpp | 49 +++++++------ launcher/ResourceDownloadTask.h | 58 ++++++++------- launcher/modplatform/ModIndex.h | 35 +++++---- launcher/modplatform/flame/FlameCheckUpdate.cpp | 22 +++--- .../modplatform/modrinth/ModrinthCheckUpdate.cpp | 28 +++---- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 82 ++++++++++----------- launcher/ui/dialogs/ResourceDownloadDialog.h | 17 ++--- launcher/ui/pages/modplatform/ModPage.cpp | 27 ++++--- launcher/ui/pages/modplatform/ModPage.h | 15 ++-- launcher/ui/pages/modplatform/ResourceModel.cpp | 65 ++++++++++++++--- launcher/ui/pages/modplatform/ResourceModel.h | 12 +++ launcher/ui/pages/modplatform/ResourcePage.cpp | 85 +++++++++++++--------- launcher/ui/pages/modplatform/ResourcePage.h | 18 +++-- launcher/ui/pages/modplatform/ShaderPackPage.cpp | 16 ++-- launcher/ui/pages/modplatform/ShaderPackPage.h | 4 +- launcher/ui/widgets/PageContainer.cpp | 5 ++ launcher/ui/widgets/PageContainer.h | 1 + 17 files changed, 314 insertions(+), 225 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index 61b918aa..06c03c77 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -1,21 +1,21 @@ // SPDX-License-Identifier: GPL-3.0-only -/* -* Prism Launcher - Minecraft Launcher -* Copyright (c) 2022-2023 flowln -* Copyright (C) 2022 Sefa Eyeoglu -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022-2023 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "ResourceDownloadTask.h" @@ -24,14 +24,15 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" -ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack, +ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion version, const std::shared_ptr packs, - bool is_indexed) - : m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs) + bool is_indexed, + QString custom_target_folder) + : m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs), m_custom_target_folder(custom_target_folder) { if (auto model = dynamic_cast(m_pack_model.get()); model && is_indexed) { - m_update_task.reset(new LocalModUpdateTask(model->indexDir(), m_pack, m_pack_version)); + m_update_task.reset(new LocalModUpdateTask(model->indexDir(), *m_pack, m_pack_version)); connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ResourceDownloadTask::hasOldResource); addTask(m_update_task); @@ -40,13 +41,13 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack, m_filesNetJob.reset(new NetJob(tr("Resource download"), APPLICATION->network())); m_filesNetJob->setStatus(tr("Downloading resource:\n%1").arg(m_pack_version.downloadUrl)); - QDir dir { m_pack_model->dir() }; + QDir dir{ m_pack_model->dir() }; { // FIXME: Make this more generic. May require adding additional info to IndexedVersion, // or adquiring a reference to the base instance. - if (!m_pack_version.custom_target_folder.isEmpty()) { + if (!m_custom_target_folder.isEmpty()) { dir.cdUp(); - dir.cd(m_pack_version.custom_target_folder); + dir.cd(m_custom_target_folder); } } diff --git a/launcher/ResourceDownloadTask.h b/launcher/ResourceDownloadTask.h index 73ad2d07..09147c8c 100644 --- a/launcher/ResourceDownloadTask.h +++ b/launcher/ResourceDownloadTask.h @@ -1,44 +1,51 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* Prism Launcher - Minecraft Launcher -* Copyright (c) 2022-2023 flowln -* Copyright (C) 2022 Sefa Eyeoglu -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022-2023 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #pragma once #include "net/NetJob.h" #include "tasks/SequentialTask.h" -#include "modplatform/ModIndex.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h" +#include "modplatform/ModIndex.h" class ResourceFolderModel; class ResourceDownloadTask : public SequentialTask { Q_OBJECT -public: - explicit ResourceDownloadTask(ModPlatform::IndexedPack pack, ModPlatform::IndexedVersion version, const std::shared_ptr packs, bool is_indexed = true); + public: + explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, + ModPlatform::IndexedVersion version, + const std::shared_ptr packs, + bool is_indexed = true, + QString custom_target_folder = {}); const QString& getFilename() const { return m_pack_version.fileName; } - const QString& getCustomPath() const { return m_pack_version.custom_target_folder; } + const QString& getCustomPath() const { return m_custom_target_folder; } const QVariant& getVersionID() const { return m_pack_version.fileId; } + const QString& getName() const { return m_pack->name; } + ModPlatform::IndexedPack::Ptr getPack() { return m_pack; } -private: - ModPlatform::IndexedPack m_pack; + private: + ModPlatform::IndexedPack::Ptr m_pack; ModPlatform::IndexedVersion m_pack_version; const std::shared_ptr m_pack_model; + QString m_custom_target_folder; NetJob::Ptr m_filesNetJob; LocalModUpdateTask::Ptr m_update_task; @@ -47,11 +54,8 @@ private: void downloadFailed(QString reason); void downloadSucceeded(); - std::tuple to_delete {"", ""}; + std::tuple to_delete{ "", "" }; -private slots: + private slots: void hasOldResource(QString name, QString filename); }; - - - diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 8d0223f9..82da2ab2 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -1,20 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #pragma once @@ -69,7 +69,6 @@ struct IndexedVersion { // For internal use, not provided by APIs bool is_currently_selected = false; - QString custom_target_folder; }; struct ExtraPackData { @@ -116,12 +115,12 @@ struct IndexedPack { if (!versionsLoaded) return false; - return std::any_of(versions.constBegin(), versions.constEnd(), - [](auto const& v) { return v.is_currently_selected; }); + return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; }); } }; } // namespace ModPlatform Q_DECLARE_METATYPE(ModPlatform::IndexedPack) +Q_DECLARE_METATYPE(ModPlatform::IndexedPack::Ptr) Q_DECLARE_METATYPE(ModPlatform::ResourceProvider) diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index 06a89502..e09aeb3d 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -3,6 +3,7 @@ #include "FlameModIndex.h" #include +#include #include "FileSystem.h" #include "Json.h" @@ -129,8 +130,7 @@ void FlameCheckUpdate::executeTask() setStatus(tr("Getting API response from CurseForge for '%1'...").arg(mod->name())); setProgress(i++, m_mods.size()); - ModPlatform::IndexedPack pack{ mod->metadata()->project_id.toString() }; - auto latest_ver = api.getLatestVersion({ pack, m_game_versions, m_loaders }); + auto latest_ver = api.getLatestVersion({ { mod->metadata()->project_id.toString() }, m_game_versions, m_loaders }); // Check if we were aborted while getting the latest version if (m_was_aborted) { @@ -156,15 +156,15 @@ void FlameCheckUpdate::executeTask() if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ModStatus::NotInstalled)) { // Fake pack with the necessary info to pass to the download task :) - ModPlatform::IndexedPack pack; - pack.name = mod->name(); - pack.slug = mod->metadata()->slug; - pack.addonId = mod->metadata()->project_id; - pack.websiteUrl = mod->homeurl(); + auto pack = std::make_shared(); + pack->name = mod->name(); + pack->slug = mod->metadata()->slug; + pack->addonId = mod->metadata()->project_id; + pack->websiteUrl = mod->homeurl(); for (auto& author : mod->authors()) - pack.authors.append({ author }); - pack.description = mod->description(); - pack.provider = ModPlatform::ResourceProvider::FLAME; + pack->authors.append({ author }); + pack->description = mod->description(); + pack->provider = ModPlatform::ResourceProvider::FLAME; auto old_version = mod->version(); if (old_version.isEmpty() && mod->status() != ModStatus::NotInstalled) { @@ -173,7 +173,7 @@ void FlameCheckUpdate::executeTask() } auto download_task = makeShared(pack, latest_ver, m_mods_folder); - m_updatable.emplace_back(pack.name, mod->metadata()->hash, old_version, latest_ver.version, + m_updatable.emplace_back(pack->name, mod->metadata()->hash, old_version, latest_ver.version, api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), ModPlatform::ResourceProvider::FLAME, download_task); } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index d1be7209..4fe91ce7 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -54,7 +54,7 @@ void ModrinthCheckUpdate::executeTask() if (mod->metadata()->hash_format != best_hash_type) { auto hash_task = Hashing::createModrinthHasher(mod->fileinfo().absoluteFilePath()); connect(hash_task.get(), &Task::succeeded, [&] { - QString hash (hash_task->getResult()); + QString hash(hash_task->getResult()); hashes.append(hash); mappings.insert(hash, mod); }); @@ -67,7 +67,7 @@ void ModrinthCheckUpdate::executeTask() } QEventLoop loop; - connect(&hashing_task, &Task::finished, [&loop]{ loop.quit(); }); + connect(&hashing_task, &Task::finished, [&loop] { loop.quit(); }); hashing_task.start(); loop.exec(); @@ -112,7 +112,8 @@ void ModrinthCheckUpdate::executeTask() // so we may want to filter it QString loader_filter; if (m_loaders.has_value()) { - static auto flags = { ResourceAPI::ModLoaderType::Forge, ResourceAPI::ModLoaderType::Fabric, ResourceAPI::ModLoaderType::Quilt }; + static auto flags = { ResourceAPI::ModLoaderType::Forge, ResourceAPI::ModLoaderType::Fabric, + ResourceAPI::ModLoaderType::Quilt }; for (auto flag : flags) { if (m_loaders.value().testFlag(flag)) { loader_filter = api.getModLoaderString(flag); @@ -122,7 +123,8 @@ void ModrinthCheckUpdate::executeTask() } // Currently, we rely on a couple heuristics to determine whether an update is actually available or not: - // - The file needs to be preferred: It is either the primary file, or the one found via (explicit) usage of the loader_filter + // - The file needs to be preferred: It is either the primary file, or the one found via (explicit) usage of the + // loader_filter // - The version reported by the JAR is different from the version reported by the indexed version (it's usually the case) // Such is the pain of having arbitrary files for a given version .-. @@ -149,19 +151,19 @@ void ModrinthCheckUpdate::executeTask() continue; // Fake pack with the necessary info to pass to the download task :) - ModPlatform::IndexedPack pack; - pack.name = mod->name(); - pack.slug = mod->metadata()->slug; - pack.addonId = mod->metadata()->project_id; - pack.websiteUrl = mod->homeurl(); + auto pack = std::make_shared(); + pack->name = mod->name(); + pack->slug = mod->metadata()->slug; + pack->addonId = mod->metadata()->project_id; + pack->websiteUrl = mod->homeurl(); for (auto& author : mod->authors()) - pack.authors.append({ author }); - pack.description = mod->description(); - pack.provider = ModPlatform::ResourceProvider::MODRINTH; + pack->authors.append({ author }); + pack->description = mod->description(); + pack->provider = ModPlatform::ResourceProvider::MODRINTH; auto download_task = makeShared(pack, project_ver, m_mods_folder); - m_updatable.emplace_back(pack.name, hash, mod->version(), project_ver.version_number, project_ver.changelog, + m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task); } } diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index d2a8d33e..61c48e75 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -20,14 +20,15 @@ #include "ResourceDownloadDialog.h" #include +#include #include "Application.h" #include "ResourceDownloadTask.h" #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourcePackFolderModel.h" -#include "minecraft/mod/TexturePackFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h" +#include "minecraft/mod/TexturePackFolderModel.h" #include "ui/dialogs/ReviewMessageBox.h" @@ -41,7 +42,10 @@ namespace ResourceDownload { ResourceDownloadDialog::ResourceDownloadDialog(QWidget* parent, const std::shared_ptr base_model) - : QDialog(parent), m_base_model(base_model), m_buttons(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel), m_vertical_layout(this) + : QDialog(parent) + , m_base_model(base_model) + , m_buttons(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel) + , m_vertical_layout(this) { setObjectName(QStringLiteral("ResourceDownloadDialog")); @@ -102,7 +106,8 @@ void ResourceDownloadDialog::initializeContainer() void ResourceDownloadDialog::connectButtons() { auto OkButton = m_buttons.button(QDialogButtonBox::Ok); - OkButton->setToolTip(tr("Opens a new popup to review your selected %1 and confirm your selection. Shortcut: Ctrl+Return").arg(resourcesString())); + OkButton->setToolTip( + tr("Opens a new popup to review your selected %1 and confirm your selection. Shortcut: Ctrl+Return").arg(resourcesString())); connect(OkButton, &QPushButton::clicked, this, &ResourceDownloadDialog::confirm); auto CancelButton = m_buttons.button(QDialogButtonBox::Cancel); @@ -114,21 +119,24 @@ void ResourceDownloadDialog::connectButtons() void ResourceDownloadDialog::confirm() { - auto keys = m_selected.keys(); - keys.sort(Qt::CaseInsensitive); + auto selected = getTasks(); + std::sort(selected.begin(), selected.end(), [](const DownloadTaskPtr& a, const DownloadTaskPtr& b) { + return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0; + }); auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString())); confirm_dialog->retranslateUi(resourcesString()); - for (auto& task : keys) { - auto selected = m_selected.constFind(task).value(); - confirm_dialog->appendResource({ task, selected->getFilename(), selected->getCustomPath() }); + for (auto& task : selected) { + confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath() }); } if (confirm_dialog->exec()) { auto deselected = confirm_dialog->deselectedResources(); - for (auto name : deselected) { - m_selected.remove(name); + for (auto page : m_container->getPages()) { + auto res = static_cast(page); + for (auto name : deselected) + res->removeResourceFromPage(name); } this->accept(); @@ -145,46 +153,39 @@ ResourcePage* ResourceDownloadDialog::getSelectedPage() return m_selectedPage; } -void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, bool is_indexed) +void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver) { - removeResource(pack, ver); - - ver.is_currently_selected = true; - m_selected.insert(pack.name, makeShared(pack, ver, getBaseModel(), is_indexed)); - - m_buttons.button(QDialogButtonBox::Ok)->setEnabled(!m_selected.isEmpty()); + removeResource(pack->name); + m_selectedPage->addResourceToPage(pack, ver, getBaseModel()); + setButtonStatus(); } -static ModPlatform::IndexedVersion& getVersionWithID(ModPlatform::IndexedPack& pack, QVariant id) +void ResourceDownloadDialog::removeResource(const QString& pack_name) { - Q_ASSERT(pack.versionsLoaded); - auto it = std::find_if(pack.versions.begin(), pack.versions.end(), [id](auto const& v) { return v.fileId == id; }); - Q_ASSERT(it != pack.versions.end()); - return *it; + for (auto page : m_container->getPages()) { + static_cast(page)->removeResourceFromPage(pack_name); + } + setButtonStatus(); } -void ResourceDownloadDialog::removeResource(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver) +void ResourceDownloadDialog::setButtonStatus() { - if (auto selected_task_it = m_selected.find(pack.name); selected_task_it != m_selected.end()) { - auto selected_task = *selected_task_it; - auto old_version_id = selected_task->getVersionID(); - - // If the new and old version IDs don't match, search for the old one and deselect it. - if (ver.fileId != old_version_id) - getVersionWithID(pack, old_version_id).is_currently_selected = false; + auto selected = false; + for (auto page : m_container->getPages()) { + auto res = static_cast(page); + selected = selected || res->hasSelectedPacks(); } - - // Deselect the new version too, since all versions of that pack got removed. - ver.is_currently_selected = false; - - m_selected.remove(pack.name); - - m_buttons.button(QDialogButtonBox::Ok)->setEnabled(!m_selected.isEmpty()); + m_buttons.button(QDialogButtonBox::Ok)->setEnabled(selected); } const QList ResourceDownloadDialog::getTasks() { - return m_selected.values(); + QList selected; + for (auto page : m_container->getPages()) { + auto res = static_cast(page); + selected.append(res->selectedPacks()); + } + return selected; } void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* selected) @@ -205,8 +206,6 @@ void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* s m_selectedPage->setSearchTerm(prev_page->getSearchTerm()); } - - ModDownloadDialog::ModDownloadDialog(QWidget* parent, const std::shared_ptr& mods, BaseInstance* instance) : ResourceDownloadDialog(parent, mods), m_instance(instance) { @@ -232,7 +231,6 @@ QList ModDownloadDialog::getPages() return pages; } - ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent, const std::shared_ptr& resource_packs, BaseInstance* instance) @@ -258,7 +256,6 @@ QList ResourcePackDownloadDialog::getPages() return pages; } - TexturePackDownloadDialog::TexturePackDownloadDialog(QWidget* parent, const std::shared_ptr& resource_packs, BaseInstance* instance) @@ -284,7 +281,6 @@ QList TexturePackDownloadDialog::getPages() return pages; } - ShaderPackDownloadDialog::ShaderPackDownloadDialog(QWidget* parent, const std::shared_ptr& shaders, BaseInstance* instance) diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index 5678dc8b..5b5b48c6 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -62,8 +62,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { bool selectPage(QString pageId); ResourcePage* getSelectedPage(); - void addResource(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&, bool is_indexed = false); - void removeResource(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&); + void addResource(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&); + void removeResource(const QString&); const QList getTasks(); [[nodiscard]] const std::shared_ptr getBaseModel() const { return m_base_model; } @@ -79,6 +79,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { protected: [[nodiscard]] virtual QString geometrySaveKey() const { return ""; } + void setButtonStatus(); protected: const std::shared_ptr m_base_model; @@ -88,12 +89,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { QDialogButtonBox m_buttons; QVBoxLayout m_vertical_layout; - - QHash m_selected; }; - - class ModDownloadDialog final : public ResourceDownloadDialog { Q_OBJECT @@ -135,8 +132,8 @@ class TexturePackDownloadDialog final : public ResourceDownloadDialog { public: explicit TexturePackDownloadDialog(QWidget* parent, - const std::shared_ptr& resource_packs, - BaseInstance* instance); + const std::shared_ptr& resource_packs, + BaseInstance* instance); ~TexturePackDownloadDialog() override = default; //: String that gets appended to the texture pack download dialog title ("Download " + resourcesString()) @@ -153,9 +150,7 @@ class ShaderPackDownloadDialog final : public ResourceDownloadDialog { Q_OBJECT public: - explicit ShaderPackDownloadDialog(QWidget* parent, - const std::shared_ptr& shader_packs, - BaseInstance* instance); + explicit ShaderPackDownloadDialog(QWidget* parent, const std::shared_ptr& shader_packs, BaseInstance* instance); ~ShaderPackDownloadDialog() override = default; //: String that gets appended to the shader pack download dialog title ("Download " + resourcesString()) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 04be43ad..95064d16 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -55,8 +55,7 @@ namespace ResourceDownload { -ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance& instance) - : ResourcePage(dialog, instance) +ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance) { connect(m_ui->searchButton, &QPushButton::clicked, this, &ModPage::triggerSearch); connect(m_ui->resourceFilterButton, &QPushButton::clicked, this, &ModPage::filterMods); @@ -75,12 +74,10 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) m_filter_widget->setInstance(&static_cast(m_base_instance)); m_filter = m_filter_widget->getFilter(); - connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, [&]{ - m_ui->searchButton->setStyleSheet("text-decoration: underline"); - }); - connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, [&]{ - m_ui->searchButton->setStyleSheet("text-decoration: none"); - }); + connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, + [&] { m_ui->searchButton->setStyleSheet("text-decoration: underline"); }); + connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, + [&] { m_ui->searchButton->setStyleSheet("text-decoration: none"); }); } /******** Callbacks to events in the UI (set up in the derived classes) ********/ @@ -125,11 +122,11 @@ void ModPage::updateVersionList() QString mcVersion = packProfile->getComponentVersion("net.minecraft"); auto current_pack = getCurrentPack(); - for (int i = 0; i < current_pack.versions.size(); i++) { - auto version = current_pack.versions[i]; + for (int i = 0; i < current_pack->versions.size(); i++) { + auto version = current_pack->versions[i]; bool valid = false; - for(auto& mcVer : m_filter->versions){ - //NOTE: Flame doesn't care about loader, so passing it changes nothing. + for (auto& mcVer : m_filter->versions) { + // NOTE: Flame doesn't care about loader, so passing it changes nothing. if (validateVersion(version, mcVer.toString(), packProfile->getModLoaders())) { valid = true; break; @@ -148,10 +145,12 @@ void ModPage::updateVersionList() updateSelectionButton(); } -void ModPage::addResourceToDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) +void ModPage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, + ModPlatform::IndexedVersion& version, + const std::shared_ptr base_model) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); - m_parent_dialog->addResource(pack, version, is_indexed); + m_model->addPack(pack, version, base_model, is_indexed); } } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 4ea55efa..5510c191 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -8,8 +8,8 @@ #include "modplatform/ModIndex.h" -#include "ui/pages/modplatform/ResourcePage.h" #include "ui/pages/modplatform/ModModel.h" +#include "ui/pages/modplatform/ResourcePage.h" #include "ui/widgets/ModFilterWidget.h" namespace Ui { @@ -25,13 +25,14 @@ class ModPage : public ResourcePage { Q_OBJECT public: - template + template static T* create(ModDownloadDialog* dialog, BaseInstance& instance) { auto page = new T(dialog, instance); auto model = static_cast(page->getModel()); - auto filter_widget = ModFilterWidget::create(static_cast(instance).getPackProfile()->getComponentVersion("net.minecraft"), page); + auto filter_widget = + ModFilterWidget::create(static_cast(instance).getPackProfile()->getComponentVersion("net.minecraft"), page); page->setFilterWidget(filter_widget); model->setFilter(page->getFilter()); @@ -48,9 +49,13 @@ class ModPage : public ResourcePage { [[nodiscard]] QMap urlHandlers() const override; - void addResourceToDialog(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&) override; + void addResourceToPage(ModPlatform::IndexedPack::Ptr, + ModPlatform::IndexedVersion&, + const std::shared_ptr) override; - virtual auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const -> bool = 0; + virtual auto validateVersion(ModPlatform::IndexedVersion& ver, + QString mineVer, + std::optional loaders = {}) const -> bool = 0; [[nodiscard]] bool supportsFiltering() const override { return true; }; auto getFilter() const -> const std::shared_ptr { return m_filter; } diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 472aa851..a5ea1ca9 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -6,9 +6,11 @@ #include #include +#include #include #include #include +#include #include #include "Application.h" @@ -65,7 +67,7 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant return QSize(0, 58); case Qt::UserRole: { QVariant v; - v.setValue(*pack); + v.setValue(pack); return v; } // Custom data @@ -103,7 +105,7 @@ bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, int if (pos >= m_packs.size() || pos < 0 || !index.isValid()) return false; - m_packs[pos] = std::make_shared(value.value()); + m_packs[pos] = value.value(); emit dataChanged(index, index); return true; @@ -230,7 +232,7 @@ void ResourceModel::clearData() void ResourceModel::runSearchJob(Task::Ptr ptr) { - m_current_search_job.reset(ptr); // clean up first + m_current_search_job.reset(ptr); // clean up first m_current_search_job->start(); } void ResourceModel::runInfoJob(Task::Ptr ptr) @@ -336,7 +338,15 @@ void ResourceModel::searchRequestSucceeded(QJsonDocument& doc) ModPlatform::IndexedPack::Ptr pack = std::make_shared(); try { loadIndexedPack(*pack, packObj); - newList.append(pack); + if (auto sel = std::find_if(m_selected.begin(), m_selected.end(), + [&pack](const DownloadTaskPtr i) { + const auto ipack = i->getPack(); + return ipack->provider == pack->provider && ipack->addonId == pack->addonId; + }); + sel != m_selected.end()) { + newList.append(sel->get()->getPack()); + } else + newList.append(pack); } catch (const JSONValidationError& e) { qWarning() << "Error while loading resource from " << debugName() << ": " << e.cause(); continue; @@ -390,15 +400,15 @@ void ResourceModel::searchRequestAborted() void ResourceModel::versionRequestSucceeded(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index) { - auto current_pack = data(index, Qt::UserRole).value(); + auto current_pack = data(index, Qt::UserRole).value(); // Check if the index is still valid for this resource or not - if (pack.addonId != current_pack.addonId) + if (pack.addonId != current_pack->addonId) return; try { auto arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array(); - loadIndexedPackVersions(current_pack, arr); + loadIndexedPackVersions(*current_pack, arr); } catch (const JSONValidationError& e) { qDebug() << doc; qWarning() << "Error while reading " << debugName() << " resource version: " << e.cause(); @@ -417,15 +427,15 @@ void ResourceModel::versionRequestSucceeded(QJsonDocument& doc, ModPlatform::Ind void ResourceModel::infoRequestSucceeded(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index) { - auto current_pack = data(index, Qt::UserRole).value(); + auto current_pack = data(index, Qt::UserRole).value(); // Check if the index is still valid for this resource or not - if (pack.addonId != current_pack.addonId) + if (pack.addonId != current_pack->addonId) return; try { auto obj = Json::requireObject(doc); - loadExtraPackInfo(current_pack, obj); + loadExtraPackInfo(*current_pack, obj); } catch (const JSONValidationError& e) { qDebug() << doc; qWarning() << "Error while reading " << debugName() << " resource info: " << e.cause(); @@ -442,4 +452,39 @@ void ResourceModel::infoRequestSucceeded(QJsonDocument& doc, ModPlatform::Indexe emit projectInfoUpdated(); } +void ResourceModel::addPack(ModPlatform::IndexedPack::Ptr pack, + ModPlatform::IndexedVersion& version, + const std::shared_ptr packs, + bool is_indexed, + QString custom_target_folder) +{ + version.is_currently_selected = true; + m_selected.append(makeShared(pack, version, packs, is_indexed, custom_target_folder)); +} + +void ResourceModel::removePack(const QString& rem) +{ + auto pred = [&rem](const DownloadTaskPtr i) { return rem == i->getName(); }; +#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) + m_selected.removeIf(pred); +#else + { + for (auto it = m_selected.begin(); it != m_selected.end();) + if (pred(*it)) + it = m_selected.erase(it); + else + ++it; + } +#endif + auto pack = std::find_if(m_packs.begin(), m_packs.end(), [&rem](const ModPlatform::IndexedPack::Ptr i) { return rem == i->name; }); + if (pack == m_packs.end()) { // ignore it if is not in the current search + return; + } + if (!pack->get()->versionsLoaded) { + return; + } + for (auto& ver : pack->get()->versions) + ver.is_currently_selected = false; +} + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index 1ec42cda..69e23473 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -10,6 +10,7 @@ #include "QObjectPtr.h" +#include "ResourceDownloadTask.h" #include "modplatform/ResourceAPI.h" #include "tasks/ConcurrentTask.h" @@ -29,6 +30,8 @@ class ResourceModel : public QAbstractListModel { Q_PROPERTY(QString search_term MEMBER m_search_term WRITE setSearchTerm) public: + using DownloadTaskPtr = shared_qobject_ptr; + ResourceModel(ResourceAPI* api); ~ResourceModel() override; @@ -80,6 +83,14 @@ class ResourceModel : public QAbstractListModel { /** Gets the icon at the URL for the given index. If it's not fetched yet, fetch it and update when fisinhed. */ std::optional getIcon(QModelIndex&, const QUrl&); + void addPack(ModPlatform::IndexedPack::Ptr pack, + ModPlatform::IndexedVersion& version, + const std::shared_ptr packs, + bool is_indexed = false, + QString custom_target_folder = {}); + void removePack(const QString& rem); + QList selectedPacks() { return m_selected; } + protected: /** Resets the model's data. */ void clearData(); @@ -124,6 +135,7 @@ class ResourceModel : public QAbstractListModel { QSet m_failed_icon_actions; QList m_packs; + QList m_selected; // HACK: We need this to prevent callbacks from calling the model after it has already been deleted. // This leaks a tiny bit of memory per time the user has opened a resource dialog. How to make this better? diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index f75bb886..736034ad 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -37,6 +37,7 @@ */ #include "ResourcePage.h" +#include "modplatform/ModIndex.h" #include "ui_ResourcePage.h" #include @@ -158,16 +159,16 @@ void ResourcePage::addSortings() m_ui->sortByBox->addItem(sorting.readable_name, QVariant(sorting.index)); } -bool ResourcePage::setCurrentPack(ModPlatform::IndexedPack pack) +bool ResourcePage::setCurrentPack(ModPlatform::IndexedPack::Ptr pack) { QVariant v; v.setValue(pack); return m_model->setData(m_ui->packView->currentIndex(), v, Qt::UserRole); } -ModPlatform::IndexedPack ResourcePage::getCurrentPack() const +ModPlatform::IndexedPack::Ptr ResourcePage::getCurrentPack() const { - return m_model->data(m_ui->packView->currentIndex(), Qt::UserRole).value(); + return m_model->data(m_ui->packView->currentIndex(), Qt::UserRole).value(); } void ResourcePage::updateUi() @@ -175,14 +176,14 @@ void ResourcePage::updateUi() auto current_pack = getCurrentPack(); QString text = ""; - QString name = current_pack.name; + QString name = current_pack->name; - if (current_pack.websiteUrl.isEmpty()) + if (current_pack->websiteUrl.isEmpty()) text = name; else - text = "" + name + ""; + text = "websiteUrl + "\">" + name + ""; - if (!current_pack.authors.empty()) { + if (!current_pack->authors.empty()) { auto authorToStr = [](ModPlatform::ModpackAuthor& author) -> QString { if (author.url.isEmpty()) { return author.name; @@ -190,44 +191,44 @@ void ResourcePage::updateUi() return QString("%2").arg(author.url, author.name); }; QStringList authorStrs; - for (auto& author : current_pack.authors) { + for (auto& author : current_pack->authors) { authorStrs.push_back(authorToStr(author)); } text += "
" + tr(" by ") + authorStrs.join(", "); } - if (current_pack.extraDataLoaded) { - if (!current_pack.extraData.donate.isEmpty()) { + if (current_pack->extraDataLoaded) { + if (!current_pack->extraData.donate.isEmpty()) { text += "

" + tr("Donate information: "); auto donateToStr = [](ModPlatform::DonationData& donate) -> QString { return QString("%2").arg(donate.url, donate.platform); }; QStringList donates; - for (auto& donate : current_pack.extraData.donate) { + for (auto& donate : current_pack->extraData.donate) { donates.append(donateToStr(donate)); } text += donates.join(", "); } - if (!current_pack.extraData.issuesUrl.isEmpty() || !current_pack.extraData.sourceUrl.isEmpty() || - !current_pack.extraData.wikiUrl.isEmpty() || !current_pack.extraData.discordUrl.isEmpty()) { + if (!current_pack->extraData.issuesUrl.isEmpty() || !current_pack->extraData.sourceUrl.isEmpty() || + !current_pack->extraData.wikiUrl.isEmpty() || !current_pack->extraData.discordUrl.isEmpty()) { text += "

" + tr("External links:") + "
"; } - if (!current_pack.extraData.issuesUrl.isEmpty()) - text += "- " + tr("Issues: %1").arg(current_pack.extraData.issuesUrl) + "
"; - if (!current_pack.extraData.wikiUrl.isEmpty()) - text += "- " + tr("Wiki: %1").arg(current_pack.extraData.wikiUrl) + "
"; - if (!current_pack.extraData.sourceUrl.isEmpty()) - text += "- " + tr("Source code: %1").arg(current_pack.extraData.sourceUrl) + "
"; - if (!current_pack.extraData.discordUrl.isEmpty()) - text += "- " + tr("Discord: %1").arg(current_pack.extraData.discordUrl) + "
"; + if (!current_pack->extraData.issuesUrl.isEmpty()) + text += "- " + tr("Issues: %1").arg(current_pack->extraData.issuesUrl) + "
"; + if (!current_pack->extraData.wikiUrl.isEmpty()) + text += "- " + tr("Wiki: %1").arg(current_pack->extraData.wikiUrl) + "
"; + if (!current_pack->extraData.sourceUrl.isEmpty()) + text += "- " + tr("Source code: %1").arg(current_pack->extraData.sourceUrl) + "
"; + if (!current_pack->extraData.discordUrl.isEmpty()) + text += "- " + tr("Discord: %1").arg(current_pack->extraData.discordUrl) + "
"; } text += "
"; m_ui->packDescription->setHtml( - text + (current_pack.extraData.body.isEmpty() ? current_pack.description : markdownToHTML(current_pack.extraData.body))); + text + (current_pack->extraData.body.isEmpty() ? current_pack->description : markdownToHTML(current_pack->extraData.body))); m_ui->packDescription->flush(); } @@ -239,7 +240,7 @@ void ResourcePage::updateSelectionButton() } m_ui->resourceSelectionButton->setEnabled(true); - if (!getCurrentPack().isVersionSelected(m_selected_version_index)) { + if (!getCurrentPack()->isVersionSelected(m_selected_version_index)) { m_ui->resourceSelectionButton->setText(tr("Select %1 for download").arg(resourceString())); } else { m_ui->resourceSelectionButton->setText(tr("Deselect %1 for download").arg(resourceString())); @@ -254,12 +255,12 @@ void ResourcePage::updateVersionList() m_ui->versionSelectionBox->clear(); m_ui->versionSelectionBox->blockSignals(false); - for (int i = 0; i < current_pack.versions.size(); i++) { - auto& version = current_pack.versions[i]; + for (int i = 0; i < current_pack->versions.size(); i++) { + auto& version = current_pack->versions[i]; if (optedOut(version)) continue; - m_ui->versionSelectionBox->addItem(current_pack.versions[i].version, QVariant(i)); + m_ui->versionSelectionBox->addItem(current_pack->versions[i].version, QVariant(i)); } if (m_ui->versionSelectionBox->count() == 0) { @@ -279,7 +280,7 @@ void ResourcePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) auto current_pack = getCurrentPack(); bool request_load = false; - if (!current_pack.versionsLoaded) { + if (!current_pack->versionsLoaded) { m_ui->resourceSelectionButton->setText(tr("Loading versions...")); m_ui->resourceSelectionButton->setEnabled(false); @@ -288,7 +289,7 @@ void ResourcePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) updateVersionList(); } - if (!current_pack.extraDataLoaded) + if (!current_pack->extraDataLoaded) request_load = true; if (request_load) @@ -308,14 +309,26 @@ void ResourcePage::onVersionSelectionChanged(QString data) updateSelectionButton(); } -void ResourcePage::addResourceToDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) +void ResourcePage::addResourceToDialog(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& version) { m_parent_dialog->addResource(pack, version); } -void ResourcePage::removeResourceFromDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) +void ResourcePage::removeResourceFromDialog(const QString& pack_name) { - m_parent_dialog->removeResource(pack, version); + m_parent_dialog->removeResource(pack_name); +} + +void ResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, + ModPlatform::IndexedVersion& ver, + const std::shared_ptr base_model) +{ + m_model->addPack(pack, ver, base_model); +} + +void ResourcePage::removeResourceFromPage(const QString& name) +{ + m_model->removePack(name); } void ResourcePage::onResourceSelected() @@ -324,12 +337,12 @@ void ResourcePage::onResourceSelected() return; auto current_pack = getCurrentPack(); - if (!current_pack.versionsLoaded) + if (!current_pack->versionsLoaded) return; - auto& version = current_pack.versions[m_selected_version_index]; + auto& version = current_pack->versions[m_selected_version_index]; if (version.is_currently_selected) - removeResourceFromDialog(current_pack, version); + removeResourceFromDialog(current_pack->name); else addResourceToDialog(current_pack, version); @@ -340,7 +353,7 @@ void ResourcePage::onResourceSelected() updateSelectionButton(); /* Force redraw on the resource list when the selection changes */ - m_ui->packView->adjustSize(); + m_ui->packView->repaint(); } void ResourcePage::openUrl(const QUrl& url) @@ -370,7 +383,7 @@ void ResourcePage::openUrl(const QUrl& url) const QString slug = match.captured(1); // ensure the user isn't opening the same mod - if (slug != getCurrentPack().slug) { + if (slug != getCurrentPack()->slug) { m_parent_dialog->selectPage(page); auto newPage = m_parent_dialog->getSelectedPage(); diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index 1896d53e..b4a87f57 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -7,10 +7,12 @@ #include #include +#include "ResourceDownloadTask.h" #include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" #include "ui/pages/BasePage.h" +#include "ui/pages/modplatform/ResourceModel.h" #include "ui/widgets/ProgressWidget.h" namespace Ui { @@ -27,6 +29,7 @@ class ResourceModel; class ResourcePage : public QWidget, public BasePage { Q_OBJECT public: + using DownloadTaskPtr = shared_qobject_ptr; ~ResourcePage() override; /* Affects what the user sees */ @@ -57,8 +60,8 @@ class ResourcePage : public QWidget, public BasePage { /** Programatically set the term in the search bar. */ void setSearchTerm(QString); - [[nodiscard]] bool setCurrentPack(ModPlatform::IndexedPack); - [[nodiscard]] auto getCurrentPack() const -> ModPlatform::IndexedPack; + [[nodiscard]] bool setCurrentPack(ModPlatform::IndexedPack::Ptr); + [[nodiscard]] auto getCurrentPack() const -> ModPlatform::IndexedPack::Ptr; [[nodiscard]] auto getDialog() const -> const ResourceDownloadDialog* { return m_parent_dialog; } [[nodiscard]] auto getModel() const -> ResourceModel* { return m_model; } @@ -72,12 +75,17 @@ class ResourcePage : public QWidget, public BasePage { virtual void updateSelectionButton(); virtual void updateVersionList(); - virtual void addResourceToDialog(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&); - virtual void removeResourceFromDialog(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&); + void addResourceToDialog(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&); + void removeResourceFromDialog(const QString& pack_name); + virtual void removeResourceFromPage(const QString& name); + virtual void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, const std::shared_ptr); + + QList selectedPacks() { return m_model->selectedPacks(); } + bool hasSelectedPacks() { return !(m_model->selectedPacks().isEmpty()); } protected slots: virtual void triggerSearch() {} - + void onSelectionChanged(QModelIndex first, QModelIndex second); void onVersionSelectionChanged(QString data); void onResourceSelected(); diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.cpp b/launcher/ui/pages/modplatform/ShaderPackPage.cpp index 251c07e7..fbf94e84 100644 --- a/launcher/ui/pages/modplatform/ShaderPackPage.cpp +++ b/launcher/ui/pages/modplatform/ShaderPackPage.cpp @@ -13,8 +13,7 @@ namespace ResourceDownload { -ShaderPackResourcePage::ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance) - : ResourcePage(dialog, instance) +ShaderPackResourcePage::ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance) { connect(m_ui->searchButton, &QPushButton::clicked, this, &ShaderPackResourcePage::triggerSearch); connect(m_ui->packView, &QListView::doubleClicked, this, &ShaderPackResourcePage::onResourceSelected); @@ -38,17 +37,20 @@ QMap ShaderPackResourcePage::urlHandlers() const { QMap map; map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?modrinth\\.com\\/shaders\\/([^\\/]+)\\/?"), "modrinth"); - map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/customization\\/([^\\/]+)\\/?"), "curseforge"); + map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/customization\\/([^\\/]+)\\/?"), + "curseforge"); map.insert(QRegularExpression::anchoredPattern("minecraft\\.curseforge\\.com\\/projects\\/([^\\/]+)\\/?"), "curseforge"); return map; } -void ShaderPackResourcePage::addResourceToDialog(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& version) +void ShaderPackResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, + ModPlatform::IndexedVersion& version, + const std::shared_ptr base_model) { + QString custom_target_folder; if (version.loaders.contains(QStringLiteral("canvas"))) - version.custom_target_folder = QStringLiteral("resourcepacks"); - - m_parent_dialog->addResource(pack, version); + custom_target_folder = QStringLiteral("resourcepacks"); + m_model->addPack(pack, version, base_model, false, custom_target_folder); } } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.h b/launcher/ui/pages/modplatform/ShaderPackPage.h index 9039c4d9..fcf6d4a7 100644 --- a/launcher/ui/pages/modplatform/ShaderPackPage.h +++ b/launcher/ui/pages/modplatform/ShaderPackPage.h @@ -38,7 +38,9 @@ class ShaderPackResourcePage : public ResourcePage { [[nodiscard]] bool supportsFiltering() const override { return false; }; - void addResourceToDialog(ModPlatform::IndexedPack&, ModPlatform::IndexedVersion&) override; + void addResourceToPage(ModPlatform::IndexedPack::Ptr, + ModPlatform::IndexedVersion&, + const std::shared_ptr) override; [[nodiscard]] QMap urlHandlers() const override; diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp index b9b17b42..38a22897 100644 --- a/launcher/ui/widgets/PageContainer.cpp +++ b/launcher/ui/widgets/PageContainer.cpp @@ -137,6 +137,11 @@ BasePage* PageContainer::getPage(QString pageId) return m_model->findPageEntryById(pageId); } +const QList PageContainer::getPages() const +{ + return m_model->pages(); +} + void PageContainer::refreshContainer() { m_proxyModel->invalidate(); diff --git a/launcher/ui/widgets/PageContainer.h b/launcher/ui/widgets/PageContainer.h index 97e294dc..ad74d43a 100644 --- a/launcher/ui/widgets/PageContainer.h +++ b/launcher/ui/widgets/PageContainer.h @@ -80,6 +80,7 @@ public: virtual bool selectPage(QString pageId) override; BasePage* getPage(QString pageId) override; + const QList getPages() const; void refreshContainer() override; virtual void setParentContainer(BasePageContainer * container) -- cgit From 961285d6abad3e70a0ab748007349ef67632f138 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 5 Jun 2023 20:49:47 +0100 Subject: Add a search bar to version lists Signed-off-by: TheKodeToad --- launcher/VersionProxyModel.cpp | 21 +++++++++++-- launcher/VersionProxyModel.h | 3 ++ launcher/ui/dialogs/VersionSelectDialog.cpp | 47 ++++++++++++++++++---------- launcher/ui/widgets/JavaSettingsWidget.cpp | 2 +- launcher/ui/widgets/VersionSelectWidget.cpp | 34 ++++++++++++++++++-- launcher/ui/widgets/VersionSelectWidget.h | 48 +++++++++++++++++++++-------- 6 files changed, 122 insertions(+), 33 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index 6aba268d..d5d14c24 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,9 +55,14 @@ public: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { const auto &filters = m_parent->filters(); + const QString &search = m_parent->search(); + const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); + + if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search)) + return false; + for (auto it = filters.begin(); it != filters.end(); ++it) { - auto idx = sourceModel()->index(source_row, 0, source_parent); auto data = sourceModel()->data(idx, it.key()); auto match = data.toString(); if(!it.value()->accepts(match)) @@ -431,6 +437,7 @@ QModelIndex VersionProxyModel::getVersion(const QString& version) const void VersionProxyModel::clearFilters() { m_filters.clear(); + m_search.clear(); filterModel->filterChanged(); } @@ -440,11 +447,21 @@ void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filt filterModel->filterChanged(); } +void VersionProxyModel::setSearch(const QString &search) { + m_search = search; + filterModel->filterChanged(); +} + const VersionProxyModel::FilterMap &VersionProxyModel::filters() const { return m_filters; } +const QString &VersionProxyModel::search() const +{ + return m_search; +} + void VersionProxyModel::sourceAboutToBeReset() { beginResetModel(); diff --git a/launcher/VersionProxyModel.h b/launcher/VersionProxyModel.h index 8991c31b..6434376c 100644 --- a/launcher/VersionProxyModel.h +++ b/launcher/VersionProxyModel.h @@ -38,7 +38,9 @@ public: virtual void setSourceModel(QAbstractItemModel *sourceModel) override; const FilterMap &filters() const; + const QString &search() const; void setFilter(const BaseVersionList::ModelRoles column, Filter * filter); + void setSearch(const QString &search); void clearFilters(); QModelIndex getRecommended() const; QModelIndex getVersion(const QString & version) const; @@ -59,6 +61,7 @@ private slots: private: QList m_columns; FilterMap m_filters; + QString m_search; BaseVersionList::RoleList roles; VersionFilterModel * filterModel; bool hasRecommended = false; diff --git a/launcher/ui/dialogs/VersionSelectDialog.cpp b/launcher/ui/dialogs/VersionSelectDialog.cpp index d7880334..5feb70d2 100644 --- a/launcher/ui/dialogs/VersionSelectDialog.cpp +++ b/launcher/ui/dialogs/VersionSelectDialog.cpp @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #include "VersionSelectDialog.h" @@ -22,15 +42,10 @@ #include #include -#include "ui/dialogs/ProgressDialog.h" #include "ui/widgets/VersionSelectWidget.h" -#include "ui/dialogs/CustomMessageBox.h" #include "BaseVersion.h" #include "BaseVersionList.h" -#include "tasks/Task.h" -#include "Application.h" -#include "VersionProxyModel.h" VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable) : QDialog(parent) @@ -40,7 +55,7 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, m_verticalLayout = new QVBoxLayout(this); m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); - m_versionWidget = new VersionSelectWidget(parent); + m_versionWidget = new VersionSelectWidget(true, parent); m_verticalLayout->addWidget(m_versionWidget); m_horizontalLayout = new QHBoxLayout(); diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 15994319..c94fdd8d 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -46,7 +46,7 @@ void JavaSettingsWidget::setupUi() m_verticalLayout = new QVBoxLayout(this); m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); - m_versionWidget = new VersionSelectWidget(this); + m_versionWidget = new VersionSelectWidget(true, this); m_verticalLayout->addWidget(m_versionWidget); m_horizontalLayout = new QHBoxLayout(); diff --git a/launcher/ui/widgets/VersionSelectWidget.cpp b/launcher/ui/widgets/VersionSelectWidget.cpp index 404860d9..252e2719 100644 --- a/launcher/ui/widgets/VersionSelectWidget.cpp +++ b/launcher/ui/widgets/VersionSelectWidget.cpp @@ -8,8 +8,10 @@ #include "ui/dialogs/CustomMessageBox.h" -VersionSelectWidget::VersionSelectWidget(QWidget* parent) - : QWidget(parent) +VersionSelectWidget::VersionSelectWidget(QWidget* parent) : VersionSelectWidget(false, parent) {} + +VersionSelectWidget::VersionSelectWidget(bool focusSearch, QWidget* parent) + : QWidget(parent), focusSearch(focusSearch) { setObjectName(QStringLiteral("VersionSelectWidget")); verticalLayout = new QVBoxLayout(this); @@ -30,6 +32,12 @@ VersionSelectWidget::VersionSelectWidget(QWidget* parent) listView->setModel(m_proxyModel); verticalLayout->addWidget(listView); + search = new QLineEdit(this); + search->setPlaceholderText(tr("Search")); + search->setClearButtonEnabled(true); + verticalLayout->addWidget(search); + connect(search, &QLineEdit::textEdited, this, &VersionSelectWidget::updateSearch); + sneakyProgressBar = new QProgressBar(this); sneakyProgressBar->setObjectName(QStringLiteral("sneakyProgressBar")); sneakyProgressBar->setFormat(QStringLiteral("%p%")); @@ -89,6 +97,8 @@ void VersionSelectWidget::initialize(BaseVersionList *vlist) { listView->setEmptyMode(VersionListView::String); } + search->setFocus(); + focusSearch = false; preselect(); } } @@ -114,6 +124,7 @@ void VersionSelectWidget::loadList() loadTask->start(); } sneakyProgressBar->setHidden(false); + search->setHidden(true); } void VersionSelectWidget::onTaskSucceeded() @@ -123,6 +134,14 @@ void VersionSelectWidget::onTaskSucceeded() listView->setEmptyMode(VersionListView::String); } sneakyProgressBar->setHidden(true); + search->setHidden(false); + + if (focusSearch) + { + search->setFocus(); + focusSearch = false; + } + preselect(); loadTask = nullptr; } @@ -155,6 +174,17 @@ void VersionSelectWidget::preselect() selectRecommended(); } +void VersionSelectWidget::updateSearch(const QString &value) { + m_proxyModel->setSearch(value); + // if nothing is selected, pick the first result + if (!value.isEmpty()) { + listView->selectionModel()->setCurrentIndex( + listView->model()->index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + listView->scrollToTop(); + } else + listView->scrollTo(listView->selectionModel()->currentIndex(), QAbstractItemView::PositionAtCenter); +} + void VersionSelectWidget::selectCurrent() { if(m_currentVersion.isEmpty()) diff --git a/launcher/ui/widgets/VersionSelectWidget.h b/launcher/ui/widgets/VersionSelectWidget.h index e75efc6f..df9c8a62 100644 --- a/launcher/ui/widgets/VersionSelectWidget.h +++ b/launcher/ui/widgets/VersionSelectWidget.h @@ -1,22 +1,43 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #pragma once #include #include +#include #include "BaseVersionList.h" #include "VersionListView.h" @@ -30,7 +51,8 @@ class VersionSelectWidget: public QWidget { Q_OBJECT public: - explicit VersionSelectWidget(QWidget *parent = 0); + explicit VersionSelectWidget(QWidget *parent); + explicit VersionSelectWidget(bool focusSearch = false, QWidget *parent = 0); ~VersionSelectWidget(); //! loads the list if needed. @@ -67,6 +89,7 @@ private slots: private: void preselect(); + void updateSearch(const QString &value); private: QString m_currentVersion; @@ -75,9 +98,10 @@ private: int resizeOnColumn = 0; Task * loadTask; bool preselectedAlready = false; + bool focusSearch; -private: QVBoxLayout *verticalLayout = nullptr; VersionListView *listView = nullptr; + QLineEdit *search; QProgressBar *sneakyProgressBar = nullptr; }; -- cgit From c343036d3bfd60cb24477fcd2ae1286276236a68 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 6 Jun 2023 12:24:53 +0100 Subject: Simplify Signed-off-by: TheKodeToad --- launcher/ui/widgets/VersionSelectWidget.cpp | 29 +++++++++-------------------- launcher/ui/widgets/VersionSelectWidget.h | 1 - 2 files changed, 9 insertions(+), 21 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/ui/widgets/VersionSelectWidget.cpp b/launcher/ui/widgets/VersionSelectWidget.cpp index 252e2719..6aaffaac 100644 --- a/launcher/ui/widgets/VersionSelectWidget.cpp +++ b/launcher/ui/widgets/VersionSelectWidget.cpp @@ -36,7 +36,15 @@ VersionSelectWidget::VersionSelectWidget(bool focusSearch, QWidget* parent) search->setPlaceholderText(tr("Search")); search->setClearButtonEnabled(true); verticalLayout->addWidget(search); - connect(search, &QLineEdit::textEdited, this, &VersionSelectWidget::updateSearch); + connect(search, &QLineEdit::textEdited, [this](const QString& value) { + m_proxyModel->setSearch(value); + if (!value.isEmpty() || !listView->selectionModel()->hasSelection()) { + const QModelIndex first = listView->model()->index(0, 0); + listView->selectionModel()->setCurrentIndex(first, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + listView->scrollToTop(); + } else + listView->scrollTo(listView->selectionModel()->currentIndex(), QAbstractItemView::PositionAtCenter); + }); sneakyProgressBar = new QProgressBar(this); sneakyProgressBar->setObjectName(QStringLiteral("sneakyProgressBar")); @@ -124,7 +132,6 @@ void VersionSelectWidget::loadList() loadTask->start(); } sneakyProgressBar->setHidden(false); - search->setHidden(true); } void VersionSelectWidget::onTaskSucceeded() @@ -134,13 +141,6 @@ void VersionSelectWidget::onTaskSucceeded() listView->setEmptyMode(VersionListView::String); } sneakyProgressBar->setHidden(true); - search->setHidden(false); - - if (focusSearch) - { - search->setFocus(); - focusSearch = false; - } preselect(); loadTask = nullptr; @@ -174,17 +174,6 @@ void VersionSelectWidget::preselect() selectRecommended(); } -void VersionSelectWidget::updateSearch(const QString &value) { - m_proxyModel->setSearch(value); - // if nothing is selected, pick the first result - if (!value.isEmpty()) { - listView->selectionModel()->setCurrentIndex( - listView->model()->index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - listView->scrollToTop(); - } else - listView->scrollTo(listView->selectionModel()->currentIndex(), QAbstractItemView::PositionAtCenter); -} - void VersionSelectWidget::selectCurrent() { if(m_currentVersion.isEmpty()) diff --git a/launcher/ui/widgets/VersionSelectWidget.h b/launcher/ui/widgets/VersionSelectWidget.h index df9c8a62..8d2c900c 100644 --- a/launcher/ui/widgets/VersionSelectWidget.h +++ b/launcher/ui/widgets/VersionSelectWidget.h @@ -89,7 +89,6 @@ private slots: private: void preselect(); - void updateSearch(const QString &value); private: QString m_currentVersion; -- cgit From a2d0d5a71d37f39535154df1e6f740625817a9eb Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 6 Jun 2023 18:25:09 +0100 Subject: Allow arrow key movement, fix auto-focus Signed-off-by: TheKodeToad --- launcher/ui/widgets/VersionSelectWidget.cpp | 29 +++++++++++++++++++++++++---- launcher/ui/widgets/VersionSelectWidget.h | 1 + 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/ui/widgets/VersionSelectWidget.cpp b/launcher/ui/widgets/VersionSelectWidget.cpp index 6aaffaac..a956ddb3 100644 --- a/launcher/ui/widgets/VersionSelectWidget.cpp +++ b/launcher/ui/widgets/VersionSelectWidget.cpp @@ -1,8 +1,11 @@ #include "VersionSelectWidget.h" +#include +#include +#include +#include #include #include -#include #include "VersionProxyModel.h" @@ -45,6 +48,7 @@ VersionSelectWidget::VersionSelectWidget(bool focusSearch, QWidget* parent) } else listView->scrollTo(listView->selectionModel()->currentIndex(), QAbstractItemView::PositionAtCenter); }); + search->installEventFilter(this); sneakyProgressBar = new QProgressBar(this); sneakyProgressBar->setObjectName(QStringLiteral("sneakyProgressBar")); @@ -88,6 +92,23 @@ void VersionSelectWidget::setResizeOn(int column) listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); } +bool VersionSelectWidget::eventFilter(QObject *watched, QEvent *event) { + if (watched == search && event->type() == QEvent::KeyPress) { + const QKeyEvent* keyEvent = (QKeyEvent*)event; + const bool up = keyEvent->key() == Qt::Key_Up; + const bool down = keyEvent->key() == Qt::Key_Down; + if (up || down) { + const QModelIndex index = listView->model()->index(listView->currentIndex().row() + (up ? -1 : 1), 0); + if (index.row() >= 0 && index.row() < listView->model()->rowCount()) { + listView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + return true; + } + } + } + + return QObject::eventFilter(watched, event); +} + void VersionSelectWidget::initialize(BaseVersionList *vlist) { m_vlist = vlist; @@ -95,6 +116,9 @@ void VersionSelectWidget::initialize(BaseVersionList *vlist) listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); + if (focusSearch) + search->setFocus(); + if (!m_vlist->isLoaded()) { loadList(); @@ -105,8 +129,6 @@ void VersionSelectWidget::initialize(BaseVersionList *vlist) { listView->setEmptyMode(VersionListView::String); } - search->setFocus(); - focusSearch = false; preselect(); } } @@ -141,7 +163,6 @@ void VersionSelectWidget::onTaskSucceeded() listView->setEmptyMode(VersionListView::String); } sneakyProgressBar->setHidden(true); - preselect(); loadTask = nullptr; } diff --git a/launcher/ui/widgets/VersionSelectWidget.h b/launcher/ui/widgets/VersionSelectWidget.h index 8d2c900c..be4ba768 100644 --- a/launcher/ui/widgets/VersionSelectWidget.h +++ b/launcher/ui/widgets/VersionSelectWidget.h @@ -74,6 +74,7 @@ public: void setEmptyErrorString(QString emptyErrorString); void setEmptyMode(VersionListView::EmptyMode mode); void setResizeOn(int column); + bool eventFilter(QObject* watched, QEvent* event) override; signals: void selectedVersionChanged(BaseVersion::Ptr version); -- cgit From 1e702ee40f211286f85fa5353704e358e7fe14a9 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 7 Jun 2023 00:16:23 +0300 Subject: Added dynamic page extra info Signed-off-by: Trial97 --- launcher/ui/pages/BasePage.h | 24 +++++++++++----------- .../ui/pages/instance/ExternalResourcesPage.cpp | 19 ++++++++++++++++- launcher/ui/pages/instance/ExternalResourcesPage.h | 1 + launcher/ui/widgets/PageContainer.cpp | 4 ++++ 4 files changed, 35 insertions(+), 13 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/ui/pages/BasePage.h b/launcher/ui/pages/BasePage.h index ceb24040..5537c28f 100644 --- a/launcher/ui/pages/BasePage.h +++ b/launcher/ui/pages/BasePage.h @@ -35,15 +35,16 @@ #pragma once -#include #include +#include +#include #include #include "BasePageContainer.h" -class BasePage -{ -public: +class BasePage { + public: + using updateExtraInfoFunc = std::function; virtual ~BasePage() {} virtual QString id() const = 0; virtual QString displayName() const = 0; @@ -63,17 +64,16 @@ public: } virtual void openedImpl() {} virtual void closedImpl() {} - virtual void setParentContainer(BasePageContainer * container) - { - m_container = container; - }; - virtual void retranslate() { } + virtual void setParentContainer(BasePageContainer* container) { m_container = container; }; + virtual void retranslate() {} -public: + public: int stackIndex = -1; int listIndex = -1; -protected: - BasePageContainer * m_container = nullptr; + updateExtraInfoFunc updateExtraInfo; + + protected: + BasePageContainer* m_container = nullptr; bool isOpened = false; }; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 1115ddc3..e5567c80 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -9,6 +9,7 @@ #include #include +#include ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared_ptr model, QWidget* parent) : QMainWindow(parent), m_instance(instance), ui(new Ui::ExternalResourcesPage), m_model(model) @@ -43,6 +44,13 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared auto selection_model = ui->treeView->selectionModel(); connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current); + auto updateExtra = [this]() { + if (updateExtraInfo) + updateExtraInfo(extraHeaderInfoString()); + }; + connect(selection_model, &QItemSelectionModel::selectionChanged, this, updateExtra); + connect(model.get(), &ResourceFolderModel::updateFinished, this, updateExtra); + connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged); } @@ -248,6 +256,15 @@ bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, const int row = sourceCurrent.row(); Resource const& resource = m_model->at(row); ui->frame->updateWithResource(resource); - return true; } + +QString ExternalResourcesPage::extraHeaderInfoString() +{ + if (ui && ui->treeView && ui->treeView->selectionModel()) { + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + if (auto count = std::count_if(selection.cbegin(), selection.cend(), [](auto v) { return v.column() == 0; }); count != 0) + return tr("[%1 installed, %2 selected]").arg(m_model->size()).arg(count); + } + return tr("[%1 installed]").arg(m_model->size()); +} diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h index d17fbb7f..fd200193 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.h +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -29,6 +29,7 @@ class ExternalResourcesPage : public QMainWindow, public BasePage { virtual QString helpPage() const override = 0; virtual bool shouldDisplay() const override = 0; + QString extraHeaderInfoString(); void openedImpl() override; void closedImpl() override; diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp index 38a22897..34df42ec 100644 --- a/launcher/ui/widgets/PageContainer.cpp +++ b/launcher/ui/widgets/PageContainer.cpp @@ -93,6 +93,10 @@ PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId, page->listIndex = counter; page->setParentContainer(this); counter++; + page->updateExtraInfo = [this](QString info) { + if (m_currentPage) + m_header->setText(m_currentPage->displayName() + info); + }; } m_model->setPages(pages); -- cgit From 0b4807dc1fd723ac2f511a935649ae3a858aa388 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 7 Jun 2023 22:55:37 +0100 Subject: Questionable fix two Signed-off-by: TheKodeToad --- launcher/ui/widgets/VersionListView.cpp | 5 ----- 1 file changed, 5 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/ui/widgets/VersionListView.cpp b/launcher/ui/widgets/VersionListView.cpp index 0e126c65..69f88155 100644 --- a/launcher/ui/widgets/VersionListView.cpp +++ b/launcher/ui/widgets/VersionListView.cpp @@ -125,14 +125,9 @@ void VersionListView::paintEvent(QPaintEvent *event) QString VersionListView::currentEmptyString() const { - if(m_itemCount) { - return QString(); - } switch(m_emptyMode) { default: - case VersionListView::Empty: - return QString(); case VersionListView::String: return m_emptyString; case VersionListView::ErrorString: -- cgit From f96b135ef72a762220941681cf1b314a3b2db266 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 8 Jun 2023 20:26:09 +0300 Subject: Higlight installed mods Signed-off-by: Trial97 --- launcher/minecraft/MinecraftInstance.cpp | 40 +++++++++------------- launcher/minecraft/MinecraftInstance.h | 16 ++++----- launcher/minecraft/WorldList.cpp | 3 +- launcher/minecraft/WorldList.h | 4 +-- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/ModFolderModel.h | 2 +- launcher/minecraft/mod/ResourceFolderModel.cpp | 2 +- launcher/minecraft/mod/ResourceFolderModel.h | 4 +-- launcher/minecraft/mod/ResourcePackFolderModel.cpp | 2 +- launcher/minecraft/mod/ResourcePackFolderModel.h | 2 +- launcher/minecraft/mod/ShaderPackFolderModel.h | 4 +-- launcher/minecraft/mod/TexturePackFolderModel.cpp | 3 +- launcher/minecraft/mod/TexturePackFolderModel.h | 4 +-- launcher/ui/pages/modplatform/ModModel.cpp | 12 +++++++ launcher/ui/pages/modplatform/ModModel.h | 1 + launcher/ui/pages/modplatform/ResourceModel.cpp | 3 ++ launcher/ui/pages/modplatform/ResourceModel.h | 2 ++ launcher/ui/pages/modplatform/flame/FlameModel.cpp | 2 ++ .../pages/modplatform/modrinth/ModrinthModel.cpp | 2 ++ launcher/ui/widgets/ProjectItem.cpp | 5 +++ launcher/ui/widgets/ProjectItem.h | 3 +- 21 files changed, 67 insertions(+), 51 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 2c624a36..76ff5723 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1109,10 +1109,9 @@ JavaVersion MinecraftInstance::getJavaVersion() return JavaVersion(settings()->get("JavaVersion").toString()); } -std::shared_ptr MinecraftInstance::loaderModList() +std::shared_ptr MinecraftInstance::loaderModList() const { - if (!m_loader_mod_list) - { + if (!m_loader_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed)); m_loader_mod_list->disableInteraction(isRunning()); @@ -1121,10 +1120,9 @@ std::shared_ptr MinecraftInstance::loaderModList() return m_loader_mod_list; } -std::shared_ptr MinecraftInstance::coreModList() +std::shared_ptr MinecraftInstance::coreModList() const { - if (!m_core_mod_list) - { + if (!m_core_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed)); m_core_mod_list->disableInteraction(isRunning()); @@ -1133,10 +1131,9 @@ std::shared_ptr MinecraftInstance::coreModList() return m_core_mod_list; } -std::shared_ptr MinecraftInstance::nilModList() +std::shared_ptr MinecraftInstance::nilModList() const { - if (!m_nil_mod_list) - { + if (!m_nil_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), this, is_indexed, false)); m_nil_mod_list->disableInteraction(isRunning()); @@ -1145,46 +1142,41 @@ std::shared_ptr MinecraftInstance::nilModList() return m_nil_mod_list; } -std::shared_ptr MinecraftInstance::resourcePackList() +std::shared_ptr MinecraftInstance::resourcePackList() const { - if (!m_resource_pack_list) - { + if (!m_resource_pack_list) { m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this)); } return m_resource_pack_list; } -std::shared_ptr MinecraftInstance::texturePackList() +std::shared_ptr MinecraftInstance::texturePackList() const { - if (!m_texture_pack_list) - { + if (!m_texture_pack_list) { m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this)); } return m_texture_pack_list; } -std::shared_ptr MinecraftInstance::shaderPackList() +std::shared_ptr MinecraftInstance::shaderPackList() const { - if (!m_shader_pack_list) - { + if (!m_shader_pack_list) { m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this)); } return m_shader_pack_list; } -std::shared_ptr MinecraftInstance::worldList() +std::shared_ptr MinecraftInstance::worldList() const { - if (!m_world_list) - { + if (!m_world_list) { m_world_list.reset(new WorldList(worldDir(), this)); } return m_world_list; } -std::shared_ptr MinecraftInstance::gameOptionsModel() +std::shared_ptr MinecraftInstance::gameOptionsModel() const { - if (!m_game_options) - { + if (!m_game_options) { m_game_options.reset(new GameOptions(FS::PathCombine(gameRoot(), "options.txt"))); } return m_game_options; diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 068b3008..a75fa481 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -115,14 +115,14 @@ public: std::shared_ptr getPackProfile() const; ////// Mod Lists ////// - std::shared_ptr loaderModList(); - std::shared_ptr coreModList(); - std::shared_ptr nilModList(); - std::shared_ptr resourcePackList(); - std::shared_ptr texturePackList(); - std::shared_ptr shaderPackList(); - std::shared_ptr worldList(); - std::shared_ptr gameOptionsModel(); + std::shared_ptr loaderModList() const; + std::shared_ptr coreModList() const; + std::shared_ptr nilModList() const; + std::shared_ptr resourcePackList() const; + std::shared_ptr texturePackList() const; + std::shared_ptr shaderPackList() const; + std::shared_ptr worldList() const; + std::shared_ptr gameOptionsModel() const; ////// Launch stuff ////// Task::Ptr createUpdateTask(Net::Mode mode) override; diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp index 0feee299..42772df5 100644 --- a/launcher/minecraft/WorldList.cpp +++ b/launcher/minecraft/WorldList.cpp @@ -45,8 +45,7 @@ #include #include -WorldList::WorldList(const QString &dir, BaseInstance* instance) - : QAbstractListModel(), m_instance(instance), m_dir(dir) +WorldList::WorldList(const QString& dir, const BaseInstance* instance) : QAbstractListModel(), m_instance(instance), m_dir(dir) { FS::ensureFolderPathExists(m_dir.absolutePath()); m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); diff --git a/launcher/minecraft/WorldList.h b/launcher/minecraft/WorldList.h index 96b64193..92d02b9b 100644 --- a/launcher/minecraft/WorldList.h +++ b/launcher/minecraft/WorldList.h @@ -50,7 +50,7 @@ public: IconFileRole }; - WorldList(const QString &dir, BaseInstance* instance); + WorldList(const QString& dir, const BaseInstance* instance); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; @@ -128,7 +128,7 @@ signals: void changed(); protected: - BaseInstance* m_instance; + const BaseInstance* m_instance; QFileSystemWatcher *m_watcher; bool is_watching; QDir m_dir; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 5e3b31e0..eccfe4ae 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -54,7 +54,7 @@ #include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "modplatform/ModIndex.h" -ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) +ModFolderModel::ModFolderModel(const QString& dir, const BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index d337fe29..261a007a 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -75,7 +75,7 @@ public: Enable, Toggle }; - ModFolderModel(const QString &dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); + ModFolderModel(const QString& dir, const BaseInstance* instance, bool is_indexed = false, bool create_dir = true); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index d2d875e4..5664ea85 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -16,7 +16,7 @@ #include "tasks/Task.h" -ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir) +ResourceFolderModel::ResourceFolderModel(QDir dir, const BaseInstance* instance, QObject* parent, bool create_dir) : QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this) { if (create_dir) { diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 0a35e1bc..d853b443 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -26,7 +26,7 @@ class QSortFilterProxyModel; class ResourceFolderModel : public QAbstractListModel { Q_OBJECT public: - ResourceFolderModel(QDir, BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true); + ResourceFolderModel(QDir, const BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true); ~ResourceFolderModel() override; /** Starts watching the paths for changes. @@ -191,7 +191,7 @@ class ResourceFolderModel : public QAbstractListModel { bool m_can_interact = true; QDir m_dir; - BaseInstance* m_instance; + const BaseInstance* m_instance; QFileSystemWatcher m_watcher; bool m_is_watching = false; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index c12d1f23..8036292b 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -45,7 +45,7 @@ #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalResourcePackParseTask.h" -ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance) +ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, const BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) { m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h index db4b14fb..9d96253a 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.h +++ b/launcher/minecraft/mod/ResourcePackFolderModel.h @@ -17,7 +17,7 @@ public: NUM_COLUMNS }; - explicit ResourcePackFolderModel(const QString &dir, BaseInstance* instance); + explicit ResourcePackFolderModel(const QString& dir, const BaseInstance* instance); [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h index dc5acf80..c05bec1d 100644 --- a/launcher/minecraft/mod/ShaderPackFolderModel.h +++ b/launcher/minecraft/mod/ShaderPackFolderModel.h @@ -6,7 +6,5 @@ class ShaderPackFolderModel : public ResourceFolderModel { Q_OBJECT public: - explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance) - : ResourceFolderModel(QDir(dir), instance) - {} + explicit ShaderPackFolderModel(const QString& dir, const BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) {} }; diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index c6609ed1..dadf8f77 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -39,8 +39,7 @@ #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalTexturePackParseTask.h" -TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) - : ResourceFolderModel(QDir(dir), instance) +TexturePackFolderModel::TexturePackFolderModel(const QString& dir, const BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) {} Task* TexturePackFolderModel::createUpdateTask() diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h index 425a71e4..a1492f8b 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.h +++ b/launcher/minecraft/mod/TexturePackFolderModel.h @@ -42,8 +42,8 @@ class TexturePackFolderModel : public ResourceFolderModel { Q_OBJECT -public: - explicit TexturePackFolderModel(const QString &dir, BaseInstance* instance); + public: + explicit TexturePackFolderModel(const QString& dir, const BaseInstance* instance); [[nodiscard]] Task* createUpdateTask() override; [[nodiscard]] Task* createParseTask(Resource&) override; }; diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index afd8b292..1579bbf4 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -6,8 +6,10 @@ #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +#include "minecraft/mod/ModFolderModel.h" #include +#include namespace ResourceDownload { @@ -67,4 +69,14 @@ void ModModel::searchWithTerm(const QString& term, unsigned int sort, bool filte refresh(); } +bool ModModel::isPackInstalled(ModPlatform::IndexedPack::Ptr pack) const +{ + auto allMods = static_cast(m_base_instance).loaderModList()->allMods(); + return std::any_of(allMods.cbegin(), allMods.cend(), [pack](Mod* mod) { + if (auto meta = mod->metadata(); meta) + return meta->provider == pack->provider && meta->project_id == pack->addonId; + return false; + }); +} + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index 5d4a7785..938ce32e 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -42,6 +42,7 @@ class ModModel : public ResourceModel { protected: auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0; + virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const override; protected: const BaseInstance& m_base_instance; diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index a5ea1ca9..49405a02 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -77,6 +77,8 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant return pack->description; case UserDataTypes::SELECTED: return pack->isAnyVersionSelected(); + case UserDataTypes::INSTALLED: + return this->isPackInstalled(pack); default: break; } @@ -95,6 +97,7 @@ QHash ResourceModel::roleNames() const roles[UserDataTypes::TITLE] = "title"; roles[UserDataTypes::DESCRIPTION] = "description"; roles[UserDataTypes::SELECTED] = "selected"; + roles[UserDataTypes::INSTALLED] = "installed"; return roles; } diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index 69e23473..6533d9c6 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -116,6 +116,8 @@ class ResourceModel : public QAbstractListModel { virtual void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&); virtual void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&); + virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const { return false; } + protected: /* Basic search parameters */ enum class SearchState { None, CanFetchMore, ResetRequested, Finished } m_search_state = SearchState::None; diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index 5961ea02..d9d5ef5b 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -60,6 +60,8 @@ QVariant ListModel::data(const QModelIndex& index, int role) const return pack.description; case UserDataTypes::SELECTED: return false; + case UserDataTypes::INSTALLED: + return false; default: break; } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 346a00b0..55d287b0 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -106,6 +106,8 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian return pack.description; case UserDataTypes::SELECTED: return false; + case UserDataTypes::INSTALLED: + return false; default: break; } diff --git a/launcher/ui/widgets/ProjectItem.cpp b/launcher/ui/widgets/ProjectItem.cpp index d1ff9dbc..e8349c10 100644 --- a/launcher/ui/widgets/ProjectItem.cpp +++ b/launcher/ui/widgets/ProjectItem.cpp @@ -64,6 +64,11 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o font.setBold(true); font.setUnderline(true); } + if (index.data(UserDataTypes::INSTALLED).toBool()) { + // Set nice font + font.setItalic(true); + font.setOverline(true); + } font.setPointSize(font.pointSize() + 2); painter->setFont(font); diff --git a/launcher/ui/widgets/ProjectItem.h b/launcher/ui/widgets/ProjectItem.h index f668edf6..196055ea 100644 --- a/launcher/ui/widgets/ProjectItem.h +++ b/launcher/ui/widgets/ProjectItem.h @@ -6,7 +6,8 @@ enum UserDataTypes { TITLE = 257, // QString DESCRIPTION = 258, // QString - SELECTED = 259 // bool + SELECTED = 259, // bool + INSTALLED = 260 // bool }; /** This is an item delegate composed of: -- cgit From b7d82354cbf4d13c428555c5625db183dffd15d7 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 10 Jun 2023 14:42:27 +0100 Subject: [ci skip] License headers!! (yay) Signed-off-by: TheKodeToad --- launcher/java/JavaInstallList.cpp | 3 ++- launcher/ui/widgets/VersionListView.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp index 04eb2e0f..3407fdf7 100644 --- a/launcher/java/JavaInstallList.cpp +++ b/launcher/java/JavaInstallList.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/widgets/VersionListView.cpp b/launcher/ui/widgets/VersionListView.cpp index 69f88155..06e58d22 100644 --- a/launcher/ui/widgets/VersionListView.cpp +++ b/launcher/ui/widgets/VersionListView.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit From cb52be433d2a55338df2205d2cebbf3e4d2f5eb5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 15 Jun 2023 13:20:08 +0300 Subject: Made the installed mods more apparent Signed-off-by: Trial97 --- launcher/ui/widgets/ProjectItem.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'launcher/ui/widgets') diff --git a/launcher/ui/widgets/ProjectItem.cpp b/launcher/ui/widgets/ProjectItem.cpp index e8349c10..0b00a9bd 100644 --- a/launcher/ui/widgets/ProjectItem.cpp +++ b/launcher/ui/widgets/ProjectItem.cpp @@ -65,9 +65,15 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o font.setUnderline(true); } if (index.data(UserDataTypes::INSTALLED).toBool()) { + auto rect = opt.rect; + rect.setX(rect.x() + 1); + rect.setY(rect.y() + 1); + rect.setHeight(rect.height() - 2); + rect.setWidth(rect.width() - 2); // Set nice font font.setItalic(true); font.setOverline(true); + painter->drawRect(rect); } font.setPointSize(font.pointSize() + 2); -- cgit From 98a07da39bbc41529b67cf515849d403280af07c Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 15 Jun 2023 14:12:29 +0300 Subject: Renamed variable Signed-off-by: Trial97 --- launcher/ui/widgets/ProjectItem.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/ui/widgets/ProjectItem.cpp b/launcher/ui/widgets/ProjectItem.cpp index 0b00a9bd..0085d6b2 100644 --- a/launcher/ui/widgets/ProjectItem.cpp +++ b/launcher/ui/widgets/ProjectItem.cpp @@ -65,15 +65,15 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o font.setUnderline(true); } if (index.data(UserDataTypes::INSTALLED).toBool()) { - auto rect = opt.rect; - rect.setX(rect.x() + 1); - rect.setY(rect.y() + 1); - rect.setHeight(rect.height() - 2); - rect.setWidth(rect.width() - 2); + auto hRect = opt.rect; + hRect.setX(hRect.x() + 1); + hRect.setY(hRect.y() + 1); + hRect.setHeight(hRect.height() - 2); + hRect.setWidth(hRect.width() - 2); // Set nice font font.setItalic(true); font.setOverline(true); - painter->drawRect(rect); + painter->drawRect(hRect); } font.setPointSize(font.pointSize() + 2); -- cgit From 5eb71fc6a96690e3d9a658f383b0937446ec3f9e Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 24 Jun 2023 23:34:37 +0100 Subject: Revert "feat(Mods): hide 'Provider' column when no mods have providers" With Ryex's change, this causes issues. Apparently you need to sign off reverts! That's just weird... Signed-off-by: TheKodeToad --- launcher/ui/widgets/ModListView.cpp | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/ui/widgets/ModListView.cpp b/launcher/ui/widgets/ModListView.cpp index 893cd120..80a918b6 100644 --- a/launcher/ui/widgets/ModListView.cpp +++ b/launcher/ui/widgets/ModListView.cpp @@ -14,9 +14,6 @@ */ #include "ModListView.h" - -#include "minecraft/mod/ModFolderModel.h" - #include #include #include @@ -65,19 +62,6 @@ void ModListView::setModel ( QAbstractItemModel* model ) for(int i = 1; i < head->count(); i++) head->setSectionResizeMode(i, QHeaderView::ResizeToContents); } - - auto real_model = model; - if (auto proxy_model = dynamic_cast(model); proxy_model) - real_model = proxy_model->sourceModel(); - - if (auto mod_model = dynamic_cast(real_model); mod_model) { - connect(mod_model, &ModFolderModel::updateFinished, this, [this, mod_model]{ - auto mods = mod_model->allMods(); - // Hide the 'Provider' column if no mod has a defined provider! - setColumnHidden(ModFolderModel::Columns::ProviderColumn, - std::none_of(mods.constBegin(), mods.constEnd(), [](auto const mod){ return mod->provider().has_value(); })); - }); - } } void ModListView::setResizeModes(const QList &modes) -- cgit From 6d0e255ca18b1934d6eb514b6324cde67bb87d60 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 25 Jun 2023 22:00:33 +0300 Subject: fix: Page container extra info set on logs page Signed-off-by: Trial97 --- launcher/ui/pages/BasePage.h | 2 +- launcher/ui/pages/instance/ExternalResourcesPage.cpp | 2 +- launcher/ui/widgets/PageContainer.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'launcher/ui/widgets') diff --git a/launcher/ui/pages/BasePage.h b/launcher/ui/pages/BasePage.h index 5537c28f..dc2bde99 100644 --- a/launcher/ui/pages/BasePage.h +++ b/launcher/ui/pages/BasePage.h @@ -44,7 +44,7 @@ class BasePage { public: - using updateExtraInfoFunc = std::function; + using updateExtraInfoFunc = std::function; virtual ~BasePage() {} virtual QString id() const = 0; virtual QString displayName() const = 0; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 8e5226ef..173bcb66 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -83,7 +83,7 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current); auto updateExtra = [this]() { if (updateExtraInfo) - updateExtraInfo(extraHeaderInfoString()); + updateExtraInfo(id(), extraHeaderInfoString()); }; connect(selection_model, &QItemSelectionModel::selectionChanged, this, updateExtra); connect(model.get(), &ResourceFolderModel::updateFinished, this, updateExtra); diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp index 34df42ec..b98c9796 100644 --- a/launcher/ui/widgets/PageContainer.cpp +++ b/launcher/ui/widgets/PageContainer.cpp @@ -93,8 +93,8 @@ PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId, page->listIndex = counter; page->setParentContainer(this); counter++; - page->updateExtraInfo = [this](QString info) { - if (m_currentPage) + page->updateExtraInfo = [this](QString id, QString info) { + if (m_currentPage && id == m_currentPage->id()) m_header->setText(m_currentPage->displayName() + info); }; } -- cgit