diff options
Diffstat (limited to 'launcher/ui/pages/instance')
17 files changed, 408 insertions, 418 deletions
diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 1115ddc3..12038f88 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> + * + * 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 <https://www.gnu.org/licenses/>. + * + * 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 "ExternalResourcesPage.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui_ExternalResourcesPage.h" @@ -9,6 +44,7 @@ #include <QKeyEvent> #include <QMenu> +#include <algorithm> ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared_ptr<ResourceFolderModel> model, QWidget* parent) : QMainWindow(parent), m_instance(instance), ui(new Ui::ExternalResourcesPage), m_model(model) @@ -24,6 +60,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); @@ -43,7 +81,21 @@ 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(id(), extraHeaderInfoString()); + }; + connect(selection_model, &QItemSelectionModel::selectionChanged, this, updateExtra); + connect(model.get(), &ResourceFolderModel::updateFinished, this, updateExtra); + 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 +117,13 @@ void ExternalResourcesPage::ShowContextMenu(const QPoint& pos) delete menu; } +void ExternalResourcesPage::ShowHeaderContextMenu(const QPoint& pos) +{ + auto menu = m_model->createHeaderContextMenu(ui->treeView); + menu->exec(ui->treeView->mapToGlobal(pos)); + menu->deleteLater(); +} + void ExternalResourcesPage::openedImpl() { m_model->startWatching(); @@ -92,11 +151,7 @@ void ExternalResourcesPage::retranslate() void ExternalResourcesPage::itemActivated(const QModelIndex&) { - if (!m_controlsEnabled) - return; - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); - m_model->setResourceEnabled(selection.indexes(), EnableAction::TOGGLE); } void ExternalResourcesPage::filterTextChanged(const QString& newContents) @@ -139,9 +194,6 @@ bool ExternalResourcesPage::eventFilter(QObject* obj, QEvent* ev) void ExternalResourcesPage::addItem() { - if (!m_controlsEnabled) - return; - auto list = GuiUtil::BrowseForFiles( helpPage(), tr("Select %1", "Select whatever type of files the page contains. Example: 'Loader Mods'").arg(displayName()), m_fileSelectionFilter.arg(displayName()), APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget()); @@ -155,9 +207,6 @@ void ExternalResourcesPage::addItem() void ExternalResourcesPage::removeItem() { - if (!m_controlsEnabled) - return; - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); int count = 0; @@ -201,23 +250,37 @@ void ExternalResourcesPage::removeItem() void ExternalResourcesPage::removeItems(const QItemSelection& selection) { + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = CustomMessageBox::selectable(this, "Confirm Delete", + "If you remove this resource while the game is running it may crash your game.\n" + "Are you sure you want to do this?", + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } m_model->deleteResources(selection.indexes()); } void ExternalResourcesPage::enableItem() { - if (!m_controlsEnabled) - return; - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); m_model->setResourceEnabled(selection.indexes(), EnableAction::ENABLE); } void ExternalResourcesPage::disableItem() { - if (!m_controlsEnabled) - return; + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = CustomMessageBox::selectable(this, "Confirm disable", + "If you disable this resource while the game is running it may crash your game.\n" + "Are you sure you want to do this?", + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + if (response != QMessageBox::Yes) + return; + } auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); m_model->setResourceEnabled(selection.indexes(), EnableAction::DISABLE); } @@ -248,6 +311,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..97d922d8 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; @@ -60,6 +61,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; @@ -71,7 +73,5 @@ class ExternalResourcesPage : public QMainWindow, public BasePage { QString m_fileSelectionFilter; QString m_viewFilter; - bool m_controlsEnabled = true; - std::shared_ptr<Setting> m_wide_bar_setting = nullptr; }; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 33a03336..3c836691 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -62,6 +62,9 @@ <property name="dragDropMode"> <enum>QAbstractItemView::DropOnly</enum> </property> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> </widget> </item> </layout> @@ -154,6 +157,17 @@ <string>Try to check or update all selected resources (all resources if none are selected)</string> </property> </action> + <action name="actionVisitItemPage"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Visit mod's page</string> + </property> + <property name="toolTip"> + <string>Go to mods home page</string> + </property> + </action> </widget> <customwidgets> <customwidget> diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 4b4c73dc..943ff17f 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -60,21 +60,14 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent) m_settings = inst->settings(); ui->setupUi(this); - accountMenu = new QMenu(this); - // Use undocumented property... https://stackoverflow.com/questions/7121718/create-a-scrollbar-in-a-submenu-qt - accountMenu->setStyleSheet("QMenu { menu-scrollable: 1; }"); - ui->instanceAccountSelector->setMenu(accountMenu); - connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked); connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings); connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings); + connect(ui->instanceAccountSelector, QOverload<int>::of(&QComboBox::currentIndexChanged), this, + &InstanceSettingsPage::changeInstanceAccount); loadSettings(); - updateThresholds(); -} -bool InstanceSettingsPage::shouldDisplay() const -{ - return !m_instance->isRunning(); + updateThresholds(); } InstanceSettingsPage::~InstanceSettingsPage() @@ -88,12 +81,12 @@ void InstanceSettingsPage::globalSettingsButtonClicked(bool) case 0: APPLICATION->ShowGlobalSettings(this, "java-settings"); return; - case 1: - APPLICATION->ShowGlobalSettings(this, "minecraft-settings"); - return; case 2: APPLICATION->ShowGlobalSettings(this, "custom-commands"); return; + default: + APPLICATION->ShowGlobalSettings(this, "minecraft-settings"); + return; } } @@ -454,36 +447,17 @@ void InstanceSettingsPage::on_javaTestBtn_clicked() void InstanceSettingsPage::updateAccountsMenu() { - accountMenu->clear(); - + ui->instanceAccountSelector->clear(); auto accounts = APPLICATION->accounts(); int accountIndex = accounts->findAccountByProfileId(m_settings->get("InstanceAccountId").toString()); - MinecraftAccountPtr defaultAccount = accounts->defaultAccount(); - - if (accountIndex != -1 && accounts->at(accountIndex)) { - defaultAccount = accounts->at(accountIndex); - } - - if (defaultAccount) { - ui->instanceAccountSelector->setText(defaultAccount->profileName()); - ui->instanceAccountSelector->setIcon(getFaceForAccount(defaultAccount)); - } else { - ui->instanceAccountSelector->setText(tr("No default account")); - ui->instanceAccountSelector->setIcon(APPLICATION->getThemedIcon("noaccount")); - } for (int i = 0; i < accounts->count(); i++) { MinecraftAccountPtr account = accounts->at(i); - QAction* action = new QAction(account->profileName(), this); - action->setData(i); - action->setCheckable(true); - if (accountIndex == i) { - action->setChecked(true); - } - action->setIcon(getFaceForAccount(account)); - accountMenu->addAction(action); - connect(action, SIGNAL(triggered(bool)), this, SLOT(changeInstanceAccount())); + ui->instanceAccountSelector->addItem(getFaceForAccount(account), account->profileName(), i); + if (i == accountIndex) + ui->instanceAccountSelector->setCurrentIndex(i); } + } QIcon InstanceSettingsPage::getFaceForAccount(MinecraftAccountPtr account) @@ -495,20 +469,13 @@ QIcon InstanceSettingsPage::getFaceForAccount(MinecraftAccountPtr account) return APPLICATION->getThemedIcon("noaccount"); } -void InstanceSettingsPage::changeInstanceAccount() +void InstanceSettingsPage::changeInstanceAccount(int index) { - QAction* sAction = (QAction*)sender(); - - Q_ASSERT(sAction->data().type() == QVariant::Type::Int); - - QVariant data = sAction->data(); - int index = data.toInt(); auto accounts = APPLICATION->accounts(); - auto account = accounts->at(index); - m_settings->set("InstanceAccountId", account->profileId()); - - ui->instanceAccountSelector->setText(account->profileName()); - ui->instanceAccountSelector->setIcon(getFaceForAccount(account)); + if (index != -1 && accounts->at(index) && ui->instanceAccountGroupBox->isChecked()) { + auto account = accounts->at(index); + m_settings->set("InstanceAccountId", account->profileId()); + } } void InstanceSettingsPage::on_maxMemSpinBox_valueChanged(int i) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index cb6fbae0..036b4181 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -75,12 +75,11 @@ public: { return "Instance-settings"; } - virtual bool shouldDisplay() const override; void retranslate() override; void updateThresholds(); -private slots: + private slots: void on_javaDetectBtn_clicked(); void on_javaTestBtn_clicked(); void on_javaBrowseBtn_clicked(); @@ -95,12 +94,11 @@ private slots: void updateAccountsMenu(); QIcon getFaceForAccount(MinecraftAccountPtr account); - void changeInstanceAccount(); + void changeInstanceAccount(int index); private: Ui::InstanceSettingsPage *ui; BaseInstance *m_instance; SettingsObjectPtr m_settings; unique_qobject_ptr<JavaCommon::TestCheck> checker; - QMenu *accountMenu = nullptr; }; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 1b986184..8427965d 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -269,7 +269,7 @@ <item> <widget class="QCheckBox" name="maximizedCheckBox"> <property name="text"> - <string>Start Minecraft maximized?</string> + <string>Start Minecraft maximized</string> </property> </widget> </item> @@ -341,21 +341,21 @@ <item> <widget class="QCheckBox" name="showConsoleCheck"> <property name="text"> - <string>Show console while the game is running?</string> + <string>Show console while the game is running</string> </property> </widget> </item> <item> <widget class="QCheckBox" name="autoCloseConsoleCheck"> <property name="text"> - <string>Automatically close console when the game quits?</string> + <string>Automatically close console when the game quits</string> </property> </widget> </item> <item> <widget class="QCheckBox" name="showConsoleErrorCheck"> <property name="text"> - <string>Show console when the game crashes?</string> + <string>Show console when the game crashes</string> </property> </widget> </item> @@ -636,14 +636,7 @@ </widget> </item> <item row="0" column="1"> - <widget class="QToolButton" name="instanceAccountSelector"> - <property name="popupMode"> - <enum>QToolButton::InstantPopup</enum> - </property> - <property name="toolButtonStyle"> - <enum>Qt::ToolButtonTextBesideIcon</enum> - </property> - </widget> + <widget class="QComboBox" name="instanceAccountSelector"/> </item> </layout> </item> diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index dc983d9a..0fc0c986 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -30,8 +30,6 @@ class NoBigComboBoxStyle : public QProxyStyle { Q_OBJECT public: - NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {} - // clang-format off int styleHint(QStyle::StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override { @@ -41,6 +39,36 @@ class NoBigComboBoxStyle : public QProxyStyle { return QProxyStyle::styleHint(hint, option, widget, returnData); } // clang-format on + + /** + * Something about QProxyStyle and QStyle objects means they can't be free'd just + * because all the widgets using them are gone. + * They seems to be tied to the QApplicaiton lifecycle. + * So make singletons tied to the lifetime of the application to clean them up and ensure they aren't + * being remade over and over again, thus leaking memory. + */ + public: + static NoBigComboBoxStyle* getInstance(QStyle* style) + { + static QHash<QStyle*, NoBigComboBoxStyle*> s_singleton_instances_ = {}; + static std::mutex s_singleton_instances_mutex_; + + std::lock_guard<std::mutex> lock(s_singleton_instances_mutex_); + auto inst_iter = s_singleton_instances_.constFind(style); + NoBigComboBoxStyle* inst = nullptr; + if (inst_iter == s_singleton_instances_.constEnd() || *inst_iter == nullptr) { + inst = new NoBigComboBoxStyle(style); + inst->setParent(APPLICATION); + s_singleton_instances_.insert(style, inst); + qDebug() << "QProxyStyle NoBigComboBox created for" << style->objectName() << style; + } else { + inst = *inst_iter; + } + return inst; + } + + private: + NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {} }; ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent) @@ -62,11 +90,13 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi // NOTE: GTK2 themes crash with the proxy style. // This seems like an upstream bug, so there's not much else that can be done. - if (!QStyleFactory::keys().contains("gtk2")) - ui->versionsComboBox->setStyle(new NoBigComboBoxStyle(ui->versionsComboBox->style())); + if (!QStyleFactory::keys().contains("gtk2")) { + auto comboStyle = NoBigComboBoxStyle::getInstance(ui->versionsComboBox->style()); + ui->versionsComboBox->setStyle(comboStyle); + } ui->reloadButton->setVisible(false); - connect(ui->reloadButton, &QPushButton::clicked, this, [this](bool){ + connect(ui->reloadButton, &QPushButton::clicked, this, [this](bool) { ui->reloadButton->setVisible(false); m_loaded = false; @@ -174,7 +204,7 @@ ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWin { Q_ASSERT(inst->isManagedPack()); connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion())); - connect(ui->updateButton, &QPushButton::pressed, this, &ModrinthManagedPackPage::update); + connect(ui->updateButton, &QPushButton::clicked, this, &ModrinthManagedPackPage::update); } // MODRINTH @@ -195,7 +225,8 @@ void ModrinthManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); - m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response.get())); + m_fetch_job->addNetAction( + Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; @@ -236,7 +267,6 @@ void ModrinthManagedPackPage::parseManagedPack() if (version.version == m_inst->getManagedPackVersionName()) name = tr("%1 (Current)").arg(name); - ui->versionsComboBox->addItem(name, QVariant(version.id)); } @@ -260,6 +290,10 @@ QString ModrinthManagedPackPage::url() const void ModrinthManagedPackPage::suggestVersion() { auto index = ui->versionsComboBox->currentIndex(); + if (m_pack.versions.length() == 0) { + setFailState(); + return; + } auto version = m_pack.versions.at(index); ui->changelogTextBrowser->setHtml(markdownToHTML(version.changelog.toUtf8())); @@ -270,6 +304,10 @@ void ModrinthManagedPackPage::suggestVersion() void ModrinthManagedPackPage::update() { auto index = ui->versionsComboBox->currentIndex(); + if (m_pack.versions.length() == 0) { + setFailState(); + return; + } auto version = m_pack.versions.at(index); QMap<QString, QString> extra_info; @@ -301,7 +339,7 @@ FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* i { Q_ASSERT(inst->isManagedPack()); connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion())); - connect(ui->updateButton, &QPushButton::pressed, this, &FlameManagedPackPage::update); + connect(ui->updateButton, &QPushButton::clicked, this, &FlameManagedPackPage::update); } void FlameManagedPackPage::parseManagedPack() @@ -338,7 +376,7 @@ void FlameManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); - m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response.get())); + m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response)); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; @@ -398,6 +436,10 @@ QString FlameManagedPackPage::url() const void FlameManagedPackPage::suggestVersion() { auto index = ui->versionsComboBox->currentIndex(); + if (m_pack.versions.length() == 0) { + setFailState(); + return; + } auto version = m_pack.versions.at(index); ui->changelogTextBrowser->setHtml(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId)); @@ -408,6 +450,10 @@ void FlameManagedPackPage::suggestVersion() void FlameManagedPackPage::update() { auto index = ui->versionsComboBox->currentIndex(); + if (m_pack.versions.length() == 0) { + setFailState(); + return; + } auto version = m_pack.versions.at(index); QMap<QString, QString> extra_info; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 4548af59..cef292bd 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -4,6 +4,7 @@ * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> + * Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> * * 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 @@ -44,6 +45,7 @@ #include <QMenu> #include <QMessageBox> #include <QSortFilterProxyModel> +#include <algorithm> #include "Application.h" @@ -59,6 +61,7 @@ #include "minecraft/mod/Mod.h" #include "minecraft/mod/ModFolderModel.h" +#include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" #include "Version.h" @@ -85,45 +88,40 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> ui->actionsToolbar->insertActionAfter(ui->actionAddItem, ui->actionUpdateItem); connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods); - auto check_allow_update = [this] { - return (!m_instance || !m_instance->isRunning()) && - (ui->treeView->selectionModel()->hasSelection() || !m_model->empty()); - }; + ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); + ui->actionsToolbar->addAction(ui->actionVisitItemPage); + connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages); - connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this, check_allow_update] { - ui->actionUpdateItem->setEnabled(check_allow_update()); - }); + auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; - connect(mods.get(), &ModFolderModel::rowsInserted, this, [this, check_allow_update] { + connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); - }); - connect(mods.get(), &ModFolderModel::rowsRemoved, this, [this, check_allow_update] { - ui->actionUpdateItem->setEnabled(check_allow_update()); + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto mods_list = m_model->selectedMods(selection); + auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(), + [](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; }); + if (selected <= 1) { + ui->actionVisitItemPage->setText(tr("Visit mod's page")); + ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); + } else { + ui->actionVisitItemPage->setText(tr("Visit mods' pages")); + ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods")); + } + ui->actionVisitItemPage->setEnabled(selected != 0); }); - connect(mods.get(), &ModFolderModel::updateFinished, this, [this, check_allow_update, mods] { - ui->actionUpdateItem->setEnabled(check_allow_update()); + connect(mods.get(), &ModFolderModel::rowsInserted, this, + [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }); - // Prevent a weird crash when trying to open the mods page twice in a session o.O - disconnect(mods.get(), &ModFolderModel::updateFinished, this, 0); - }); + connect(mods.get(), &ModFolderModel::rowsRemoved, this, + [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }); - connect(m_instance, &BaseInstance::runningStatusChanged, this, &ModFolderPage::runningStateChanged); - ModFolderPage::runningStateChanged(m_instance && m_instance->isRunning()); + connect(mods.get(), &ModFolderModel::updateFinished, this, + [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }); } } -void ModFolderPage::runningStateChanged(bool running) -{ - ui->actionDownloadItem->setEnabled(!running); - ui->actionUpdateItem->setEnabled(!running); - ui->actionAddItem->setEnabled(!running); - ui->actionEnableItem->setEnabled(!running); - ui->actionDisableItem->setEnabled(!running); - ui->actionRemoveItem->setEnabled(!running); -} - bool ModFolderPage::shouldDisplay() const { return true; @@ -140,15 +138,23 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, const QModelI return true; } -void ModFolderPage::removeItems(const QItemSelection &selection) +void ModFolderPage::removeItems(const QItemSelection& selection) { + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = CustomMessageBox::selectable(this, "Confirm Delete", + "If you remove mods while the game is running it may crash your game.\n" + "Are you sure you want to do this?", + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } m_model->deleteMods(selection.indexes()); } void ModFolderPage::installMods() { - if (!m_controlsEnabled) - return; if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance @@ -214,8 +220,7 @@ void ModFolderPage::updateMods() message = tr("All selected mods are up-to-date! :)"); } } - CustomMessageBox::selectable(this, tr("Update checker"), message) - ->exec(); + CustomMessageBox::selectable(this, tr("Update checker"), message)->exec(); return; } |
