This commit is contained in:
Trial97 2023-11-23 12:22:41 +02:00
commit 2268e46b91
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
86 changed files with 540 additions and 303 deletions

View File

@ -1,4 +1,5 @@
Checks: Checks:
- modernize-use-using - modernize-use-using
- readability-avoid-const-params-in-decls
SystemHeaders: false SystemHeaders: false

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

@ -594,7 +594,7 @@ jobs:
flatpak: flatpak:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08 image: bilelmoussaoui/flatpak-github-actions:kde-5.15-23.08
options: --privileged options: --privileged
steps: steps:
- name: Checkout - name: Checkout

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**.

View File

@ -68,6 +68,8 @@ function(
/w14906 # string literal cast to 'LPWSTR' /w14906 # string literal cast to 'LPWSTR'
/w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
/permissive- # standards conformance mode for MSVC compiler. /permissive- # standards conformance mode for MSVC compiler.
/we4062 # forbid omitting a possible value of an enum in a switch statement
) )
endif() endif()
@ -93,6 +95,8 @@ function(
# in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour # in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour
# instead of the exact standard wording so we can safely ignore it # instead of the exact standard wording so we can safely ignore it
-Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-zero-variadic-macro-arguments
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
) )
endif() endif()
@ -104,6 +108,8 @@ function(
-Wduplicated-branches # warn if if / else branches have duplicated code -Wduplicated-branches # warn if if / else branches have duplicated code
-Wlogical-op # warn about logical operations being used where bitwise were probably wanted -Wlogical-op # warn about logical operations being used where bitwise were probably wanted
-Wuseless-cast # warn if you perform a cast to the same type -Wuseless-cast # warn if you perform a cast to the same type
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
) )
endif() endif()
@ -128,6 +134,8 @@ function(
-Woverloaded-virtual -Woverloaded-virtual
-Wuseless-cast -Wuseless-cast
-Wextra-semi -Wextra-semi
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
) )
target_compile_options( target_compile_options(

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": 1700108881,
"narHash": "sha256-sO72WDBKyijYD1GcKPlGsycKbMBiTJMBCnmOxLAs880=", "narHash": "sha256-+Lqybl8kj0+nD/IlAWPPG/RDTa47gff9nbei0u7BntE=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "808c0d8c53c7ae50f82aca8e7df263225cf235bf", "rev": "7414e9ee0b3e9903c24d3379f577a417f0aae5f1",
"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": 1700064067,
"narHash": "sha256-Fi5H9jbaQLmLw9qBi/mkR33CoFjNbobo5xWdX4tKz1Q=", "narHash": "sha256-1ZWNDzhu8UlVCK7+DUN9dVQfiHX1bv6OQP9VxstY/gs=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "bd38df3d508dfcdff52cd243d297f218ed2257bf", "rev": "e558068cba67b23b4fbc5537173dbb43748a17e8",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -1,6 +1,6 @@
id: org.prismlauncher.PrismLauncher id: org.prismlauncher.PrismLauncher
runtime: org.kde.Platform runtime: org.kde.Platform
runtime-version: "5.15-22.08" runtime-version: "5.15-23.08"
sdk: org.kde.Sdk sdk: org.kde.Sdk
sdk-extensions: sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17 - org.freedesktop.Sdk.Extension.openjdk17
@ -113,6 +113,9 @@ modules:
version-query: .tag_name version-query: .tag_name
url-query: .tarball_url url-query: .tarball_url
timestamp-query: .published_at timestamp-query: .published_at
# from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch
- type: patch
path: patches/gamemode.patch
cleanup: cleanup:
- /include - /include
- /lib/pkgconfig - /lib/pkgconfig

View File

@ -0,0 +1,12 @@
diff -ruN a/common/common-pidfds.c b/common/common-pidfds.c
--- a/common/common-pidfds.c 2021-02-18 20:00:12.000000000 +0100
+++ b/common/common-pidfds.c 2023-09-07 08:57:42.954362763 +0200
@@ -58,6 +58,8 @@
{
return (int)syscall(__NR_pidfd_open, pid, flags);
}
+#else
+#include <sys/pidfd.h>
#endif
/* pidfd functions */

View File

@ -1,5 +1,6 @@
builds: builds:
exclude: [] exclude:
- "*.x86_64-darwin.*"
include: include:
- "checks.x86_64-linux.*" - "checks.x86_64-linux.*"
- "devShells.*.*" - "devShells.*.*"

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

@ -18,7 +18,7 @@
class DataMigrationTask : public Task { class DataMigrationTask : public Task {
Q_OBJECT Q_OBJECT
public: public:
explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathmatcher); explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, IPathMatcher::Ptr pathmatcher);
~DataMigrationTask() override = default; ~DataMigrationTask() override = default;
protected: protected:

View File

@ -108,12 +108,9 @@ bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if (!isSandbox()) { if (!isSandbox()) {
return IndirectOpen(f); return IndirectOpen(f);
} else {
return f();
} }
#else
return f();
#endif #endif
return f();
} }
bool openFile(const QString& path) bool openFile(const QString& path)

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

@ -59,7 +59,7 @@
#include <quazip/quazipdir.h> #include <quazip/quazipdir.h>
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info) InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent) : m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
{} {}

View File

@ -54,7 +54,7 @@ class FileResolvingTask;
class InstanceImportTask : public InstanceTask { class InstanceImportTask : public InstanceTask {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {}); explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
bool abort() override; bool abort() override;
const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; } const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; }

View File

