Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into resource_size

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2023-11-17 22:38:17 +02:00
commit 6b777653a5
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
33 changed files with 266 additions and 169 deletions

View File

@ -24,7 +24,7 @@ jobs:
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs - name: Create backport PRs
uses: korthout/backport-action@v2.0.0 uses: korthout/backport-action@v2.1.1
with: with:
# Config README: https://github.com/korthout/backport-action#backport-action # Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |- pull_description: |-

View File

@ -18,11 +18,18 @@
</a> </a>
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download). - All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download).
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions). - Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) tab (this also includes the pull requests status).
### Development Builds ### Development Builds
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger. Please understand that these builds are not intended for most users. There may be bugs, and other instabilities. You have been warned.
There are development builds available through:
- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contribuitors)
- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) (this will always point only to the latest version of develop)
These have debug information in the binaries, so their file sizes are relatively larger.
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**. Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.

30
flake.lock generated
View File

@ -21,11 +21,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1696343447, "lastModified": 1698882062,
"narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=", "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4", "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -76,11 +76,11 @@
"libnbtplusplus": { "libnbtplusplus": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1690036783, "lastModified": 1699286814,
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=", "narHash": "sha256-yy0q+bky80LtK1GWzz7qpM+aAGrOqLuewbid8WT1ilk=",
"owner": "PrismLauncher", "owner": "PrismLauncher",
"repo": "libnbtplusplus", "repo": "libnbtplusplus",
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f", "rev": "23b955121b8217c1c348a9ed2483167a6f3ff4ad",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -106,11 +106,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1698336494, "lastModified": 1699343069,
"narHash": "sha256-sO72WDBKyijYD1GcKPlGsycKbMBiTJMBCnmOxLAs880=", "narHash": "sha256-s7BBhyLA6MI6FuJgs4F/SgpntHBzz40/qV0xLPW6A1Q=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "808c0d8c53c7ae50f82aca8e7df263225cf235bf", "rev": "ec750fd01963ab6b20ee1f0cb488754e8036d89d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -123,11 +123,11 @@
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1696019113, "lastModified": 1698611440,
"narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=", "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a", "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -153,11 +153,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1698227354, "lastModified": 1699271226,
"narHash": "sha256-Fi5H9jbaQLmLw9qBi/mkR33CoFjNbobo5xWdX4tKz1Q=", "narHash": "sha256-8Jt1KW3xTjolD6c6OjJm9USx/jmL+VVmbooADCkdDfU=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "bd38df3d508dfcdff52cd243d297f218ed2257bf", "rev": "ea758da1a6dcde6dc36db348ed690d09b9864128",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -644,6 +644,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Minecraft mods // Minecraft mods
m_settings->registerSetting("ModMetadataDisabled", false); m_settings->registerSetting("ModMetadataDisabled", false);
m_settings->registerSetting("ModDependenciesDisabled", false);
// Minecraft offline player name // Minecraft offline player name
m_settings->registerSetting("LastOfflinePlayerName", ""); m_settings->registerSetting("LastOfflinePlayerName", "");

View File

@ -943,6 +943,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
<< "\n"; << "\n";
stream << "Type=Application" stream << "Type=Application"
<< "\n"; << "\n";
stream << "Categories=Game;ActionGame;AdventureGame;Simulation"
<< "\n";
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n"; stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n";
if (!icon.isEmpty()) { if (!icon.isEmpty()) {

View File

@ -89,7 +89,7 @@ void LaunchController::decideAccount()
// Tell the user they need to log in at least one account in order to play. // Tell the user they need to log in at least one account in order to play.
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
tr("In order to play Minecraft, you must have at least one Microsoft " tr("In order to play Minecraft, you must have at least one Microsoft "
"account which owns Minecraft logged in." "account which owns Minecraft logged in. "
"Would you like to open the account manager to add an account now?"), "Would you like to open the account manager to add an account now?"),
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
->exec(); ->exec();

View File

@ -5,6 +5,7 @@
#include <QPixmapCache> #include <QPixmapCache>
#include <QThread> #include <QThread>
#include <QTime> #include <QTime>
#include <limits>
#define GET_TYPE() \ #define GET_TYPE() \
Qt::ConnectionType type; \ Qt::ConnectionType type; \
@ -100,10 +101,14 @@ class PixmapCache final : public QObject {
*/ */
bool _markCacheMissByEviciton() bool _markCacheMissByEviciton()
{ {
static constexpr uint maxInt = static_cast<uint>(std::numeric_limits<int>::max());
static constexpr uint step = 10240;
static constexpr int oneSecond = 1000;
auto now = QTime::currentTime(); auto now = QTime::currentTime();
if (!m_last_cache_miss_by_eviciton.isNull()) { if (!m_last_cache_miss_by_eviciton.isNull()) {
auto diff = m_last_cache_miss_by_eviciton.msecsTo(now); auto diff = m_last_cache_miss_by_eviciton.msecsTo(now);
if (diff < 1000) { // less than a second ago if (diff < oneSecond) { // less than a second ago
++m_consecutive_fast_evicitons; ++m_consecutive_fast_evicitons;
} else { } else {
m_consecutive_fast_evicitons = 0; m_consecutive_fast_evicitons = 0;
@ -111,11 +116,17 @@ class PixmapCache final : public QObject {
} }
m_last_cache_miss_by_eviciton = now; m_last_cache_miss_by_eviciton = now;
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
// double the cache size // increase the cache size
auto newSize = _cacheLimit() * 2; uint newSize = _cacheLimit() + step;
qDebug() << m_consecutive_fast_evicitons << "pixmap cache misses by eviction happened too fast, doubling cache size to" if (newSize >= maxInt) { // increase it until you overflow :D
<< newSize; newSize = maxInt;
_setCacheLimit(newSize); qDebug() << m_consecutive_fast_evicitons
<< tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit");
} else {
qDebug() << m_consecutive_fast_evicitons
<< tr("pixmap cache misses by eviction happened too fast, increasing cache size to") << static_cast<int>(newSize);
}
_setCacheLimit(static_cast<int>(newSize));
m_consecutive_fast_evicitons = 0; m_consecutive_fast_evicitons = 0;
return true; return true;
} }

View File

@ -34,6 +34,7 @@
*/ */
#include <QDir> #include <QDir>
#include <QFileInfo>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -440,18 +441,26 @@ QStringList getMinecraftJavaBundle()
{ {
QString partialPath; QString partialPath;
QString executable = "java"; QString executable = "java";
QStringList processpaths;
#if defined(Q_OS_OSX) #if defined(Q_OS_OSX)
partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support");
#elif defined(Q_OS_WIN32) #elif defined(Q_OS_WIN32)
partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", "");
executable += "w.exe"; executable += "w.exe";
// add the microsoft store version of the launcher to the search. the current path is:
// C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime
auto minecraftMSStorePath =
FS::PathCombine(QFileInfo(partialPath).absolutePath(), "Local", "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe");
minecraftMSStorePath = FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime");
processpaths << minecraftMSStorePath;
#else #else
partialPath = QDir::homePath(); partialPath = QDir::homePath();
#endif #endif
auto minecraftPath = FS::PathCombine(partialPath, ".minecraft", "runtime"); auto minecraftDataPath = FS::PathCombine(partialPath, ".minecraft", "runtime");
QStringList javas; processpaths << minecraftDataPath;
QStringList processpaths{ minecraftPath };
QStringList javas;
while (!processpaths.isEmpty()) { while (!processpaths.isEmpty()) {
auto dirPath = processpaths.takeFirst(); auto dirPath = processpaths.takeFirst();
QDir dir(dirPath); QDir dir(dirPath);

View File

@ -575,15 +575,20 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) { if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) {
auto preloadList = env.value("LD_PRELOAD").split(QLatin1String(":")); QStringList preloadList;
auto libPaths = env.value("LD_LIBRARY_PATH").split(QLatin1String(":")); if (auto value = env.value("LD_PRELOAD"); !value.isEmpty())
preloadList = value.split(QLatin1String(":"));
QStringList libPaths;
if (auto value = env.value("LD_LIBRARY_PATH"); !value.isEmpty())
libPaths = value.split(QLatin1String(":"));
auto mangoHudLibString = MangoHud::getLibraryString(); auto mangoHudLibString = MangoHud::getLibraryString();
if (!mangoHudLibString.isEmpty()) { if (!mangoHudLibString.isEmpty()) {
QFileInfo mangoHudLib(mangoHudLibString); QFileInfo mangoHudLib(mangoHudLibString);
// dlsym variant is only needed for OpenGL and not included in the vulkan layer // dlsym variant is only needed for OpenGL and not included in the vulkan layer
preloadList << "libMangoHud_dlsym.so" << mangoHudLib.fileName(); preloadList << "libMangoHud_dlsym.so"
<< "libMangoHud_opengl.so" << mangoHudLib.fileName();
libPaths << mangoHudLib.absolutePath(); libPaths << mangoHudLib.absolutePath();
} }

View File

@ -37,9 +37,9 @@
#include "ModFolderModel.h" #include "ModFolderModel.h"
#include <FileSystem.h> #include <FileSystem.h>
#include <qheaderview.h>
#include <QDebug> #include <QDebug>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QHeaderView>
#include <QIcon> #include <QIcon>
#include <QMimeData> #include <QMimeData>
#include <QString> #include <QString>
@ -69,9 +69,8 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool
QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Size") }); QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Size") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION,
SortType::DATE, SortType::PROVIDER, SortType::SIZE }; SortType::DATE, SortType::PROVIDER, SortType::SIZE };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
QHeaderView::ResizeToContents };
m_columnsHideable = { false, true, false, true, true, true, true }; m_columnsHideable = { false, true, false, true, true, true, true };
} }
@ -138,6 +137,11 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
} }
return {}; return {};
} }
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole: case Qt::CheckStateRole:
switch (column) { switch (column) {
case ActiveColumn: case ActiveColumn:

View File

@ -4,6 +4,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QFileInfo> #include <QFileInfo>
#include <QHeaderView>
#include <QIcon> #include <QIcon>
#include <QMenu> #include <QMenu>
#include <QMimeData> #include <QMimeData>
@ -522,36 +523,22 @@ void ResourceFolderModel::setupHeaderAction(QAction* act, int column)
act->setText(columnNames().at(column)); act->setText(columnNames().at(column));
} }
void ResourceFolderModel::saveHiddenColumn(int column, bool hidden) void ResourceFolderModel::saveColumns(QTreeView* tree)
{ {
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); auto const setting_name = QString("UI/%1_Page/Columns").arg(id());
auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name)
: m_instance->settings()->registerSetting(setting_name); : m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList(); setting->set(tree->header()->saveState());
auto name = columnNames(false).at(column);
auto index = hiddenColumns.indexOf(name);
if (index >= 0 && !hidden) {
hiddenColumns.removeAt(index);
} else if (index < 0 && hidden) {
hiddenColumns.append(name);
}
setting->set(hiddenColumns);
} }
void ResourceFolderModel::loadHiddenColumns(QTreeView* tree) void ResourceFolderModel::loadColumns(QTreeView* tree)
{ {
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); auto const setting_name = QString("UI/%1_Page/Columns").arg(id());
auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name)
: m_instance->settings()->registerSetting(setting_name); : m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList(); tree->header()->restoreState(setting->get().toByteArray());
auto col_names = columnNames(false);
for (auto col_name : hiddenColumns) {
auto index = col_names.indexOf(col_name);
if (index >= 0)
tree->setColumnHidden(index, true);
}
} }
QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree) QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
@ -576,7 +563,7 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents) if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents)
tree->resizeColumnToContents(c); tree->resizeColumnToContents(c);
} }
saveHiddenColumn(col, !toggled); saveColumns(tree);
}); });
menu->addAction(act); menu->addAction(act);

