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:
- modernize-use-using
- readability-avoid-const-params-in-decls
SystemHeaders: false

View File

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

View File

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

View File

@ -18,11 +18,18 @@
</a>
- 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
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**.

View File

@ -68,6 +68,8 @@ function(
/w14906 # string literal cast to 'LPWSTR'
/w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
/permissive- # standards conformance mode for MSVC compiler.
/we4062 # forbid omitting a possible value of an enum in a switch statement
)
endif()
@ -93,6 +95,8 @@ function(
# 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
-Wno-gnu-zero-variadic-macro-arguments
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
)
endif()
@ -104,6 +108,8 @@ function(
-Wduplicated-branches # warn if if / else branches have duplicated code
-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
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
)
endif()
@ -128,6 +134,8 @@ function(
-Woverloaded-virtual
-Wuseless-cast
-Wextra-semi
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
)
target_compile_options(

30
flake.lock generated
View File

@ -21,11 +21,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1696343447,
"narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=",
"lastModified": 1698882062,
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4",
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
"type": "github"
},
"original": {
@ -76,11 +76,11 @@
"libnbtplusplus": {
"flake": false,
"locked": {
"lastModified": 1690036783,
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=",
"lastModified": 1699286814,
"narHash": "sha256-yy0q+bky80LtK1GWzz7qpM+aAGrOqLuewbid8WT1ilk=",
"owner": "PrismLauncher",
"repo": "libnbtplusplus",
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f",
"rev": "23b955121b8217c1c348a9ed2483167a6f3ff4ad",
"type": "github"
},
"original": {
@ -106,11 +106,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1698336494,
"narHash": "sha256-sO72WDBKyijYD1GcKPlGsycKbMBiTJMBCnmOxLAs880=",
"lastModified": 1700108881,
"narHash": "sha256-+Lqybl8kj0+nD/IlAWPPG/RDTa47gff9nbei0u7BntE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "808c0d8c53c7ae50f82aca8e7df263225cf235bf",
"rev": "7414e9ee0b3e9903c24d3379f577a417f0aae5f1",
"type": "github"
},
"original": {
@ -123,11 +123,11 @@
"nixpkgs-lib": {
"locked": {
"dir": "lib",
"lastModified": 1696019113,
"narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=",
"lastModified": 1698611440,
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a",
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
"type": "github"
},
"original": {
@ -153,11 +153,11 @@
]
},
"locked": {
"lastModified": 1698227354,
"narHash": "sha256-Fi5H9jbaQLmLw9qBi/mkR33CoFjNbobo5xWdX4tKz1Q=",
"lastModified": 1700064067,
"narHash": "sha256-1ZWNDzhu8UlVCK7+DUN9dVQfiHX1bv6OQP9VxstY/gs=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "bd38df3d508dfcdff52cd243d297f218ed2257bf",
"rev": "e558068cba67b23b4fbc5537173dbb43748a17e8",
"type": "github"
},
"original": {

View File

@ -1,6 +1,6 @@
id: org.prismlauncher.PrismLauncher
runtime: org.kde.Platform
runtime-version: "5.15-22.08"
runtime-version: "5.15-23.08"
sdk: org.kde.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17
@ -113,6 +113,9 @@ modules:
version-query: .tag_name
url-query: .tarball_url
timestamp-query: .published_at
# from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch
- type: patch
path: patches/gamemode.patch
cleanup:
- /include
- /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:
exclude: []
exclude:
- "*.x86_64-darwin.*"
include:
- "checks.x86_64-linux.*"
- "devShells.*.*"

View File

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

View File

@ -18,7 +18,7 @@
class DataMigrationTask : public Task {
Q_OBJECT
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;
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 (!isSandbox()) {
return IndirectOpen(f);
} else {
return f();
}
#else
return f();
#endif
return f();
}
bool openFile(const QString& path)

View File

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

View File

@ -59,7 +59,7 @@
#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)
{}

View File

@ -54,7 +54,7 @@ class FileResolvingTask;
class InstanceImportTask : public InstanceTask {
Q_OBJECT
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;
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;
removed = true;
auto idx = getInstIndex(instance.get());
if (idx > 0)
if (idx >= 0)
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;
modified = true;
auto idx = getInstIndex(instance.get());
if (idx > 0)
if (idx >= 0)
emit dataChanged(index(idx), index(idx), { GroupRole });
}
}
@ -823,6 +823,9 @@ void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting,
}
m_instDir = newInstDir;
m_groupsLoaded = false;
beginRemoveRows(QModelIndex(), 0, count());
m_instances.erase(m_instances.begin(), m_instances.end());
endRemoveRows();
emit instancesChanged();
}
}
@ -947,9 +950,12 @@ QString InstanceList::getStagedInstancePath()
bool InstanceList::commitStagedInstance(const QString& path,
InstanceName const& instanceName,
const QString& groupName,
QString groupName,
InstanceTask const& commiting)
{
if (groupName.isEmpty() && !groupName.isNull())
groupName = QString();
QDir dir;
QString instID;
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
* - 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.

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.
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
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?"),
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
->exec();
@ -169,7 +169,7 @@ void LaunchController::login()
m_accountToUse->fillSession(m_session);
// Launch immediately in true offline mode
if (m_accountToUse->isOffline()) {
if (m_accountToUse->accountType() == AccountType::Offline) {
launchInstance();
return;
}

View File

@ -50,7 +50,7 @@
namespace MMCZip {
// 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());
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
*/
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

View File

@ -5,6 +5,7 @@
#include <QPixmapCache>
#include <QThread>
#include <QTime>
#include <limits>
#define GET_TYPE() \
Qt::ConnectionType type; \
@ -100,10 +101,14 @@ class PixmapCache final : public QObject {
*/
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();
if (!m_last_cache_miss_by_eviciton.isNull()) {
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;
} else {
m_consecutive_fast_evicitons = 0;
@ -111,11 +116,17 @@ class PixmapCache final : public QObject {
}
m_last_cache_miss_by_eviciton = now;
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
// double the cache size
auto newSize = _cacheLimit() * 2;
qDebug() << m_consecutive_fast_evicitons << "pixmap cache misses by eviction happened too fast, doubling cache size to"
<< newSize;
_setCacheLimit(newSize);
// increase the cache size
uint newSize = _cacheLimit() + step;
if (newSize >= maxInt) { // increase it until you overflow :D
newSize = maxInt;
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;
return true;
}

View File

@ -13,7 +13,7 @@ class RecursiveFileSystemWatcher : public QObject {
QDir rootDir() const { return m_root; }
// 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; }
void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; }

View File

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

View File

@ -28,7 +28,7 @@ class VersionProxyModel : public QAbstractProxyModel {
const FilterMap& filters() 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 clearFilters();
QModelIndex getRecommended() const;

View File

@ -67,7 +67,7 @@ class IconList : public QAbstractListModel {
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
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;
bool deleteIcon(const QString& key);
bool trashIcon(const QString& key);

View File

@ -34,6 +34,7 @@
*/
#include <QDir>
#include <QFileInfo>
#include <QString>
#include <QStringList>
@ -335,6 +336,7 @@ QList<QString> JavaUtils::FindJavaPaths()
}
}
candidates.append(getMinecraftJavaBundle());
candidates = addJavasFromEnv(candidates);
candidates.removeDuplicates();
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/Commands/java");
}
javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas);
javas.removeDuplicates();
return javas;
@ -411,6 +414,7 @@ QList<QString> JavaUtils::FindJavaPaths()
// javas downloaded by sdkman
scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java"));
javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas);
javas.removeDuplicates();
return javas;
@ -423,6 +427,7 @@ QList<QString> JavaUtils::FindJavaPaths()
QList<QString> javas;
javas.append(this->GetDefaultJava()->path);
javas.append(getMinecraftJavaBundle());
return addJavasFromEnv(javas);
}
#endif
@ -431,3 +436,50 @@ QString JavaUtils::getJavaCheckPath()
{
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);
QProcessEnvironment CleanEnviroment();
QStringList getMinecraftJavaBundle();
class JavaUtils : public QObject {
Q_OBJECT

View File

@ -55,6 +55,6 @@ class Index : public QAbstractListModel, public BaseEntity {
QVector<VersionList::Ptr> m_lists;
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

View File

@ -64,7 +64,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity {
public: // for usage by format parsers only
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 setVolatile(bool volatile_);
void setRecommended(bool recommended);

View File

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

View File

@ -565,43 +565,6 @@ QProcessEnvironment MinecraftInstance::createEnvironment()
for (auto it = variables.begin(); it != variables.end(); ++it) {
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
auto insertEnv = [&env](QMap<QString, QVariant> envMap) {
@ -618,7 +581,53 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
insertEnv(APPLICATION->settings()->get("Env").toMap());
else
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;
}

View File

@ -9,7 +9,7 @@
class OneSixVersionFormat {
public:
// 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);
// libraries

View File

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

View File

@ -89,13 +89,13 @@ class PackProfile : public QAbstractListModel {
enum MoveDirection { MoveUp, MoveDown };
/// 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
bool remove(const int index);
bool remove(int index);
/// remove component file by id - including files/records
bool remove(const QString id);
bool remove(const QString& id);
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);
}
bool saveJsonFile(const QJsonDocument doc, const QString& filename)
bool saveJsonFile(const QJsonDocument& doc, const QString& filename)
{
auto data = doc.toJson();
QSaveFile jsonFile(filename);

View File

@ -47,10 +47,10 @@ bool readOverrideOrders(QString path, PatchOrder& order);
bool writeOverrideOrders(QString path, const PatchOrder& order);
/// 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)
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.
void removeLwjglFromPatch(VersionFilePtr patch);

View File

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

View File

@ -75,7 +75,7 @@ class AccountList : public QAbstractListModel {
virtual Qt::ItemFlags flags(const QModelIndex& index) const 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);
int findAccountByProfileId(const QString& profileId) const;
MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const;

View File

@ -52,6 +52,7 @@
#include "flows/MSA.h"
#include "flows/Offline.h"
#include "minecraft/auth/AccountData.h"
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.
} break;
case AccountTaskState::STATE_FAILED_HARD: {
if (isMSA()) {
if (accountType() == AccountType::MSA) {
data.msaToken.token = QString();
data.msaToken.refresh_token = QString();
data.msaToken.validity = Katabasis::Validity::None;

View File

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

View File

@ -37,9 +37,9 @@
#include "ModFolderModel.h"
#include <FileSystem.h>
#include <qheaderview.h>
#include <QDebug>
#include <QFileSystemWatcher>
#include <QHeaderView>
#include <QIcon>
#include <QMimeData>
#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_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_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch,
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents };
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch,
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
m_columnsHideable = { false, true, false, true, true, true };
}
@ -131,6 +131,11 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
}
return {};
}
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole:
switch (column) {
case ActiveColumn:

View File

@ -4,6 +4,7 @@
#include <QCoreApplication>
#include <QDebug>
#include <QFileInfo>
#include <QHeaderView>
#include <QIcon>
#include <QMenu>
#include <QMimeData>
@ -45,7 +46,7 @@ ResourceFolderModel::~ResourceFolderModel()
QCoreApplication::processEvents();
}
bool ResourceFolderModel::startWatching(const QStringList paths)
bool ResourceFolderModel::startWatching(const QStringList& paths)
{
if (m_is_watching)
return false;
@ -64,7 +65,7 @@ bool ResourceFolderModel::startWatching(const QStringList paths)
return m_is_watching;
}
bool ResourceFolderModel::stopWatching(const QStringList paths)
bool ResourceFolderModel::stopWatching(const QStringList& paths)
{
if (!m_is_watching)
return false;
@ -460,9 +461,9 @@ bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] con
if (role == Qt::CheckStateRole) {
if (m_instance != nullptr && m_instance->isRunning()) {
auto response =
CustomMessageBox::selectable(nullptr, "Confirm toggle",
"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?",
CustomMessageBox::selectable(nullptr, tr("Confirm toggle"),
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?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
@ -516,36 +517,22 @@ void ResourceFolderModel::setupHeaderAction(QAction* act, int 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)
: m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList();
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);
setting->set(tree->header()->saveState());
}
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)
: m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList();
auto col_names = columnNames(false);
for (auto col_name : hiddenColumns) {
auto index = col_names.indexOf(col_name);
if (index >= 0)
tree->setColumnHidden(index, true);
}
tree->header()->restoreState(setting->get().toByteArray());
}
QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
@ -570,7 +557,7 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents)
tree->resizeColumnToContents(c);
}
saveHiddenColumn(col, !toggled);
saveColumns(tree);
});
menu->addAction(act);