@ -290,7 +290,7 @@ void InstanceList::deleteGroup(const GroupId& name)
qDebug() << "Remove" << instID << "from group" << name; qDebug() << "Remove" << instID << "from group" << name;
removed = true; removed = true;
auto idx = getInstIndex(instance.get()); auto idx = getInstIndex(instance.get());
if (idx > 0) if (idx >= 0)
emit dataChanged(index(idx), index(idx), { GroupRole }); emit dataChanged(index(idx), index(idx), { GroupRole });
} }
} }
@ -315,7 +315,7 @@ void InstanceList::renameGroup(const QString& src, const QString& dst)
qDebug() << "Set" << instID << "group to" << dst; qDebug() << "Set" << instID << "group to" << dst;
modified = true; modified = true;
auto idx = getInstIndex(instance.get()); auto idx = getInstIndex(instance.get());
if (idx > 0) if (idx >= 0)
emit dataChanged(index(idx), index(idx), { GroupRole }); emit dataChanged(index(idx), index(idx), { GroupRole });
} }
} }
@ -823,6 +823,9 @@ void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting,
} }
m_instDir = newInstDir; m_instDir = newInstDir;
m_groupsLoaded = false; m_groupsLoaded = false;
beginRemoveRows(QModelIndex(), 0, count());
m_instances.erase(m_instances.begin(), m_instances.end());
endRemoveRows();
emit instancesChanged(); emit instancesChanged();
} }
} }
@ -947,9 +950,12 @@ QString InstanceList::getStagedInstancePath()
bool InstanceList::commitStagedInstance(const QString& path, bool InstanceList::commitStagedInstance(const QString& path,
InstanceName const& instanceName, InstanceName const& instanceName,
const QString& groupName, QString groupName,
InstanceTask const& commiting) InstanceTask const& commiting)
{ {
if (groupName.isEmpty() && !groupName.isNull())
groupName = QString();
QDir dir; QDir dir;
QString instID; QString instID;
InstancePtr inst; InstancePtr inst;

View File

@ -130,7 +130,7 @@ class InstanceList : public QAbstractListModel {
* should_override is used when another similar instance already exists, and we want to override it * should_override is used when another similar instance already exists, and we want to override it
* - for instance, when updating it. * - for instance, when updating it.
*/ */
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, const InstanceTask&); bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, QString groupName, const InstanceTask&);
/** /**
* Destroy a previously created staging area given by @keyPath - used when creation fails. * Destroy a previously created staging area given by @keyPath - used when creation fails.

View File

@ -91,7 +91,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();
@ -169,7 +169,7 @@ void LaunchController::login()
m_accountToUse->fillSession(m_session); m_accountToUse->fillSession(m_session);
// Launch immediately in true offline mode // Launch immediately in true offline mode
if (m_accountToUse->isOffline()) { if (m_accountToUse->accountType() == AccountType::Offline) {
launchInstance(); launchInstance();
return; return;
} }

View File

@ -50,7 +50,7 @@
namespace MMCZip { namespace MMCZip {
// ours // ours
bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter) bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction& filter)
{ {
QuaZip modZip(from.filePath()); QuaZip modZip(from.filePath());
modZip.open(QuaZip::mdUnzip); modZip.open(QuaZip::mdUnzip);

View File

@ -60,7 +60,7 @@ using FilterFunction = std::function<bool(const QString&)>;
/** /**
* Merge two zip files, using a filter function * Merge two zip files, using a filter function
*/ */
bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter = nullptr); bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction& filter = nullptr);
/** /**
* Compress directory, by providing a list of files to compress * Compress directory, by providing a list of files to compress

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

@ -13,7 +13,7 @@ class RecursiveFileSystemWatcher : public QObject {
QDir rootDir() const { return m_root; } QDir rootDir() const { return m_root; }
// WARNING: setting this to true may be bad for performance // WARNING: setting this to true may be bad for performance
void setWatchFiles(const bool watchFiles); void setWatchFiles(bool watchFiles);
bool watchFiles() const { return m_watchFiles; } bool watchFiles() const { return m_watchFiles; }
void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; } void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; }

View File

@ -32,7 +32,7 @@ class ResourceDownloadTask : public SequentialTask {
public: public:
explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion version, ModPlatform::IndexedVersion version,
const std::shared_ptr<ResourceFolderModel> packs, std::shared_ptr<ResourceFolderModel> packs,
bool is_indexed = true, bool is_indexed = true,
QString custom_target_folder = {}); QString custom_target_folder = {});
const QString& getFilename() const { return m_pack_version.fileName; } const QString& getFilename() const { return m_pack_version.fileName; }

View File

@ -28,7 +28,7 @@ class VersionProxyModel : public QAbstractProxyModel {
const FilterMap& filters() const; const FilterMap& filters() const;
const QString& search() const; const QString& search() const;
void setFilter(const BaseVersionList::ModelRoles column, Filter* filter); void setFilter(BaseVersionList::ModelRoles column, Filter* filter);
void setSearch(const QString& search); void setSearch(const QString& search);
void clearFilters(); void clearFilters();
QModelIndex getRecommended() const; QModelIndex getRecommended() const;

View File

@ -67,7 +67,7 @@ class IconList : public QAbstractListModel {
virtual Qt::ItemFlags flags(const QModelIndex& index) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
bool addThemeIcon(const QString& key); bool addThemeIcon(const QString& key);
bool addIcon(const QString& key, const QString& name, const QString& path, const IconType type); bool addIcon(const QString& key, const QString& name, const QString& path, IconType type);
void saveIcon(const QString& key, const QString& path, const char* format) const; void saveIcon(const QString& key, const QString& path, const char* format) const;
bool deleteIcon(const QString& key); bool deleteIcon(const QString& key);
bool trashIcon(const QString& key); bool trashIcon(const QString& key);

View File

@ -34,6 +34,7 @@
*/ */
#include <QDir> #include <QDir>
#include <QFileInfo>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -335,6 +336,7 @@ QList<QString> JavaUtils::FindJavaPaths()
} }
} }
candidates.append(getMinecraftJavaBundle());
candidates = addJavasFromEnv(candidates); candidates = addJavasFromEnv(candidates);
candidates.removeDuplicates(); candidates.removeDuplicates();
return candidates; return candidates;
@ -360,6 +362,7 @@ QList<QString> JavaUtils::FindJavaPaths()
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
} }
javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
javas.removeDuplicates(); javas.removeDuplicates();
return javas; return javas;
@ -411,6 +414,7 @@ QList<QString> JavaUtils::FindJavaPaths()
// javas downloaded by sdkman // javas downloaded by sdkman
scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java"));
javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
javas.removeDuplicates(); javas.removeDuplicates();
return javas; return javas;
@ -423,6 +427,7 @@ QList<QString> JavaUtils::FindJavaPaths()
QList<QString> javas; QList<QString> javas;
javas.append(this->GetDefaultJava()->path); javas.append(this->GetDefaultJava()->path);
javas.append(getMinecraftJavaBundle());
return addJavasFromEnv(javas); return addJavasFromEnv(javas);
} }
#endif #endif
@ -431,3 +436,50 @@ QString JavaUtils::getJavaCheckPath()
{ {
return APPLICATION->getJarPath("JavaCheck.jar"); return APPLICATION->getJarPath("JavaCheck.jar");
} }
QStringList getMinecraftJavaBundle()
{
QString partialPath;
QString executable = "java";
QStringList processpaths;
#if defined(Q_OS_OSX)
partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support");
#elif defined(Q_OS_WIN32)
partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", "");
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
partialPath = QDir::homePath();
#endif
auto minecraftDataPath = FS::PathCombine(partialPath, ".minecraft", "runtime");
processpaths << minecraftDataPath;
QStringList javas;
while (!processpaths.isEmpty()) {
auto dirPath = processpaths.takeFirst();
QDir dir(dirPath);
if (!dir.exists())
continue;
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
auto binFound = false;
for (auto& entry : entries) {
if (entry.baseName() == "bin") {
javas.append(FS::PathCombine(entry.canonicalFilePath(), executable));
binFound = true;
break;
}
}
if (!binFound) {
for (auto& entry : entries) {
processpaths << entry.canonicalFilePath();
}
}
}
return javas;
}