View File

@ -117,8 +117,8 @@ class ResourceFolderModel : public QAbstractListModel {
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
void setupHeaderAction(QAction* act, int column); void setupHeaderAction(QAction* act, int column);
void saveHiddenColumn(int column, bool hidden); void saveColumns(QTreeView* tree);
void loadHiddenColumns(QTreeView* tree); void loadColumns(QTreeView* tree);
QMenu* createHeaderContextMenu(QTreeView* tree); QMenu* createHeaderContextMenu(QTreeView* tree);
/** This creates a proxy model to filter / sort the model for a UI. /** This creates a proxy model to filter / sort the model for a UI.
@ -201,8 +201,8 @@ class ResourceFolderModel : public QAbstractListModel {
QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::SIZE }; QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::SIZE };
QStringList m_column_names = { "Enable", "Name", "Last Modified", "Size" }; QStringList m_column_names = { "Enable", "Name", "Last Modified", "Size" };
QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Size") }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Size") };
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; QHeaderView::Interactive };
QList<bool> m_columnsHideable = { false, false, true, true }; QList<bool> m_columnsHideable = { false, false, true, true };
QDir m_dir; QDir m_dir;

View File

@ -54,8 +54,8 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc
m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Size" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Size" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Size") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Size") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::SIZE }; m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::SIZE };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch,
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
m_columnsHideable = { false, true, false, true, true, true }; m_columnsHideable = { false, true, false, true, true, true };
} }
@ -121,6 +121,11 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
} }
return m_resources[row]->internal_id(); return m_resources[row]->internal_id();
} }
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole: case Qt::CheckStateRole:
switch (column) { switch (column) {
case ActiveColumn: case ActiveColumn:

View File

@ -48,8 +48,8 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance*
m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Size" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Size" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Size") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Size") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::SIZE }; m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::SIZE };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
QHeaderView::ResizeToContents }; QHeaderView::Interactive };
m_columnsHideable = { false, true, false, true, true }; m_columnsHideable = { false, true, false, true, true };
} }
@ -107,6 +107,11 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
} }
return {}; return {};
} }
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole: case Qt::CheckStateRole:
if (column == ActiveColumn) { if (column == ActiveColumn) {
return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked;

View File

@ -208,17 +208,15 @@ Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::
return netJob; return netJob;
} }
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
{ 3, "LastUpdated", QObject::tr("Sort by Last Updated") },
{ 4, "Name", QObject::tr("Sort by Name") },
{ 5, "Author", QObject::tr("Sort by Author") },
{ 6, "TotalDownloads", QObject::tr("Sort by Downloads") },
{ 7, "Category", QObject::tr("Sort by Category") },
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const
{ {
return s_sorts; // https://docs.curseforge.com/?python#tocS_ModsSearchSortField
return { { 1, "Featured", QObject::tr("Sort by Featured") },
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
{ 3, "LastUpdated", QObject::tr("Sort by Last Updated") },
{ 4, "Name", QObject::tr("Sort by Name") },
{ 5, "Author", QObject::tr("Sort by Author") },
{ 6, "TotalDownloads", QObject::tr("Sort by Downloads") },
{ 7, "Category", QObject::tr("Sort by Category") },
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
} }

View File

@ -111,14 +111,12 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteAr
return netJob; return netJob;
} }
// https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "relevance", QObject::tr("Sort by Relevance") },
{ 2, "downloads", QObject::tr("Sort by Downloads") },
{ 3, "follows", QObject::tr("Sort by Follows") },
{ 4, "newest", QObject::tr("Sort by Last Updated") },
{ 5, "updated", QObject::tr("Sort by Newest") } };
QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const
{ {
return s_sorts; // https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects
return { { 1, "relevance", QObject::tr("Sort by Relevance") },
{ 2, "downloads", QObject::tr("Sort by Downloads") },
{ 3, "follows", QObject::tr("Sort by Follows") },
{ 4, "newest", QObject::tr("Sort by Newest") },
{ 5, "updated", QObject::tr("Sort by Last Updated") } };
} }

View File

@ -101,7 +101,7 @@ QString getCreditsHtml()
stream << "<h3>" << QObject::tr("With thanks to", "About Credits") << "</h3>\n"; stream << "<h3>" << QObject::tr("With thanks to", "About Credits") << "</h3>\n";
stream << QString("<p>Boba %1</p>\n").arg(getWebsite("https://bobaonline.neocities.org/")); stream << QString("<p>Boba %1</p>\n").arg(getWebsite("https://bobaonline.neocities.org/"));
stream << QString("<p>Davi Rafael %1</p>\n").arg(getWebsite("https://auti.one/")); stream << QString("<p>Davi Rafael %1</p>\n").arg(getWebsite("https://auti.one/"));
stream << QString("<p>Fulmine %1</p>\n").arg(getWebsite("https://www.fulmine.xyz/")); stream << QString("<p>Fulmine %1</p>\n").arg(getWebsite("https://fulmine.xyz/"));
stream << QString("<p>ely %1</p>\n").arg(getGitHub("elyrodso")); stream << QString("<p>ely %1</p>\n").arg(getGitHub("elyrodso"));
stream << QString("<p>gon sawa %1</p>\n").arg(getGitHub("gonsawa")); stream << QString("<p>gon sawa %1</p>\n").arg(getGitHub("gonsawa"));
stream << QString("<p>Pankakes</p>\n"); stream << QString("<p>Pankakes</p>\n");

View File

@ -13,7 +13,6 @@ enum class ResourceProvider;
class Mod; class Mod;
class NetJob; class NetJob;
class ModUpdateDialog;
class ChooseProviderDialog : public QDialog { class ChooseProviderDialog : public QDialog {
Q_OBJECT Q_OBJECT

View File

@ -214,10 +214,11 @@ void ExportToModListDialog::addExtra(ExportToModList::OptionalData option)
void ExportToModListDialog::enableCustom(bool enabled) void ExportToModListDialog::enableCustom(bool enabled)
{ {
ui->authorsCheckBox->setHidden(enabled); ui->authorsCheckBox->setHidden(enabled);
ui->versionCheckBox->setHidden(enabled);
ui->urlCheckBox->setHidden(enabled);
ui->authorsButton->setHidden(!enabled); ui->authorsButton->setHidden(!enabled);
ui->versionCheckBox->setHidden(enabled);
ui->versionButton->setHidden(!enabled); ui->versionButton->setHidden(!enabled);
ui->urlCheckBox->setHidden(enabled);
ui->urlButton->setHidden(!enabled); ui->urlButton->setHidden(!enabled);
} }

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>650</width> <width>650</width>
<height>446</height> <height>522</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -61,18 +61,37 @@
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QGroupBox" name="templateGroup"> <widget class="QGroupBox" name="templateGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title"> <property name="title">
<string>Template</string> <string>Template</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
<item> <item>
<widget class="QTextEdit" name="templateText"/> <widget class="QTextEdit" name="templateText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QGroupBox" name="optionsGroup"> <widget class="QGroupBox" name="optionsGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title"> <property name="title">
<string>Optional Info</string> <string>Optional Info</string>
</property> </property>

View File

@ -105,8 +105,16 @@ void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString&
QString urlString = uri.toString(); QString urlString = uri.toString();
QString linkString = QString("<a href=\"%1\">%2</a>").arg(urlString, urlString); QString linkString = QString("<a href=\"%1\">%2</a>").arg(urlString, urlString);
ui->label->setText( if (urlString == "https://www.microsoft.com/link" && !code.isEmpty()) {
tr("<p>Please open up %1 in a browser and put in the code <b>%2</b> to proceed with login.</p>").arg(linkString, code)); urlString += QString("?otc=%1").arg(code);
DesktopServices::openUrl(urlString);
ui->label->setText(tr("<p>Please login in the opened browser. If no browser was opened, please open up %1 in "
"a browser and put in the code <b>%2</b> to proceed with login.</p>")
.arg(linkString, code));
} else {
ui->label->setText(
tr("<p>Please open up %1 in a browser and put in the code <b>%2</b> to proceed with login.</p>").arg(linkString, code));
}
ui->actionButton->setVisible(true); ui->actionButton->setVisible(true);
connect(ui->actionButton, &QPushButton::clicked, [=]() { connect(ui->actionButton, &QPushButton::clicked, [=]() {
DesktopServices::openUrl(uri); DesktopServices::openUrl(uri);

View File

@ -39,7 +39,8 @@ static std::optional<ModPlatform::ModLoaderTypes> mcLoaders(BaseInstance* inst)
ModUpdateDialog::ModUpdateDialog(QWidget* parent, ModUpdateDialog::ModUpdateDialog(QWidget* parent,
BaseInstance* instance, BaseInstance* instance,
const std::shared_ptr<ModFolderModel> mods, const std::shared_ptr<ModFolderModel> mods,
QList<Mod*>& search_for) QList<Mod*>& search_for,
bool includeDeps)
: ReviewMessageBox(parent, tr("Confirm mods to update"), "") : ReviewMessageBox(parent, tr("Confirm mods to update"), "")
, m_parent(parent) , m_parent(parent)
, m_mod_model(mods) , m_mod_model(mods)
@ -47,6 +48,7 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent,
, m_second_try_metadata( , m_second_try_metadata(
new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())) new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()))
, m_instance(instance) , m_instance(instance)
, m_include_deps(includeDeps)
{ {
ReviewMessageBox::setGeometry(0, 0, 800, 600); ReviewMessageBox::setGeometry(0, 0, 800, 600);
@ -186,7 +188,7 @@ void ModUpdateDialog::checkCandidates()
} }
} }
{ // dependencies if (m_include_deps && !APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies
auto depTask = makeShared<GetModDependenciesTask>(this, m_instance, m_mod_model.get(), selectedVers); auto depTask = makeShared<GetModDependenciesTask>(this, m_instance, m_mod_model.get(), selectedVers);
connect(depTask.get(), &Task::failed, this, connect(depTask.get(), &Task::failed, this,

View File

@ -19,7 +19,8 @@ class ModUpdateDialog final : public ReviewMessageBox {
explicit ModUpdateDialog(QWidget* parent, explicit ModUpdateDialog(QWidget* parent,
BaseInstance* instance, BaseInstance* instance,
const std::shared_ptr<ModFolderModel> mod_model, const std::shared_ptr<ModFolderModel> mod_model,
QList<Mod*>& search_for); QList<Mod*>& search_for,
bool includeDeps);
void checkCandidates(); void checkCandidates();
@ -61,4 +62,5 @@ class ModUpdateDialog final : public ReviewMessageBox {
bool m_no_updates = false; bool m_no_updates = false;
bool m_aborted = false; bool m_aborted = false;
bool m_include_deps = false;
}; };

View File

@ -270,13 +270,15 @@ QList<BasePage*> ModDownloadDialog::getPages()
GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask() GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask()
{ {
if (auto model = dynamic_cast<ModFolderModel*>(getBaseModel().get()); model) { if (!APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies
QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> selectedVers; if (auto model = dynamic_cast<ModFolderModel*>(getBaseModel().get()); model) {
for (auto& selected : getTasks()) { QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> selectedVers;
selectedVers.append(std::make_shared<GetModDependenciesTask::PackDependency>(selected->getPack(), selected->getVersion())); for (auto& selected : getTasks()) {
} selectedVers.append(std::make_shared<GetModDependenciesTask::PackDependency>(selected->getPack(), selected->getVersion()));
}
return makeShared<GetModDependenciesTask>(this, m_instance, model, selectedVers); return makeShared<GetModDependenciesTask>(this, m_instance, model, selectedVers);
}
} }
return nullptr; return nullptr;
} }

View File

@ -278,12 +278,14 @@ void InstanceView::mousePressEvent(QMouseEvent* event)
m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex);
m_pressedPosition = geometryPos; m_pressedPosition = geometryPos;
VisualGroup::HitResults hitResult; if (event->button() == Qt::LeftButton) {
m_pressedCategory = categoryAt(geometryPos, hitResult); VisualGroup::HitResults hitResult;
if (m_pressedCategory && hitResult & VisualGroup::CheckboxHit) { m_pressedCategory = categoryAt(geometryPos, hitResult);
setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState); if (m_pressedCategory && hitResult & VisualGroup::CheckboxHit) {
event->accept(); setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState);
return; event->accept();
return;
}
} }
if (index.isValid() && (index.flags() & Qt::ItemIsEnabled)) { if (index.isValid() && (index.flags() & Qt::ItemIsEnabled)) {
@ -366,10 +368,7 @@ void InstanceView::mouseReleaseEvent(QMouseEvent* event)
VisualGroup::HitResults hitResult; VisualGroup::HitResults hitResult;
bool click = if (event->button() == Qt::LeftButton && m_pressedCategory != nullptr && m_pressedCategory == categoryAt(geometryPos, hitResult)) {
(index == m_pressedIndex && index.isValid()) || (m_pressedCategory && m_pressedCategory == categoryAt(geometryPos, hitResult));
if (click && m_pressedCategory) {
if (state() == ExpandingState) { if (state() == ExpandingState) {
m_pressedCategory->collapsed = false; m_pressedCategory->collapsed = false;
emit groupStateChanged(m_pressedCategory->text, false); emit groupStateChanged(m_pressedCategory->text, false);
@ -397,7 +396,7 @@ void InstanceView::mouseReleaseEvent(QMouseEvent* event)
setState(NoState); setState(NoState);
if (click) { if (index == m_pressedIndex && index.isValid()) {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
emit clicked(index); emit clicked(index);
} }

View File

@ -42,22 +42,17 @@
#include <QDebug> #include <QDebug>
#include "net/NetJob.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/MSALoginDialog.h"
#include "ui/dialogs/OfflineLoginDialog.h" #include "ui/dialogs/OfflineLoginDialog.h"
#include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/ProgressDialog.h"
#include "ui/dialogs/SkinUploadDialog.h" #include "ui/dialogs/SkinUploadDialog.h"
#include "minecraft/auth/AccountTask.h"
#include "minecraft/services/SkinDelete.h" #include "minecraft/services/SkinDelete.h"
#include "tasks/Task.h" #include "tasks/Task.h"
#include "Application.h" #include "Application.h"
#include "BuildConfig.h"
AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new Ui::AccountListPage) AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new Ui::AccountListPage)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -172,6 +167,12 @@ void AccountListPage::on_actionAddOffline_triggered()
void AccountListPage::on_actionRemove_triggered() void AccountListPage::on_actionRemove_triggered()
{ {
auto response = CustomMessageBox::selectable(this, tr("Remove account?"), tr("Do you really want to delete this account?"),
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
if (response != QMessageBox::Yes) {
return;
}
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
if (selection.size() > 0) { if (selection.size() > 0) {
QModelIndex selected = selection.first(); QModelIndex selected = selection.first();
@ -230,6 +231,7 @@ void AccountListPage::updateButtonStates()
ui->actionNoDefault->setEnabled(true); ui->actionNoDefault->setEnabled(true);
ui->actionNoDefault->setChecked(false); ui->actionNoDefault->setChecked(false);
} }
ui->listView->resizeColumnToContents(3);
} }
void AccountListPage::on_actionUploadSkin_triggered() void AccountListPage::on_actionUploadSkin_triggered()

View File

@ -223,6 +223,7 @@ void LauncherPage::applySettings()
// Mods // Mods
s->set("ModMetadataDisabled", ui->metadataDisableBtn->isChecked()); s->set("ModMetadataDisabled", ui->metadataDisableBtn->isChecked());
s->set("ModDependenciesDisabled", ui->dependenciesDisableBtn->isChecked());
} }
void LauncherPage::loadSettings() void LauncherPage::loadSettings()
{ {
@ -278,6 +279,7 @@ void LauncherPage::loadSettings()
// Mods // Mods
ui->metadataDisableBtn->setChecked(s->get("ModMetadataDisabled").toBool()); ui->metadataDisableBtn->setChecked(s->get("ModMetadataDisabled").toBool());
ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked());
ui->dependenciesDisableBtn->setChecked(s->get("ModDependenciesDisabled").toBool());
} }
void LauncherPage::refreshFontPreview() void LauncherPage::refreshFontPreview()

View File

@ -186,6 +186,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="dependenciesDisableBtn">
<property name="toolTip">
<string>Disable the automatic detection, installation, and updating of mod dependencies.</string>
</property>
<property name="text">
<string>Disable automatic mod dependency management</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -42,6 +42,7 @@
#include "minecraft/mod/ResourceFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h"
#include "ui/GuiUtil.h" #include "ui/GuiUtil.h"
#include <QHeaderView>
#include <QKeyEvent> #include <QKeyEvent>
#include <QMenu> #include <QMenu>
#include <algorithm> #include <algorithm>
@ -95,7 +96,8 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
connect(viewHeader, &QHeaderView::customContextMenuRequested, this, &ExternalResourcesPage::ShowHeaderContextMenu); connect(viewHeader, &QHeaderView::customContextMenuRequested, this, &ExternalResourcesPage::ShowHeaderContextMenu);
m_model->loadHiddenColumns(ui->treeView); m_model->loadColumns(ui->treeView);
connect(ui->treeView->header(), &QHeaderView::sectionResized, this, [this] { m_model->saveColumns(ui->treeView); });
} }
ExternalResourcesPage::~ExternalResourcesPage() ExternalResourcesPage::~ExternalResourcesPage()

View File

@ -70,6 +70,9 @@
</layout> </layout>
</widget> </widget>
<widget class="WideBar" name="actionsToolbar"> <widget class="WideBar" name="actionsToolbar">
<property name="useDefaultAction" stdset="0">
<bool>true</bool>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Actions</string> <string>Actions</string>
</property> </property>
@ -146,17 +149,6 @@
<string>Download a new resource</string> <string>Download a new resource</string>
</property> </property>
</action> </action>
<action name="actionUpdateItem">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Check for &amp;Updates</string>
</property>
<property name="toolTip">
<string>Try to check or update all selected resources (all resources if none are selected)</string>
</property>
</action>
<action name="actionVisitItemPage"> <action name="actionVisitItemPage">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
@ -168,15 +160,15 @@
<string>Go to mods home page</string> <string>Go to mods home page</string>
</property> </property>
</action> </action>
<action name="actionRemoveItemMetadata"> <action name="actionUpdateItem">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Remove metadata</string> <string>Check for &amp;Updates</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Remove mod's metadata</string> <string>Try to check or update all selected resources (all resources if none are selected)</string>
</property> </property>
</action> </action>
</widget> </widget>

View File

@ -84,50 +84,71 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::installMods); connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::installMods);
// update menu
auto updateMenu = ui->actionUpdateItem->menu();
if (updateMenu) {
updateMenu->clear();
} else {
updateMenu = new QMenu(this);
}
auto update = updateMenu->addAction(tr("Check for Updates"));
update->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)"));
connect(update, &QAction::triggered, this, &ModFolderPage::updateMods);
auto updateWithDeps = updateMenu->addAction(tr("Verify Dependencies"));
updateWithDeps->setToolTip(
tr("Try to update and check for missing dependencies all selected mods (all mods if none are selected)"));
connect(updateWithDeps, &QAction::triggered, this, [this] { updateMods(true); });
auto depsDisabled = APPLICATION->settings()->getSetting("ModDependenciesDisabled");
updateWithDeps->setVisible(!depsDisabled->get().toBool());
connect(depsDisabled.get(), &Setting::SettingChanged, this,
[updateWithDeps](const Setting& setting, QVariant value) { updateWithDeps->setVisible(!value.toBool()); });
auto actionRemoveItemMetadata = updateMenu->addAction(tr("Reset update metadata"));
actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata"));
connect(actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata);
actionRemoveItemMetadata->setEnabled(false);
ui->actionUpdateItem->setMenu(updateMenu);
ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)"));
ui->actionsToolbar->insertActionAfter(ui->actionAddItem, ui->actionUpdateItem);
connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods); connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods);
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem);
ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page"));
ui->actionsToolbar->addAction(ui->actionVisitItemPage); ui->actionsToolbar->addAction(ui->actionVisitItemPage);
connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages); connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages);
ui->actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata"));
ui->actionsToolbar->insertActionAfter(ui->actionRemoveItem, ui->actionRemoveItemMetadata);
connect(ui->actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata);
auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); };
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this, check_allow_update] { connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
ui->actionUpdateItem->setEnabled(check_allow_update()); [this, check_allow_update, actionRemoveItemMetadata] {
ui->actionUpdateItem->setEnabled(check_allow_update());
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
auto mods_list = m_model->selectedMods(selection); auto mods_list = m_model->selectedMods(selection);
auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(), auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(),
[](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; }); [](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; });
if (selected <= 1) { if (selected <= 1) {
ui->actionVisitItemPage->setText(tr("Visit mod's page")); ui->actionVisitItemPage->setText(tr("Visit mod's page"));
ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page"));
ui->actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata")); } else {
} else { ui->actionVisitItemPage->setText(tr("Visit mods' pages"));
ui->actionVisitItemPage->setText(tr("Visit mods' pages")); ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods"));
ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods")); }
ui->actionVisitItemPage->setEnabled(selected != 0);
actionRemoveItemMetadata->setEnabled(selected != 0);
});
ui->actionRemoveItemMetadata->setToolTip(tr("Remove mods' metadata")); auto updateButtons = [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); };
} connect(mods.get(), &ModFolderModel::rowsInserted, this, updateButtons);
ui->actionVisitItemPage->setEnabled(selected != 0);
ui->actionRemoveItemMetadata->setEnabled(selected != 0);
});
connect(mods.get(), &ModFolderModel::rowsInserted, this, connect(mods.get(), &ModFolderModel::rowsRemoved, this, updateButtons);
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
connect(mods.get(), &ModFolderModel::rowsRemoved, this, connect(mods.get(), &ModFolderModel::updateFinished, this, updateButtons);
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
connect(mods.get(), &ModFolderModel::updateFinished, this,
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
} }
} }
@ -204,7 +225,7 @@ void ModFolderPage::installMods()
} }
} }
void ModFolderPage::updateMods() void ModFolderPage::updateMods(bool includeDeps)
{ {
if (m_instance->typeName() != "Minecraft") if (m_instance->typeName() != "Minecraft")
return; // this is a null instance or a legacy instance return; // this is a null instance or a legacy instance
@ -214,6 +235,10 @@ void ModFolderPage::updateMods()
QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!")); QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!"));
return; return;
} }
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
QMessageBox::critical(this, tr("Error"), tr("Mod updates are unavailable when metadata is disabled!"));
return;
}
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
auto mods_list = m_model->selectedMods(selection); auto mods_list = m_model->selectedMods(selection);
@ -221,7 +246,7 @@ void ModFolderPage::updateMods()
if (use_all) if (use_all)
mods_list = m_model->allMods(); mods_list = m_model->allMods();
ModUpdateDialog update_dialog(this, m_instance, m_model, mods_list); ModUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps);
update_dialog.checkCandidates(); update_dialog.checkCandidates();
if (update_dialog.aborted()) { if (update_dialog.aborted()) {

View File

@ -64,7 +64,7 @@ class ModFolderPage : public ExternalResourcesPage {
void deleteModMetadata(); void deleteModMetadata();
void installMods(); void installMods();
void updateMods(); void updateMods(bool includeDeps = false);
void visitModPages(); void visitModPages();
protected: protected:

View File

@ -48,14 +48,14 @@ void ModListView::setModel(QAbstractItemModel* model)
return; return;
} }
if (!string.size()) { if (!string.size()) {
head->setSectionResizeMode(0, QHeaderView::ResizeToContents); head->setSectionResizeMode(0, QHeaderView::Interactive);
head->setSectionResizeMode(1, QHeaderView::Stretch); head->setSectionResizeMode(1, QHeaderView::Stretch);
for (int i = 2; i < head->count(); i++) for (int i = 2; i < head->count(); i++)
head->setSectionResizeMode(i, QHeaderView::ResizeToContents); head->setSectionResizeMode(i, QHeaderView::Interactive);
} else { } else {
head->setSectionResizeMode(0, QHeaderView::Stretch); head->setSectionResizeMode(0, QHeaderView::Stretch);
for (int i = 1; i < head->count(); i++) for (int i = 1; i < head->count(); i++)
head->setSectionResizeMode(i, QHeaderView::ResizeToContents); head->setSectionResizeMode(i, QHeaderView::Interactive);
} }
} }