View File

@ -39,14 +39,14 @@ class ResourceFolderModel : public QAbstractListModel {
* Returns whether starting to watch all the paths was successful.
* If one or more fails, it returns false.
*/
bool startWatching(const QStringList paths);
bool startWatching(const QStringList& paths);
/** Stops watching the paths for changes.
*
* Returns whether stopping to watch all the paths was successful.
* 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. */
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;
void setupHeaderAction(QAction* act, int column);
void saveHiddenColumn(int column, bool hidden);
void loadHiddenColumns(QTreeView* tree);
void saveColumns(QTreeView* tree);
void loadColumns(QTreeView* tree);
QMenu* createHeaderContextMenu(QTreeView* tree);
/** 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 };
QStringList m_column_names = { "Enable", "Name", "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,
QHeaderView::ResizeToContents };
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive };
QList<bool> m_columnsHideable = { false, false, true };
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_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_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents,
QHeaderView::ResizeToContents };
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
QHeaderView::Interactive };
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();
}
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole:
switch (column) {
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_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_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch,
QHeaderView::ResizeToContents };
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive };
m_columnsHideable = { false, true, false, true };
}
@ -104,6 +103,11 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
}
return {};
}
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole:
if (column == ActiveColumn) {
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 responseInfo = std::make_shared<QByteArray>();
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{};
QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
removePack(pDep->pack->addonId);
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
<< " reason: " << parse_error.errorString();
qDebug() << *responseInfo;
@ -151,6 +152,7 @@ Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDepende
: Json::requireObject(doc);
provider.mod->loadIndexedPack(*pDep->pack, obj);
} catch (const JSONValidationError& e) {
removePack(pDep->pack->addonId);
qDebug() << doc;
qWarning() << "Error while reading mod info: " << e.cause();
}
@ -211,11 +213,13 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
pDep->pack->versionsLoaded = true;
} catch (const JSONValidationError& e) {
removePack(dep.addonId);
qDebug() << doc;
qWarning() << "Error while reading mod version: " << e.cause();
return;
}
if (level == 0) {
removePack(dep.addonId);
qWarning() << "Dependency cycle exceeded";
return;
}
@ -238,7 +242,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
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; };
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)

View File

@ -65,13 +65,13 @@ class GetModDependenciesTask : public SequentialTask {
QHash<QString, QStringList> getRequiredBy();
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&,
const ModPlatform::ResourceProvider providerName);
ModPlatform::ResourceProvider providerName);
void prepare();
Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep);
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider providerName);
void removePack(const QVariant addonId);
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, ModPlatform::ResourceProvider providerName);
void removePack(const QVariant& addonId);
private:
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;
}
// 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
{
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;
}
// 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
{
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 collectHashes();
void makeApiRequest();
void parseApiResponse(const std::shared_ptr<QByteArray> response);
void parseApiResponse(std::shared_ptr<QByteArray> response);
void buildZip();
QByteArray generateIndex();

View File

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

View File

@ -1198,17 +1198,27 @@ void MainWindow::on_actionViewCentralModsFolder_triggered()
void MainWindow::on_actionViewIconThemeFolder_triggered()
{
DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path());
DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path(), true);
}
void MainWindow::on_actionViewWidgetThemeFolder_triggered()
{
DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path());
DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path(), true);
}
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()

View File

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

View File

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

View File

@ -101,7 +101,7 @@ QString getCreditsHtml()
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>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>gon sawa %1</p>\n").arg(getGitHub("gonsawa"));
stream << QString("<p>Pankakes</p>\n");

View File

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

View File

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

View File

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

View File

@ -105,8 +105,16 @@ void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString&
QString urlString = uri.toString();
QString linkString = QString("<a href=\"%1\">%2</a>").arg(urlString, urlString);
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));
if (urlString == "https://www.microsoft.com/link" && !code.isEmpty()) {
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);
connect(ui->actionButton, &QPushButton::clicked, [=]() {
DesktopServices::openUrl(uri);

View File

@ -39,7 +39,8 @@ static std::optional<ModPlatform::ModLoaderTypes> mcLoaders(BaseInstance* inst)
ModUpdateDialog::ModUpdateDialog(QWidget* parent,
BaseInstance* instance,
const std::shared_ptr<ModFolderModel> mods,
QList<Mod*>& search_for)
QList<Mod*>& search_for,
bool includeDeps)
: ReviewMessageBox(parent, tr("Confirm mods to update"), "")
, m_parent(parent)
, m_mod_model(mods)
@ -47,6 +48,7 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent,
, m_second_try_metadata(
new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()))
, m_instance(instance)
, m_include_deps(includeDeps)
{
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);
connect(depTask.get(), &Task::failed, this,

View File

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

View File

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

View File

@ -50,7 +50,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
public:
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 connectButtons();

View File

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

View File

@ -65,7 +65,7 @@ class InstanceView : public QAbstractItemView {
/// get the model index at the specified visual point
virtual QModelIndex indexAt(const QPoint& point) const override;
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 verticalOffset() const override;

View File

@ -209,6 +209,9 @@
<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>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">

View File

@ -35,6 +35,7 @@
*/
#include "AccountListPage.h"
#include "minecraft/auth/AccountData.h"
#include "ui_AccountListPage.h"
#include <QItemSelectionModel>
@ -42,22 +43,17 @@
#include <QDebug>
#include "net/NetJob.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/MSALoginDialog.h"
#include "ui/dialogs/OfflineLoginDialog.h"
#include "ui/dialogs/ProgressDialog.h"
#include "ui/dialogs/SkinUploadDialog.h"
#include "minecraft/auth/AccountTask.h"
#include "minecraft/services/SkinDelete.h"
#include "tasks/Task.h"
#include "Application.h"
#include "BuildConfig.h"
AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new Ui::AccountListPage)
{
ui->setupUi(this);
@ -172,6 +168,12 @@ void AccountListPage::on_actionAddOffline_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();
if (selection.size() > 0) {
QModelIndex selected = selection.first();
@ -215,7 +217,7 @@ void AccountListPage::updateButtonStates()
QModelIndex selected = selection.first();
MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value<MinecraftAccountPtr>();
accountIsReady = !account->isActive();
accountIsOnline = !account->isOffline();
accountIsOnline = account->accountType() != AccountType::Offline;
}
ui->actionRemove->setEnabled(accountIsReady);
ui->actionSetDefault->setEnabled(accountIsReady);
@ -230,6 +232,7 @@ void AccountListPage::updateButtonStates()
ui->actionNoDefault->setEnabled(true);
ui->actionNoDefault->setChecked(false);
}
ui->listView->resizeColumnToContents(3);
}
void AccountListPage::on_actionUploadSkin_triggered()