View File

@ -26,6 +26,7 @@
QString stripVariableEntries(QString name, QString target, QString remove); QString stripVariableEntries(QString name, QString target, QString remove);
QProcessEnvironment CleanEnviroment(); QProcessEnvironment CleanEnviroment();
QStringList getMinecraftJavaBundle();
class JavaUtils : public QObject { class JavaUtils : public QObject {
Q_OBJECT Q_OBJECT

View File

@ -55,6 +55,6 @@ class Index : public QAbstractListModel, public BaseEntity {
QVector<VersionList::Ptr> m_lists; QVector<VersionList::Ptr> m_lists;
QHash<QString, VersionList::Ptr> m_uids; QHash<QString, VersionList::Ptr> m_uids;
void connectVersionList(const int row, const VersionList::Ptr& list); void connectVersionList(int row, const VersionList::Ptr& list);
}; };
} // namespace Meta } // namespace Meta

View File

@ -64,7 +64,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity {
public: // for usage by format parsers only public: // for usage by format parsers only
void setType(const QString& type); void setType(const QString& type);
void setTime(const qint64 time); void setTime(qint64 time);
void setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts); void setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts);
void setVolatile(bool volatile_); void setVolatile(bool volatile_);
void setRecommended(bool recommended); void setRecommended(bool recommended);

View File

@ -79,7 +79,7 @@ class VersionList : public BaseVersionList, public BaseEntity {
Version::Ptr m_recommended; Version::Ptr m_recommended;
void setupAddedVersion(const int row, const Version::Ptr& version); void setupAddedVersion(int row, const Version::Ptr& version);
}; };
} // namespace Meta } // namespace Meta
Q_DECLARE_METATYPE(Meta::VersionList::Ptr) Q_DECLARE_METATYPE(Meta::VersionList::Ptr)

View File

@ -565,43 +565,6 @@ QProcessEnvironment MinecraftInstance::createEnvironment()
for (auto it = variables.begin(); it != variables.end(); ++it) { for (auto it = variables.begin(); it != variables.end(); ++it) {
env.insert(it.key(), it.value()); env.insert(it.key(), it.value());
} }
return env;
}
QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
{
// prepare the process environment
QProcessEnvironment env = createEnvironment();
#ifdef Q_OS_LINUX
if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) {
auto preloadList = env.value("LD_PRELOAD").split(QLatin1String(":"));
auto libPaths = env.value("LD_LIBRARY_PATH").split(QLatin1String(":"));
auto mangoHudLibString = MangoHud::getLibraryString();
if (!mangoHudLibString.isEmpty()) {
QFileInfo mangoHudLib(mangoHudLibString);
// dlsym variant is only needed for OpenGL and not included in the vulkan layer
preloadList << "libMangoHud_dlsym.so" << mangoHudLib.fileName();
libPaths << mangoHudLib.absolutePath();
}
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
env.insert("LD_LIBRARY_PATH", libPaths.join(QLatin1String(":")));
env.insert("MANGOHUD", "1");
}
if (settings()->get("UseDiscreteGpu").toBool()) {
// Open Source Drivers
env.insert("DRI_PRIME", "1");
// Proprietary Nvidia Drivers
env.insert("__NV_PRIME_RENDER_OFFLOAD", "1");
env.insert("__VK_LAYER_NV_optimus", "NVIDIA_only");
env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia");
}
#endif
// custom env // custom env
auto insertEnv = [&env](QMap<QString, QVariant> envMap) { auto insertEnv = [&env](QMap<QString, QVariant> envMap) {
@ -618,7 +581,53 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
insertEnv(APPLICATION->settings()->get("Env").toMap()); insertEnv(APPLICATION->settings()->get("Env").toMap());
else else
insertEnv(settings()->get("Env").toMap()); insertEnv(settings()->get("Env").toMap());
return env;
}
QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
{
// prepare the process environment
QProcessEnvironment env = createEnvironment();
#ifdef Q_OS_LINUX
if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) {
QStringList preloadList;
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();
if (!mangoHudLibString.isEmpty()) {
QFileInfo mangoHudLib(mangoHudLibString);
QString libPath = mangoHudLib.absolutePath();
auto appendLib = [libPath, &preloadList](QString fileName) {
if (QFileInfo(FS::PathCombine(libPath, fileName)).exists())
preloadList << fileName;
};
// dlsym variant is only needed for OpenGL and not included in the vulkan layer
appendLib("libMangoHud_dlsym.so");
appendLib("libMangoHud_opengl.so");
appendLib(mangoHudLib.fileName());
libPaths << libPath;
}
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
env.insert("LD_LIBRARY_PATH", libPaths.join(QLatin1String(":")));
env.insert("MANGOHUD", "1");
}
if (settings()->get("UseDiscreteGpu").toBool()) {
// Open Source Drivers
env.insert("DRI_PRIME", "1");
// Proprietary Nvidia Drivers
env.insert("__NV_PRIME_RENDER_OFFLOAD", "1");
env.insert("__VK_LAYER_NV_optimus", "NVIDIA_only");
env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia");
}
#endif
return env; return env;
} }

View File

@ -9,7 +9,7 @@
class OneSixVersionFormat { class OneSixVersionFormat {
public: public:
// version files / profile patches // version files / profile patches
static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename, const bool requireOrder); static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename, bool requireOrder);
static QJsonDocument versionFileToJson(const VersionFilePtr& patch); static QJsonDocument versionFileToJson(const VersionFilePtr& patch);
// libraries // libraries

View File

@ -430,7 +430,7 @@ bool PackProfile::remove(const int index)
return true; return true;
} }
bool PackProfile::remove(const QString id) bool PackProfile::remove(const QString& id)
{ {
int i = 0; int i = 0;
for (auto patch : d->components) { for (auto patch : d->components) {

View File

@ -89,13 +89,13 @@ class PackProfile : public QAbstractListModel {
enum MoveDirection { MoveUp, MoveDown }; enum MoveDirection { MoveUp, MoveDown };
/// move component file # up or down the list /// move component file # up or down the list
void move(const int index, const MoveDirection direction); void move(int index, MoveDirection direction);
/// remove component file # - including files/records /// remove component file # - including files/records
bool remove(const int index); bool remove(int index);
/// remove component file by id - including files/records /// remove component file by id - including files/records
bool remove(const QString id); bool remove(const QString& id);
bool customize(int index); bool customize(int index);

View File

@ -140,7 +140,7 @@ VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool requireOrder)
return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder); return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder);
} }
bool saveJsonFile(const QJsonDocument doc, const QString& filename) bool saveJsonFile(const QJsonDocument& doc, const QString& filename)
{ {
auto data = doc.toJson(); auto data = doc.toJson();
QSaveFile jsonFile(filename); QSaveFile jsonFile(filename);

View File

@ -47,10 +47,10 @@ bool readOverrideOrders(QString path, PatchOrder& order);
bool writeOverrideOrders(QString path, const PatchOrder& order); bool writeOverrideOrders(QString path, const PatchOrder& order);
/// Parse a version file in JSON format /// Parse a version file in JSON format
VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool requireOrder); VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, bool requireOrder);
/// Save a JSON file (in any format) /// Save a JSON file (in any format)
bool saveJsonFile(const QJsonDocument doc, const QString& filename); bool saveJsonFile(const QJsonDocument& doc, const QString& filename);
/// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files. /// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files.
void removeLwjglFromPatch(VersionFilePtr patch); void removeLwjglFromPatch(VersionFilePtr patch);

View File

@ -283,9 +283,15 @@ QVariant AccountList::data(const QModelIndex& index, int role) const
return account->accountDisplayString(); return account->accountDisplayString();
case TypeColumn: { case TypeColumn: {
auto typeStr = account->typeString(); switch (account->accountType()) {
typeStr[0] = typeStr[0].toUpper(); case AccountType::MSA: {
return typeStr; return tr("MSA", "Account type");
}
case AccountType::Offline: {
return tr("Offline", "Account type");
}
}
return tr("Unknown", "Account type");
} }
case StatusColumn: { case StatusColumn: {

View File

@ -75,7 +75,7 @@ class AccountList : public QAbstractListModel {
virtual Qt::ItemFlags flags(const QModelIndex& index) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override; virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override;
void addAccount(const MinecraftAccountPtr account); void addAccount(MinecraftAccountPtr account);
void removeAccount(QModelIndex index); void removeAccount(QModelIndex index);
int findAccountByProfileId(const QString& profileId) const; int findAccountByProfileId(const QString& profileId) const;
MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const; MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const;

View File

@ -52,6 +52,7 @@
#include "flows/MSA.h" #include "flows/MSA.h"
#include "flows/Offline.h" #include "flows/Offline.h"
#include "minecraft/auth/AccountData.h"
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent)
{ {
@ -185,7 +186,7 @@ void MinecraftAccount::authFailed(QString reason)
// NOTE: this doesn't do much. There was an error of some sort. // NOTE: this doesn't do much. There was an error of some sort.
} break; } break;
case AccountTaskState::STATE_FAILED_HARD: { case AccountTaskState::STATE_FAILED_HARD: {
if (isMSA()) { if (accountType() == AccountType::MSA) {
data.msaToken.token = QString(); data.msaToken.token = QString();
data.msaToken.refresh_token = QString(); data.msaToken.refresh_token = QString();
data.msaToken.validity = Katabasis::Validity::None; data.msaToken.validity = Katabasis::Validity::None;

View File

@ -118,9 +118,7 @@ class MinecraftAccount : public QObject, public Usable {
bool isActive() const; bool isActive() const;
bool isMSA() const { return data.type == AccountType::MSA; } [[nodiscard]] AccountType accountType() const noexcept { return data.type; }
bool isOffline() const { return data.type == AccountType::Offline; }
bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; } bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; }

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>
@ -65,8 +65,8 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
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 };
} }
@ -131,6 +131,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>
@ -45,7 +46,7 @@ ResourceFolderModel::~ResourceFolderModel()
QCoreApplication::processEvents(); QCoreApplication::processEvents();
} }
bool ResourceFolderModel::startWatching(const QStringList paths) bool ResourceFolderModel::startWatching(const QStringList& paths)
{ {
if (m_is_watching) if (m_is_watching)
return false; return false;
@ -64,7 +65,7 @@ bool ResourceFolderModel::startWatching(const QStringList paths)
return m_is_watching; return m_is_watching;
} }
bool ResourceFolderModel::stopWatching(const QStringList paths) bool ResourceFolderModel::stopWatching(const QStringList& paths)
{ {
if (!m_is_watching) if (!m_is_watching)
return false; return false;
@ -460,9 +461,9 @@ bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] con
if (role == Qt::CheckStateRole) { if (role == Qt::CheckStateRole) {
if (m_instance != nullptr && m_instance->isRunning()) { if (m_instance != nullptr && m_instance->isRunning()) {
auto response = auto response =
CustomMessageBox::selectable(nullptr, "Confirm toggle", CustomMessageBox::selectable(nullptr, tr("Confirm toggle"),
"If you enable/disable this resource while the game is running it may crash your game.\n" tr("If you enable/disable this resource while the game is running it may crash your game.\n"
"Are you sure you want to do this?", "Are you sure you want to do this?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec(); ->exec();
@ -516,36 +517,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)
@ -570,7 +557,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

@ -39,14 +39,14 @@ class ResourceFolderModel : public QAbstractListModel {
* Returns whether starting to watch all the paths was successful. * Returns whether starting to watch all the paths was successful.
* If one or more fails, it returns false. * If one or more fails, it returns false.
*/ */
bool startWatching(const QStringList paths); bool startWatching(const QStringList& paths);
/** Stops watching the paths for changes. /** Stops watching the paths for changes.
* *
* Returns whether stopping to watch all the paths was successful. * Returns whether stopping to watch all the paths was successful.
* If one or more fails, it returns false. * If one or more fails, it returns false.
*/ */
bool stopWatching(const QStringList paths); bool stopWatching(const QStringList& paths);
/* Helper methods for subclasses, using a predetermined list of paths. */ /* Helper methods for subclasses, using a predetermined list of paths. */
virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); } virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); }
@ -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,7 @@ class ResourceFolderModel : public QAbstractListModel {
QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE };
QStringList m_column_names = { "Enable", "Name", "Last Modified" }; QStringList m_column_names = { "Enable", "Name", "Last Modified" };
QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") };
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 };
QList<bool> m_columnsHideable = { false, false, true }; QList<bool> m_columnsHideable = { false, false, true };
QDir m_dir; QDir m_dir;

View File

@ -52,8 +52,8 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc
m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
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 };
} }
@ -117,6 +117,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