View File

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

View File

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

View File

@ -186,6 +186,16 @@
</property>
</widget>
</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>
</widget>
</item>

View File

@ -42,6 +42,7 @@
#include "minecraft/mod/ResourceFolderModel.h"
#include "ui/GuiUtil.h"
#include <QHeaderView>
#include <QKeyEvent>
#include <QMenu>
#include <algorithm>
@ -95,7 +96,8 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
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()
@ -252,9 +254,9 @@ void ExternalResourcesPage::removeItem()
void ExternalResourcesPage::removeItems(const QItemSelection& selection)
{
if (m_instance != nullptr && m_instance->isRunning()) {
auto response = CustomMessageBox::selectable(this, "Confirm Delete",
"If you remove this resource while the game is running it may crash your game.\n"
"Are you sure you want to do this?",
auto response = CustomMessageBox::selectable(this, tr("Confirm Delete"),
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?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
@ -273,9 +275,9 @@ void ExternalResourcesPage::enableItem()
void ExternalResourcesPage::disableItem()
{
if (m_instance != nullptr && m_instance->isRunning()) {
auto response = CustomMessageBox::selectable(this, "Confirm disable",
"If you disable this resource while the game is running it may crash your game.\n"
"Are you sure you want to do this?",
auto response = CustomMessageBox::selectable(this, tr("Confirm disable"),
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?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();

View File

@ -70,6 +70,9 @@
</layout>
</widget>
<widget class="WideBar" name="actionsToolbar">
<property name="useDefaultAction" stdset="0">
<bool>true</bool>
</property>
<property name="windowTitle">
<string>Actions</string>
</property>
@ -146,17 +149,6 @@
<string>Download a new resource</string>
</property>
</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">
<property name="enabled">
<bool>false</bool>
@ -168,15 +160,15 @@
<string>Go to mods home page</string>
</property>
</action>
<action name="actionRemoveItemMetadata">
<action name="actionUpdateItem">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="text">
<string>Remove metadata</string>
<string>Check for &amp;Updates</string>
</property>
<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>
</action>
</widget>

View File

@ -155,7 +155,7 @@
<string notr="true"> MiB</string>
</property>
<property name="minimum">
<number>128</number>
<number>8</number>
</property>
<property name="maximum">
<number>1048576</number>
@ -177,7 +177,7 @@
<string notr="true"> MiB</string>
</property>
<property name="minimum">
<number>128</number>
<number>8</number>
</property>
<property name="maximum">
<number>1048576</number>
@ -199,7 +199,7 @@
<string notr="true"> MiB</string>
</property>
<property name="minimum">
<number>64</number>
<number>4</number>
</property>
<property name="maximum">
<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);
// 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->actionsToolbar->insertActionAfter(ui->actionAddItem, ui->actionUpdateItem);
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->actionsToolbar->addAction(ui->actionVisitItemPage);
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(); };
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this, check_allow_update] {
ui->actionUpdateItem->setEnabled(check_allow_update());
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
[this, check_allow_update, actionRemoveItemMetadata] {
ui->actionUpdateItem->setEnabled(check_allow_update());
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
auto mods_list = m_model->selectedMods(selection);
auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(),
[](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; });
if (selected <= 1) {
ui->actionVisitItemPage->setText(tr("Visit mod's page"));
ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page"));
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
auto mods_list = m_model->selectedMods(selection);
auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(),
[](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; });
if (selected <= 1) {
ui->actionVisitItemPage->setText(tr("Visit mod's page"));
ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page"));
ui->actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata"));
} else {
ui->actionVisitItemPage->setText(tr("Visit mods' pages"));
ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods"));
} else {
ui->actionVisitItemPage->setText(tr("Visit mods' pages"));
ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods"));
}
ui->actionVisitItemPage->setEnabled(selected != 0);
actionRemoveItemMetadata->setEnabled(selected != 0);
});
ui->actionRemoveItemMetadata->setToolTip(tr("Remove mods' metadata"));
}
ui->actionVisitItemPage->setEnabled(selected != 0);
ui->actionRemoveItemMetadata->setEnabled(selected != 0);
});
auto updateButtons = [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); };
connect(mods.get(), &ModFolderModel::rowsInserted, this, updateButtons);
connect(mods.get(), &ModFolderModel::rowsInserted, this,
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
connect(mods.get(), &ModFolderModel::rowsRemoved, this, updateButtons);
connect(mods.get(), &ModFolderModel::rowsRemoved, this,
[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()); });
connect(mods.get(), &ModFolderModel::updateFinished, this, updateButtons);
}
}
@ -150,9 +171,9 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unuse
void ModFolderPage::removeItems(const QItemSelection& selection)
{
if (m_instance != nullptr && m_instance->isRunning()) {
auto response = CustomMessageBox::selectable(this, "Confirm Delete",
"If you remove mods while the game is running it may crash your game.\n"
"Are you sure you want to do this?",
auto response = CustomMessageBox::selectable(this, tr("Confirm Delete"),
tr("If you remove mods while the game is running it may crash your game.\n"
"Are you sure you want to do this?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
@ -204,7 +225,7 @@ void ModFolderPage::installMods()
}
}
void ModFolderPage::updateMods()
void ModFolderPage::updateMods(bool includeDeps)
{
if (m_instance->typeName() != "Minecraft")
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!"));
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 mods_list = m_model->selectedMods(selection);
@ -221,7 +258,7 @@ void ModFolderPage::updateMods()
if (use_all)
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();
if (update_dialog.aborted()) {

View File

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

View File

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

View File

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

View File

@ -88,7 +88,7 @@ class ResourceModel : public QAbstractListModel {
void addPack(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& version,
const std::shared_ptr<ResourceFolderModel> packs,
std::shared_ptr<ResourceFolderModel> packs,
bool is_indexed = false,
QString custom_target_folder = {});
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 removeResourceFromDialog(const QString& pack_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(); }
bool hasSelectedPacks() { return !(m_model->selectedPacks().isEmpty()); }

View File

@ -38,9 +38,7 @@ class ShaderPackResourcePage : public ResourcePage {
[[nodiscard]] bool supportsFiltering() const override { return false; };
void addResourceToPage(ModPlatform::IndexedPack::Ptr,
ModPlatform::IndexedVersion&,
const std::shared_ptr<ResourceFolderModel>) override;
void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, std::shared_ptr<ResourceFolderModel>) 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 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]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }

View File

@ -71,7 +71,7 @@ class ModpackListModel : public QAbstractListModel {
/* Ask the API for more information */
void fetchMore(const QModelIndex& parent) override;
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]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }

View File

@ -80,7 +80,7 @@ void JavaSettingsWidget::setupUi()
m_minMemSpinBox = new QSpinBox(m_memoryGroupBox);
m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox"));
m_minMemSpinBox->setSuffix(QStringLiteral(" MiB"));
m_minMemSpinBox->setMinimum(128);
m_minMemSpinBox->setMinimum(8);
m_minMemSpinBox->setMaximum(1048576);
m_minMemSpinBox->setSingleStep(128);
m_labelMinMem->setBuddy(m_minMemSpinBox);
@ -93,7 +93,7 @@ void JavaSettingsWidget::setupUi()
m_maxMemSpinBox = new QSpinBox(m_memoryGroupBox);
m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox"));
m_maxMemSpinBox->setSuffix(QStringLiteral(" MiB"));
m_maxMemSpinBox->setMinimum(128);
m_maxMemSpinBox->setMinimum(8);
m_maxMemSpinBox->setMaximum(1048576);
m_maxMemSpinBox->setSingleStep(128);
m_labelMaxMem->setBuddy(m_maxMemSpinBox);
@ -112,7 +112,7 @@ void JavaSettingsWidget::setupUi()
m_permGenSpinBox = new QSpinBox(m_memoryGroupBox);
m_permGenSpinBox->setObjectName(QStringLiteral("permGenSpinBox"));
m_permGenSpinBox->setSuffix(QStringLiteral(" MiB"));
m_permGenSpinBox->setMinimum(64);
m_permGenSpinBox->setMinimum(4);
m_permGenSpinBox->setMaximum(1048576);
m_permGenSpinBox->setSingleStep(8);
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);
}
void LanguageSelectionWidget::languageSettingChanged(const Setting&, const QVariant)
void LanguageSelectionWidget::languageSettingChanged(const Setting&, const QVariant&)
{
auto translations = APPLICATION->translations();
auto index = translations->selectedIndex();

View File

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

View File

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