@ -47,8 +47,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance*
m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive };
QHeaderView::ResizeToContents };
m_columnsHideable = { false, true, false, true }; m_columnsHideable = { false, true, false, true };
} }
@ -104,6 +103,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

@ -137,10 +137,11 @@ Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDepende
auto provider = pDep->pack->provider == m_flame_provider.name ? m_flame_provider : m_modrinth_provider; auto provider = pDep->pack->provider == m_flame_provider.name ? m_flame_provider : m_modrinth_provider;
auto responseInfo = std::make_shared<QByteArray>(); auto responseInfo = std::make_shared<QByteArray>();
auto info = provider.api->getProject(pDep->pack->addonId.toString(), responseInfo); auto info = provider.api->getProject(pDep->pack->addonId.toString(), responseInfo);
QObject::connect(info.get(), &NetJob::succeeded, [responseInfo, provider, pDep] { QObject::connect(info.get(), &NetJob::succeeded, [this, responseInfo, provider, pDep] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error); QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error);
if (parse_error.error != QJsonParseError::NoError) { if (parse_error.error != QJsonParseError::NoError) {
removePack(pDep->pack->addonId);
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
<< " reason: " << parse_error.errorString(); << " reason: " << parse_error.errorString();
qDebug() << *responseInfo; qDebug() << *responseInfo;
@ -151,6 +152,7 @@ Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDepende
: Json::requireObject(doc); : Json::requireObject(doc);
provider.mod->loadIndexedPack(*pDep->pack, obj); provider.mod->loadIndexedPack(*pDep->pack, obj);
} catch (const JSONValidationError& e) { } catch (const JSONValidationError& e) {
removePack(pDep->pack->addonId);
qDebug() << doc; qDebug() << doc;
qWarning() << "Error while reading mod info: " << e.cause(); qWarning() << "Error while reading mod info: " << e.cause();
} }
@ -211,11 +213,13 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
pDep->pack->versionsLoaded = true; pDep->pack->versionsLoaded = true;
} catch (const JSONValidationError& e) { } catch (const JSONValidationError& e) {
removePack(dep.addonId);
qDebug() << doc; qDebug() << doc;
qWarning() << "Error while reading mod version: " << e.cause(); qWarning() << "Error while reading mod version: " << e.cause();
return; return;
} }
if (level == 0) { if (level == 0) {
removePack(dep.addonId);
qWarning() << "Dependency cycle exceeded"; qWarning() << "Dependency cycle exceeded";
return; return;
} }
@ -238,7 +242,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
return tasks; return tasks;
} }
void GetModDependenciesTask::removePack(const QVariant addonId) void GetModDependenciesTask::removePack(const QVariant& addonId)
{ {
auto pred = [addonId](const std::shared_ptr<PackDependency>& v) { return v->pack->addonId == addonId; }; auto pred = [addonId](const std::shared_ptr<PackDependency>& v) { return v->pack->addonId == addonId; };
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)

View File

@ -65,13 +65,13 @@ class GetModDependenciesTask : public SequentialTask {
QHash<QString, QStringList> getRequiredBy(); QHash<QString, QStringList> getRequiredBy();
protected slots: protected slots:
Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider, int); Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, ModPlatform::ResourceProvider, int);
QList<ModPlatform::Dependency> getDependenciesForVersion(const ModPlatform::IndexedVersion&, QList<ModPlatform::Dependency> getDependenciesForVersion(const ModPlatform::IndexedVersion&,
const ModPlatform::ResourceProvider providerName); ModPlatform::ResourceProvider providerName);
void prepare(); void prepare();
Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep); Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep);
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider providerName); ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, ModPlatform::ResourceProvider providerName);
void removePack(const QVariant addonId); void removePack(const QVariant& addonId);
private: private:
QList<std::shared_ptr<PackDependency>> m_pack_dependencies; QList<std::shared_ptr<PackDependency>> m_pack_dependencies;

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

@ -68,7 +68,7 @@ class ModrinthPackExportTask : public Task {
void collectFiles(); void collectFiles();
void collectHashes(); void collectHashes();
void makeApiRequest(); void makeApiRequest();
void parseApiResponse(const std::shared_ptr<QByteArray> response); void parseApiResponse(std::shared_ptr<QByteArray> response);
void buildZip(); void buildZip();
QByteArray generateIndex(); QByteArray generateIndex();

View File

@ -33,6 +33,6 @@ class TechnicPackProcessor : public QObject {
const QString& instIcon, const QString& instIcon,
const QString& stagingPath, const QString& stagingPath,
const QString& minecraftVersion = QString(), const QString& minecraftVersion = QString(),
const bool isSolder = false); bool isSolder = false);
}; };
} // namespace Technic } // namespace Technic

View File

@ -1198,17 +1198,27 @@ void MainWindow::on_actionViewCentralModsFolder_triggered()
void MainWindow::on_actionViewIconThemeFolder_triggered() void MainWindow::on_actionViewIconThemeFolder_triggered()
{ {
DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path(), true);
} }
void MainWindow::on_actionViewWidgetThemeFolder_triggered() void MainWindow::on_actionViewWidgetThemeFolder_triggered()
{ {
DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path(), true);
} }
void MainWindow::on_actionViewCatPackFolder_triggered() void MainWindow::on_actionViewCatPackFolder_triggered()
{ {
DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path(), true);
}
void MainWindow::on_actionViewIconsFolder_triggered()
{
DesktopServices::openDirectory(APPLICATION->icons()->getDirectory(), true);
}
void MainWindow::on_actionViewLogsFolder_triggered()
{
DesktopServices::openDirectory("logs", true);
} }
void MainWindow::refreshInstances() void MainWindow::refreshInstances()

View File

@ -117,6 +117,8 @@ class MainWindow : public QMainWindow {
void on_actionViewIconThemeFolder_triggered(); void on_actionViewIconThemeFolder_triggered();
void on_actionViewWidgetThemeFolder_triggered(); void on_actionViewWidgetThemeFolder_triggered();
void on_actionViewCatPackFolder_triggered(); void on_actionViewCatPackFolder_triggered();
void on_actionViewIconsFolder_triggered();
void on_actionViewLogsFolder_triggered();
void on_actionViewSelectedInstFolder_triggered(); void on_actionViewSelectedInstFolder_triggered();

View File

@ -194,6 +194,9 @@
<addaction name="actionViewIconThemeFolder"/> <addaction name="actionViewIconThemeFolder"/>
<addaction name="actionViewWidgetThemeFolder"/> <addaction name="actionViewWidgetThemeFolder"/>
<addaction name="actionViewCatPackFolder"/> <addaction name="actionViewCatPackFolder"/>
<addaction name="actionViewIconsFolder"/>
<addaction name="separator"/>
<addaction name="actionViewLogsFolder"/>
</widget> </widget>
<widget class="QMenu" name="accountsMenu"> <widget class="QMenu" name="accountsMenu">
<property name="title"> <property name="title">
@ -545,10 +548,10 @@
<normaloff>.</normaloff>.</iconset> <normaloff>.</normaloff>.</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>View &amp;Instance Folder</string> <string>&amp;Instances</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Open the instance folder in a file browser.</string> <string>Open the instances folder in a file browser.</string>
</property> </property>
</action> </action>
<action name="actionViewLauncherRootFolder"> <action name="actionViewLauncherRootFolder">
@ -557,7 +560,7 @@
<normaloff>.</normaloff>.</iconset> <normaloff>.</normaloff>.</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>View Launcher &amp;Root Folder</string> <string>Launcher &amp;Root</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Open the launcher's root folder in a file browser.</string> <string>Open the launcher's root folder in a file browser.</string>
@ -569,12 +572,36 @@
<normaloff>.</normaloff>.</iconset> <normaloff>.</normaloff>.</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>View &amp;Central Mods Folder</string> <string>&amp;Central Mods</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Open the central mods folder in a file browser.</string> <string>Open the central mods folder in a file browser.</string>
</property> </property>
</action> </action>
<action name="actionViewIconsFolder">
<property name="icon">
<iconset theme="viewfolder">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Instance Icons</string>
</property>
<property name="toolTip">
<string>Open the instance icons folder in a file browser.</string>
</property>
</action>
<action name="actionViewLogsFolder">
<property name="icon">
<iconset theme="viewfolder">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Logs</string>
</property>
<property name="toolTip">
<string>Open the logs folder in a file browser.</string>
</property>
</action>
<action name="actionChangeTheme"> <action name="actionChangeTheme">
<property name="text"> <property name="text">
<string>Themes</string> <string>Themes</string>
@ -718,10 +745,10 @@
<normaloff>.</normaloff>.</iconset> <normaloff>.</normaloff>.</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>View &amp;Widget Themes Folder</string> <string>&amp;Widget Themes</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>View Widget Theme Folder</string> <string>Open the widget themes folder in a file browser.</string>
</property> </property>
</action> </action>
<action name="actionViewIconThemeFolder"> <action name="actionViewIconThemeFolder">
@ -730,18 +757,22 @@
<normaloff>.</normaloff>.</iconset> <normaloff>.</normaloff>.</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>View I&amp;con Theme Folder</string> <string>I&amp;con Theme</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>View Icon Theme Folder</string> <string>Open the icon theme folder in a file browser.</string>
</property> </property>
</action> </action>
<action name="actionViewCatPackFolder"> <action name="actionViewCatPackFolder">
<property name="icon"> <property name="icon">
<iconset theme="viewfolder"/> <iconset theme="viewfolder">
<normaloff>.</normaloff>.</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>View Cat Packs Folder</string> <string>Cat Packs</string>
</property>
<property name="toolTip">
<string>Open the cat packs folder in a file browser.</string>
</property> </property>
</action> </action>
</widget> </widget>

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

@ -16,10 +16,12 @@ class ConcurrentTask;
class ModUpdateDialog final : public ReviewMessageBox { class ModUpdateDialog final : public ReviewMessageBox {
Q_OBJECT Q_OBJECT
public: public:
explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, std::shared_ptr<ModFolderModel> mod_model, QList<Mod*>& search_for);
explicit ModUpdateDialog(QWidget* parent, explicit ModUpdateDialog(QWidget* parent,
BaseInstance* instance, BaseInstance* instance,
const std::shared_ptr<ModFolderModel> mod_model, std::shared_ptr<ModFolderModel> mod_model,
QList<Mod*>& search_for); QList<Mod*>& search_for,
bool includeDeps);
void checkCandidates(); void checkCandidates();
@ -61,4 +63,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

@ -50,7 +50,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
public: public:
using DownloadTaskPtr = shared_qobject_ptr<ResourceDownloadTask>; using DownloadTaskPtr = shared_qobject_ptr<ResourceDownloadTask>;
ResourceDownloadDialog(QWidget* parent, const std::shared_ptr<ResourceFolderModel> base_model); ResourceDownloadDialog(QWidget* parent, std::shared_ptr<ResourceFolderModel> base_model);
void initializeContainer(); void initializeContainer();
void connectButtons(); void connectButtons();

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

@ -65,7 +65,7 @@ class InstanceView : public QAbstractItemView {
/// get the model index at the specified visual point /// get the model index at the specified visual point
virtual QModelIndex indexAt(const QPoint& point) const override; virtual QModelIndex indexAt(const QPoint& point) const override;
QString groupNameAt(const QPoint& point); QString groupNameAt(const QPoint& point);
void setSelection(const QRect& rect, const QItemSelectionModel::SelectionFlags commands) override; void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags commands) override;
virtual int horizontalOffset() const override; virtual int horizontalOffset() const override;
virtual int verticalOffset() const override; virtual int verticalOffset() const override;

View File

@ -209,6 +209,9 @@
<property name="text"> <property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Note: you only need to set this to access private data. Read the &lt;a href=&quot;https://docs.modrinth.com/api-spec/#section/Authentication&quot;&gt;documentation&lt;/a&gt; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Note: you only need to set this to access private data. Read the &lt;a href=&quot;https://docs.modrinth.com/api-spec/#section/Authentication&quot;&gt;documentation&lt;/a&gt; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">

View File

@ -35,6 +35,7 @@
*/ */
#include "AccountListPage.h" #include "AccountListPage.h"
#include "minecraft/auth/AccountData.h"
#include "ui_AccountListPage.h" #include "ui_AccountListPage.h"
#include <QItemSelectionModel> #include <QItemSelectionModel>
@ -42,22 +43,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 +168,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();
@ -215,7 +217,7 @@ void AccountListPage::updateButtonStates()
QModelIndex selected = selection.first(); QModelIndex selected = selection.first();
MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value<MinecraftAccountPtr>(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value<MinecraftAccountPtr>();
accountIsReady = !account->isActive(); accountIsReady = !account->isActive();
accountIsOnline = !account->isOffline(); accountIsOnline = account->accountType() != AccountType::Offline;
} }
ui->actionRemove->setEnabled(accountIsReady); ui->actionRemove->setEnabled(accountIsReady);
ui->actionSetDefault->setEnabled(accountIsReady); ui->actionSetDefault->setEnabled(accountIsReady);
@ -230,6 +232,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

@ -84,7 +84,7 @@
<string notr="true"> MiB</string> <string notr="true"> MiB</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>128</number> <number>8</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>1048576</number> <number>1048576</number>
@ -106,7 +106,7 @@
<string notr="true"> MiB</string> <string notr="true"> MiB</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>128</number> <number>8</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>1048576</number> <number>1048576</number>
@ -128,7 +128,7 @@
<string notr="true"> MiB</string> <string notr="true"> MiB</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>64</number> <number>4</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>999999999</number> <number>999999999</number>

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()
@ -252,9 +254,9 @@ void ExternalResourcesPage::removeItem()
void ExternalResourcesPage::removeItems(const QItemSelection& selection) void ExternalResourcesPage::removeItems(const QItemSelection& selection)
{ {
if (m_instance != nullptr && m_instance->isRunning()) { if (m_instance != nullptr && m_instance->isRunning()) {
auto response = CustomMessageBox::selectable(this, "Confirm Delete", auto response = CustomMessageBox::selectable(this, tr("Confirm Delete"),
"If you remove this resource while the game is running it may crash your game.\n" tr("If you remove this resource while the game is running it may crash your game.\n"
"Are you sure you want to do this?", "Are you sure you want to do this?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec(); ->exec();
@ -273,9 +275,9 @@ void ExternalResourcesPage::enableItem()
void ExternalResourcesPage::disableItem() void ExternalResourcesPage::disableItem()
{ {
if (m_instance != nullptr && m_instance->isRunning()) { if (m_instance != nullptr && m_instance->isRunning()) {
auto response = CustomMessageBox::selectable(this, "Confirm disable", auto response = CustomMessageBox::selectable(this, tr("Confirm disable"),
"If you disable this resource while the game is running it may crash your game.\n" tr("If you disable this resource while the game is running it may crash your game.\n"
"Are you sure you want to do this?", "Are you sure you want to do this?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec(); ->exec();

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

@ -155,7 +155,7 @@
<string notr="true"> MiB</string> <string notr="true"> MiB</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>128</number> <number>8</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>1048576</number> <number>1048576</number>
@ -177,7 +177,7 @@
<string notr="true"> MiB</string> <string notr="true"> MiB</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>128</number> <number>8</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>1048576</number> <number>1048576</number>
@ -199,7 +199,7 @@
<string notr="true"> MiB</string> <string notr="true"> MiB</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>64</number> <number>4</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>999999999</number> <number>999999999</number>

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()); });
} }
} }
@ -150,9 +171,9 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unuse
void ModFolderPage::removeItems(const QItemSelection& selection) void ModFolderPage::removeItems(const QItemSelection& selection)
{ {
if (m_instance != nullptr && m_instance->isRunning()) { if (m_instance != nullptr && m_instance->isRunning()) {
auto response = CustomMessageBox::selectable(this, "Confirm Delete", auto response = CustomMessageBox::selectable(this, tr("Confirm Delete"),
"If you remove mods while the game is running it may crash your game.\n" tr("If you remove mods while the game is running it may crash your game.\n"
"Are you sure you want to do this?", "Are you sure you want to do this?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec(); ->exec();
@ -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,22 @@ 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;
}
if (m_instance != nullptr && m_instance->isRunning()) {
auto response =
CustomMessageBox::selectable(this, tr("Confirm Update"),
tr("If you update mods while the game is running may cause mod duplication and game crashes.\n"
"The old files may not be deleted as they are in use.\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()).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 +258,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

@ -65,7 +65,7 @@ class OtherLogsPage : public QWidget, public BasePage {
private slots: private slots:
void populateSelectLogBox(); void populateSelectLogBox();
void on_selectLogBox_currentIndexChanged(const int index); void on_selectLogBox_currentIndexChanged(int index);
void on_btnReload_clicked(); void on_btnReload_clicked();
void on_btnPaste_clicked(); void on_btnPaste_clicked();
void on_btnCopy_clicked(); void on_btnCopy_clicked();
@ -78,7 +78,7 @@ class OtherLogsPage : public QWidget, public BasePage {
void findPreviousActivated(); void findPreviousActivated();
private: private:
void setControlsEnabled(const bool enabled); void setControlsEnabled(bool enabled);
private: private:
Ui::OtherLogsPage* ui; Ui::OtherLogsPage* ui;

View File

@ -49,9 +49,7 @@ class ModPage : public ResourcePage {
[[nodiscard]] QMap<QString, QString> urlHandlers() const override; [[nodiscard]] QMap<QString, QString> urlHandlers() const override;
void addResourceToPage(ModPlatform::IndexedPack::Ptr, void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, std::shared_ptr<ResourceFolderModel>) override;
ModPlatform::IndexedVersion&,
const std::shared_ptr<ResourceFolderModel>) override;
virtual auto validateVersion(ModPlatform::IndexedVersion& ver, virtual auto validateVersion(ModPlatform::IndexedVersion& ver,
QString mineVer, QString mineVer,

View File

@ -88,7 +88,7 @@ class ResourceModel : public QAbstractListModel {
void addPack(ModPlatform::IndexedPack::Ptr pack, void addPack(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& version, ModPlatform::IndexedVersion& version,
const std::shared_ptr<ResourceFolderModel> packs, std::shared_ptr<ResourceFolderModel> packs,
bool is_indexed = false, bool is_indexed = false,
QString custom_target_folder = {}); QString custom_target_folder = {});
void removePack(const QString& rem); void removePack(const QString& rem);

View File

@ -78,7 +78,7 @@ class ResourcePage : public QWidget, public BasePage {
void addResourceToDialog(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&); void addResourceToDialog(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&);
void removeResourceFromDialog(const QString& pack_name); void removeResourceFromDialog(const QString& pack_name);
virtual void removeResourceFromPage(const QString& name); virtual void removeResourceFromPage(const QString& name);
virtual void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, const std::shared_ptr<ResourceFolderModel>); virtual void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, std::shared_ptr<ResourceFolderModel>);
QList<DownloadTaskPtr> selectedPacks() { return m_model->selectedPacks(); } QList<DownloadTaskPtr> selectedPacks() { return m_model->selectedPacks(); }
bool hasSelectedPacks() { return !(m_model->selectedPacks().isEmpty()); } bool hasSelectedPacks() { return !(m_model->selectedPacks().isEmpty()); }

View File

@ -38,9 +38,7 @@ class ShaderPackResourcePage : public ResourcePage {
[[nodiscard]] bool supportsFiltering() const override { return false; }; [[nodiscard]] bool supportsFiltering() const override { return false; };
void addResourceToPage(ModPlatform::IndexedPack::Ptr, void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, std::shared_ptr<ResourceFolderModel>) override;
ModPlatform::IndexedVersion&,
const std::shared_ptr<ResourceFolderModel>) override;
[[nodiscard]] QMap<QString, QString> urlHandlers() const override; [[nodiscard]] QMap<QString, QString> urlHandlers() const override;

View File

@ -38,7 +38,7 @@ class ListModel : public QAbstractListModel {
void fetchMore(const QModelIndex& parent) override; void fetchMore(const QModelIndex& parent) override;
void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback);
void searchWithTerm(const QString& term, const int sort); void searchWithTerm(const QString& term, int sort);
[[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); }
[[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; } [[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }

View File

@ -71,7 +71,7 @@ class ModpackListModel : public QAbstractListModel {
/* Ask the API for more information */ /* Ask the API for more information */
void fetchMore(const QModelIndex& parent) override; void fetchMore(const QModelIndex& parent) override;
void refresh(); void refresh();
void searchWithTerm(const QString& term, const int sort); void searchWithTerm(const QString& term, int sort);
[[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); }
[[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; } [[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }

View File

@ -80,7 +80,7 @@ void JavaSettingsWidget::setupUi()
m_minMemSpinBox = new QSpinBox(m_memoryGroupBox); m_minMemSpinBox = new QSpinBox(m_memoryGroupBox);
m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox")); m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox"));
m_minMemSpinBox->setSuffix(QStringLiteral(" MiB")); m_minMemSpinBox->setSuffix(QStringLiteral(" MiB"));
m_minMemSpinBox->setMinimum(128); m_minMemSpinBox->setMinimum(8);
m_minMemSpinBox->setMaximum(1048576); m_minMemSpinBox->setMaximum(1048576);
m_minMemSpinBox->setSingleStep(128); m_minMemSpinBox->setSingleStep(128);
m_labelMinMem->setBuddy(m_minMemSpinBox); m_labelMinMem->setBuddy(m_minMemSpinBox);
@ -93,7 +93,7 @@ void JavaSettingsWidget::setupUi()
m_maxMemSpinBox = new QSpinBox(m_memoryGroupBox); m_maxMemSpinBox = new QSpinBox(m_memoryGroupBox);
m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox")); m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox"));
m_maxMemSpinBox->setSuffix(QStringLiteral(" MiB")); m_maxMemSpinBox->setSuffix(QStringLiteral(" MiB"));
m_maxMemSpinBox->setMinimum(128); m_maxMemSpinBox->setMinimum(8);
m_maxMemSpinBox->setMaximum(1048576); m_maxMemSpinBox->setMaximum(1048576);
m_maxMemSpinBox->setSingleStep(128); m_maxMemSpinBox->setSingleStep(128);
m_labelMaxMem->setBuddy(m_maxMemSpinBox); m_labelMaxMem->setBuddy(m_maxMemSpinBox);
@ -112,7 +112,7 @@ void JavaSettingsWidget::setupUi()
m_permGenSpinBox = new QSpinBox(m_memoryGroupBox); m_permGenSpinBox = new QSpinBox(m_memoryGroupBox);
m_permGenSpinBox->setObjectName(QStringLiteral("permGenSpinBox")); m_permGenSpinBox->setObjectName(QStringLiteral("permGenSpinBox"));
m_permGenSpinBox->setSuffix(QStringLiteral(" MiB")); m_permGenSpinBox->setSuffix(QStringLiteral(" MiB"));
m_permGenSpinBox->setMinimum(64); m_permGenSpinBox->setMinimum(4);
m_permGenSpinBox->setMaximum(1048576); m_permGenSpinBox->setMaximum(1048576);
m_permGenSpinBox->setSingleStep(8); m_permGenSpinBox->setSingleStep(8);
m_gridLayout_2->addWidget(m_permGenSpinBox, 2, 1, 1, 1); m_gridLayout_2->addWidget(m_permGenSpinBox, 2, 1, 1, 1);

View File

@ -76,7 +76,7 @@ void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, con
translations->updateLanguage(key); translations->updateLanguage(key);
} }
void LanguageSelectionWidget::languageSettingChanged(const Setting&, const QVariant) void LanguageSelectionWidget::languageSettingChanged(const Setting&, const QVariant&)
{ {
auto translations = APPLICATION->translations(); auto translations = APPLICATION->translations();
auto index = translations->selectedIndex(); auto index = translations->selectedIndex();

View File

@ -34,7 +34,7 @@ class LanguageSelectionWidget : public QWidget {
protected slots: protected slots:
void languageRowChanged(const QModelIndex& current, const QModelIndex& previous); void languageRowChanged(const QModelIndex& current, const QModelIndex& previous);
void languageSettingChanged(const Setting&, const QVariant); void languageSettingChanged(const Setting&, const QVariant&);
private: private:
QVBoxLayout* verticalLayout = nullptr; QVBoxLayout* verticalLayout = nullptr;

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);
} }
} }