Compare commits

..

19 commits

Author SHA1 Message Date
Alexandru Ionut Tripon a71b8d8fe3
[Backport release-11.x] enable modpack changelog for modrinth page (#5360) 2026-04-11 09:36:29 +03:00
Trial97 6d38b34c00 enable modpack changelog for modrinth page
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit f3ff0a730a)
2026-04-11 05:37:04 +00:00
Alexandru Ionut Tripon 85f19da603
[Backport release-11.x] fix pack upgrade (#5356) 2026-04-10 20:32:09 +03:00
Trial97 239be1ec43 fix pack upgrade
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit b7344af313)
2026-04-10 17:31:48 +00:00
Alexandru Ionut Tripon 34349a6810
[Backport release-11.x] Allow disabling low RAM warning (#5350) 2026-04-10 15:23:20 +03:00
Octol1ttle fb7e4da4e6 Change LowMemWarning default to always enabled
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 4b3aedd5d0)
2026-04-10 09:31:16 +00:00
Octol1ttle f766cdd847 change(EnsureAvailableMemory): add lenience
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 658a1391f8)
2026-04-10 09:31:16 +00:00
Octol1ttle 789c656463 feat: allow disabling low RAM warning
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit c044ed36af)
2026-04-10 09:31:16 +00:00
Alexandru Ionut Tripon 2d01dfa4f2
[Backport release-11.x] fix McClient (#5344) 2026-04-10 00:50:18 +03:00
Alexandru Ionut Tripon 9231fe8592
[Backport release-11.x] Don't count JAR mods when checking offline libraries (#5343) 2026-04-10 00:49:58 +03:00
Alexandru Ionut Tripon 25fee30f95
[Backport release-11.x] CI/Nix: Bump macOS (#5342) 2026-04-10 00:49:42 +03:00
Alexandru Ionut Tripon ec6a173608
[Backport release-11.x] fix(PrintInstanceInfo): add break before OS info (#5341) 2026-04-10 00:49:24 +03:00
Alexandru Ionut Tripon b7d70dc1c1
chore: bump to 11.0.1 (#5340) 2026-04-10 00:49:09 +03:00
Octol1ttle 09f0467e81 refactor: McClient
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 91616ae9b6)
2026-04-09 21:06:40 +00:00
Octol1ttle fe02ad8524 fix(McClient): do not use unsigned type for response length
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 2fe0569bd6)
2026-04-09 21:06:40 +00:00
Octol1ttle f66796e806 fix: don't count JAR mods when checking offline libraries
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit ec4484282c)
2026-04-09 20:56:32 +00:00
Octol1ttle 4cd8c343fe fix(CI/nix): bump macOS
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 724c9a4a2c)
2026-04-09 20:47:44 +00:00
Octol1ttle 960e1bac87 fix(PrintInstanceInfo): add break before OS info
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 4cf8cf7d18)
2026-04-09 20:46:36 +00:00
Trial97 838e7fb8d2 chore: bump to 11.0.1
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2026-04-09 23:33:42 +03:00
56 changed files with 255 additions and 431 deletions

View file

@ -55,7 +55,7 @@ runs:
# TODO(@getchoo): Get this working on MSYS2!
- name: Setup ccache
if: ${{ (runner.os != 'Windows' || inputs.msystem == '') && inputs.build-type == 'Debug' }}
uses: hendrikmuhs/ccache-action@v1.2.23
uses: hendrikmuhs/ccache-action@v1.2.22
with:
variant: sccache
create-symlink: ${{ runner.os != 'Windows' }}

View file

@ -91,7 +91,7 @@ runs:
- name: Retrieve ccache cache (MinGW)
if: ${{ inputs.msystem != '' && inputs.build-type == 'Debug' }}
uses: actions/cache@v5.0.5
uses: actions/cache@v5.0.4
with:
path: '${{ github.workspace }}\.ccache'
key: ${{ runner.os }}-mingw-w64-ccache-${{ github.run_id }}

View file

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

View file

@ -29,7 +29,7 @@ jobs:
submodules: "true"
- name: Setup sccache
uses: hendrikmuhs/ccache-action@v1.2.23
uses: hendrikmuhs/ccache-action@v1.2.22
with:
variant: sccache

View file

@ -33,7 +33,7 @@ jobs:
- arch: arm64
os: ubuntu-24.04-arm
- arch: amd64
os: ubuntu-24.04
os: ubuntu-24.04-arm
runs-on: ${{ matrix.os }}

View file

@ -94,7 +94,7 @@ jobs:
- name: Create release
id: create_release
uses: softprops/action-gh-release@v3
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag_name: ${{ github.ref }}

View file

@ -20,7 +20,7 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: cachix/install-nix-action@616559265b40713947b9c190a8ff4b507b5df49b # v31
- uses: cachix/install-nix-action@96951a368ba55167b55f1c916f7d416bac6505fe # v31
- uses: DeterminateSystems/update-flake-lock@v28
with:

View file

@ -179,9 +179,9 @@ set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CAC
set(Launcher_LEGACY_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for legacy (<=1.5.2) FML Libraries.")
######## Set version numbers ########
set(Launcher_VERSION_MAJOR 12)
set(Launcher_VERSION_MAJOR 11)
set(Launcher_VERSION_MINOR 0)
set(Launcher_VERSION_PATCH 0)
set(Launcher_VERSION_PATCH 1)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}")
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0")

View file

@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1776169885,
"narHash": "sha256-Gk2T0tDDDAs319hp/ak+bAIUG5bPMvnNEjPV8CS86Fg=",
"rev": "4bd9165a9165d7b5e33ae57f3eecbcb28fb231c9",
"lastModified": 1774709303,
"narHash": "sha256-D4ely1FsBcvtj/qSrNhSWpq+CUZKNiKwJIxpxnfy9o4=",
"rev": "8110df5ad7abf5d4c0f6fb0f8f978390e77f9685",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre980183.4bd9165a9165/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre971119.8110df5ad7ab/nixexprs.tar.xz"
},
"original": {
"type": "tarball",

View file

@ -871,7 +871,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
resetIfInvalid(m_settings->registerSetting("LegacyFMLLibsURLOverride", "").get());
}
m_settings->registerSetting("MetaRefreshOnLaunch", true);
m_settings->registerSetting("CloseAfterLaunch", false);
m_settings->registerSetting("QuitAfterGameStop", false);

View file

@ -63,7 +63,7 @@ class BaseVersionList : public QAbstractListModel {
* The task returned by this function should reset the model when it's done.
* \return A pointer to a task that reloads the version list.
*/
virtual Task::Ptr getLoadTask(bool forceReload = false) = 0;
virtual Task::Ptr getLoadTask() = 0;
//! Checks whether or not the list is loaded. If this returns false, the list should be
// loaded.

View file

@ -141,6 +141,7 @@ uint64_t HardwareInfo::availableRamMiB()
}
#elif defined(Q_OS_MACOS)
#include "mach/mach.h"
#include "sys/sysctl.h"
QString HardwareInfo::cpuInfo()
@ -170,34 +171,20 @@ uint64_t HardwareInfo::totalRamMiB()
uint64_t HardwareInfo::availableRamMiB()
{
mach_port_t host_port = mach_host_self();
mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
vm_statistics64_data_t vm_stats;
if (host_statistics64(host_port, HOST_VM_INFO64, reinterpret_cast<host_info64_t>(&vm_stats), &count) == KERN_SUCCESS) {
// transforming bytes -> mib
return (vm_stats.free_count + vm_stats.inactive_count) * vm_page_size / 1024 / 1024;
}
qWarning() << "Could not get available RAM: host_statistics64";
return 0;
}
MacOSHardwareInfo::MemoryPressureLevel MacOSHardwareInfo::memoryPressureLevel()
{
uint32_t level;
size_t levelSize = sizeof level;
if (sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &levelSize, nullptr, 0) == 0) {
return static_cast<MemoryPressureLevel>(level);
}
qWarning() << "Could not get memory pressure level: sysctlbyname";
return MemoryPressureLevel::Normal;
}
QString MacOSHardwareInfo::memoryPressureLevelName()
{
// The names are internal, users refer to levels by their graph colors in Activity Monitor
switch (memoryPressureLevel()) {
case MemoryPressureLevel::Normal:
return "Green";
case MemoryPressureLevel::Warning:
return "Yellow";
case MemoryPressureLevel::Critical:
return "Red";
}
}
#elif defined(Q_OS_LINUX)
#include <fstream>

View file

@ -27,16 +27,3 @@ uint64_t totalRamMiB();
uint64_t availableRamMiB();
QStringList gpuInfo();
} // namespace HardwareInfo
#ifdef Q_OS_MACOS
namespace MacOSHardwareInfo {
enum class MemoryPressureLevel : uint8_t {
Normal = 1,
Warning = 2,
Critical = 4,
};
MemoryPressureLevel memoryPressureLevel();
QString memoryPressureLevelName();
} // namespace MacOSHardwareInfo
#endif

View file

@ -18,7 +18,7 @@ bool InstanceCreationTask::abort()
return m_gameFilesTask->abort();
}
return InstanceTask::abort();
return true;
}
void InstanceCreationTask::executeTask()

View file

@ -51,9 +51,8 @@ JavaInstallList::JavaInstallList(QObject* parent, bool onlyManagedVersions)
: BaseVersionList(parent), m_only_managed_versions(onlyManagedVersions)
{}
Task::Ptr JavaInstallList::getLoadTask(bool forceReload)
Task::Ptr JavaInstallList::getLoadTask()
{
Q_UNUSED(forceReload)
load();
return getCurrentTask();
}

View file

@ -35,7 +35,7 @@ class JavaInstallList : public BaseVersionList {
public:
explicit JavaInstallList(QObject* parent = 0, bool onlyManagedVersions = false);
Task::Ptr getLoadTask(bool forceReload = false) override;
Task::Ptr getLoadTask() override;
bool isLoaded() override;
const BaseVersion::Ptr at(int i) const override;
int count() const override;

View file

@ -82,12 +82,12 @@ QUrl BaseEntity::url() const
return QUrl(metaOverride).resolved(localFilename());
}
Task::Ptr BaseEntity::loadTask(Net::Mode mode, bool forceReload)
Task::Ptr BaseEntity::loadTask(Net::Mode mode)
{
if (m_task && m_task->isRunning()) {
return m_task;
}
m_task.reset(new BaseEntityLoadTask(this, mode, forceReload));
m_task.reset(new BaseEntityLoadTask(this, mode));
return m_task;
}
@ -107,9 +107,7 @@ BaseEntity::LoadStatus BaseEntity::status() const
return m_load_status;
}
BaseEntityLoadTask::BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode, bool forceReload)
: m_entity(parent), m_mode(mode), m_force_reload(forceReload)
{}
BaseEntityLoadTask::BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode) : m_entity(parent), m_mode(mode) {}
void BaseEntityLoadTask::executeTask()
{
@ -127,11 +125,9 @@ void BaseEntityLoadTask::executeTask()
}
// on online the hash needs to match
const auto& expected = m_entity->m_sha256;
const auto& actual = m_entity->m_file_sha256;
hashMatches = expected == actual;
hashMatches = m_entity->m_sha256 == m_entity->m_file_sha256;
if (m_mode == Net::Mode::Online && !m_entity->m_sha256.isEmpty() && !hashMatches) {
throw Exception(QString("Checksum mismatch, expected sha256: %1, got: %2").arg(expected, actual));
throw Exception("mismatched checksum");
}
// load local file
@ -153,18 +149,13 @@ void BaseEntityLoadTask::executeTask()
auto wasLoadedOffline = m_entity->m_load_status != BaseEntity::LoadStatus::NotLoaded && m_mode == Net::Mode::Offline;
// if has is not present allways fetch from remote(e.g. the main index file), else only fetch if hash doesn't match
auto wasLoadedRemote = m_entity->m_sha256.isEmpty() ? m_entity->m_load_status == BaseEntity::LoadStatus::Remote : hashMatches;
if (wasLoadedOffline || (wasLoadedRemote && !m_force_reload)) {
if (wasLoadedOffline || wasLoadedRemote) {
emitSucceeded();
return;
}
m_task.reset(new NetJob(QObject::tr("Download of meta file %1").arg(m_entity->localFilename()), APPLICATION->network()));
auto url = m_entity->url();
auto entry = APPLICATION->metacache()->resolveEntry("meta", m_entity->localFilename());
if (m_force_reload) {
// clear validators so manual refreshes fetch a fresh body
entry->setETag({});
entry->setRemoteChangedTimestamp({});
}
entry->setStale(true);
auto dl = Net::ApiDownload::makeCached(url, entry);
/*

View file

@ -43,7 +43,7 @@ class BaseEntity {
void setSha256(QString sha256);
virtual void parse(const QJsonObject& obj) = 0;
[[nodiscard]] Task::Ptr loadTask(Net::Mode loadType = Net::Mode::Online, bool forceReload = false);
[[nodiscard]] Task::Ptr loadTask(Net::Mode loadType = Net::Mode::Online);
protected:
QString m_sha256; // the expected sha256
@ -58,7 +58,7 @@ class BaseEntityLoadTask : public Task {
Q_OBJECT
public:
explicit BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode, bool forceReload);
explicit BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode);
~BaseEntityLoadTask() override = default;
virtual void executeTask() override;
@ -68,7 +68,6 @@ class BaseEntityLoadTask : public Task {
private:
BaseEntity* m_entity;
Net::Mode m_mode;
bool m_force_reload = false;
NetJob::Ptr m_task;
};
} // namespace Meta

View file

@ -15,7 +15,6 @@
#include "Index.h"
#include "Application.h"
#include "JsonFormat.h"
#include "QObjectPtr.h"
#include "VersionList.h"
@ -136,7 +135,7 @@ void Index::connectVersionList(const int row, const VersionList::Ptr& list)
Task::Ptr Index::loadVersion(const QString& uid, const QString& version, Net::Mode mode, bool force)
{
if (mode == Net::Mode::Offline || !APPLICATION->settings()->get("MetaRefreshOnLaunch").toBool()) {
if (mode == Net::Mode::Offline) {
return get(uid, version)->loadTask(mode);
}

View file

@ -32,11 +32,11 @@ VersionList::VersionList(const QString& uid, QObject* parent) : BaseVersionList(
setObjectName("Version list: " + uid);
}
Task::Ptr VersionList::getLoadTask(bool forceReload)
Task::Ptr VersionList::getLoadTask()
{
auto loadTask = makeShared<SequentialTask>(tr("Load meta for %1", "This is for the task name that loads the meta index.").arg(m_uid));
loadTask->addTask(APPLICATION->metadataIndex()->loadTask(Net::Mode::Online, forceReload));
loadTask->addTask(this->loadTask(Net::Mode::Online, forceReload));
loadTask->addTask(APPLICATION->metadataIndex()->loadTask(Net::Mode::Online));
loadTask->addTask(this->loadTask(Net::Mode::Online));
return loadTask;
}

View file

@ -37,7 +37,7 @@ class VersionList : public BaseVersionList, public BaseEntity {
enum Roles { UidRole = Qt::UserRole + 100, TimeRole, RequiresRole, VersionPtrRole };
bool isLoaded() override;
Task::Ptr getLoadTask(bool forceReload = false) override;
Task::Ptr getLoadTask() override;
const BaseVersion::Ptr at(int i) const override;
int count() const override;
void sortVersions() override;

View file

@ -149,7 +149,7 @@ QList<Net::NetRequest::Ptr> Library::getDownloads(const RuntimeContext& runtimeC
if (sha1.size()) {
auto dl = Net::ApiDownload::makeCached(url, entry, options);
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, sha1));
qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url << "expected sha1:" << sha1;
qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
out.append(dl);
} else {
out.append(Net::ApiDownload::makeCached(url, entry, options));

View file

@ -157,8 +157,7 @@ bool WorldList::resetIcon(int row)
return false;
World& m = m_worlds[row];
if (m.resetIcon()) {
QModelIndex modelIndex = index(row, NameColumn);
emit dataChanged(modelIndex, modelIndex, { WorldList::IconFileRole });
emit dataChanged(index(row), index(row), { WorldList::IconFileRole });
return true;
}
return false;
@ -427,7 +426,7 @@ void WorldList::loadWorldsAsync()
m_worlds[row].setSize(size);
// Notify views
QModelIndex modelIndex = index(row, SizeColumn);
QModelIndex modelIndex = index(row);
emit dataChanged(modelIndex, modelIndex, { SizeRole });
}
},

View file

@ -25,72 +25,22 @@ EnsureAvailableMemory::EnsureAvailableMemory(LaunchTask* parent, MinecraftInstan
void EnsureAvailableMemory::executeTask()
{
#ifdef Q_OS_MACOS
QString text;
switch (MacOSHardwareInfo::memoryPressureLevel()) {
case MacOSHardwareInfo::MemoryPressureLevel::Normal:
emitSucceeded();
return;
case MacOSHardwareInfo::MemoryPressureLevel::Warning:
text =
tr("The system is under increased memory pressure.\n"
"This may lead to lag or slowdowns.\n"
"If possible, close other applications before continuing.\n\n"
"Launch anyway?");
break;
case MacOSHardwareInfo::MemoryPressureLevel::Critical:
text =
tr("Your system is under critical memory pressure.\n"
"This may lead to severe slowdowns, crashes or system instability.\n"
"It is recommended to close other applications or restart your system.\n\n"
"Launch anyway?");
break;
}
bool shouldAbort = false;
if (m_instance->settings()->get("LowMemWarning").toBool()) {
auto* dialog = CustomMessageBox::selectable(nullptr, tr("High memory pressure"), text, QMessageBox::Icon::Warning,
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
QMessageBox::StandardButton::No);
shouldAbort = dialog->exec() == QMessageBox::No;
dialog->deleteLater();
}
const auto message = tr("The system is under high memory pressure");
if (shouldAbort) {
emit logLine(message, MessageLevel::Fatal);
emitFailed(message);
return;
}
emit logLine(message, MessageLevel::Warning);
emitSucceeded();
#else
const uint64_t available = HardwareInfo::availableRamMiB();
if (available == 0) {
// could not read
emitSucceeded();
return;
}
const uint64_t min = m_instance->settings()->get("MinMemAlloc").toUInt();
const uint64_t max = m_instance->settings()->get("MaxMemAlloc").toUInt();
const uint64_t required = std::max(min, max);
const uint64_t settingMin = m_instance->settings()->get("MinMemAlloc").toUInt();
const uint64_t settingMax = m_instance->settings()->get("MaxMemAlloc").toUInt();
const uint64_t max = std::max(settingMin, settingMax);
if (static_cast<double>(max) * 0.9 > static_cast<double>(available)) {
if (static_cast<double>(required) * 0.9 > static_cast<double>(available)) {
bool shouldAbort = false;
if (m_instance->settings()->get("LowMemWarning").toBool()) {
auto* dialog = CustomMessageBox::selectable(
nullptr, tr("Low free memory"),
tr("There might not be enough free RAM to launch this instance with the current memory settings.\n\n"
"Maximum allocated: %1 MiB\nFree: %2 MiB (out of %3 MiB total)\n\n"
"Launch anyway? This may cause slowdowns in the game and your system.")
.arg(max)
.arg(available)
.arg(HardwareInfo::totalRamMiB()),
nullptr, tr("Not enough RAM"),
tr("There is not enough RAM available to launch this instance with the current memory settings.\n\n"
"Required: %1 MiB\nAvailable: %2 MiB\n\n"
"Continue anyway? This may cause slowdowns in the game and your system.")
.arg(required)
.arg(available),
QMessageBox::Icon::Warning, QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
QMessageBox::StandardButton::No);
@ -109,5 +59,4 @@ void EnsureAvailableMemory::executeTask()
}
emitSucceeded();
#endif
}

View file

@ -68,12 +68,7 @@ void PrintInstanceInfo::executeTask()
::runPciconf(log);
#else
log << "CPU: " + HardwareInfo::cpuInfo();
#ifdef Q_OS_MACOS
log << "Memory pressure level: " + MacOSHardwareInfo::memoryPressureLevelName();
#else
log << QString("RAM: %1 MiB (available: %2 MiB)").arg(HardwareInfo::totalRamMiB()).arg(HardwareInfo::availableRamMiB());
#endif
#endif
log.append(HardwareInfo::gpuInfo());
log << "";

View file

@ -121,7 +121,7 @@ bool SkinList::update()
auto folderContents = m_dir.entryInfoList();
// if there are any untracked files...
for (QFileInfo entry : folderContents) {
if (!entry.isFile() || entry.suffix() != "png")
if (!entry.isFile() && entry.suffix() != "png")
continue;
SkinModel w(entry.absoluteFilePath());

View file

@ -148,9 +148,9 @@ Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback<QVe
return netJob;
}
Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback<ModPlatform::IndexedPack::Ptr>&& callbacks, bool askRetry) const
Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback<ModPlatform::IndexedPack::Ptr>&& callbacks) const
{
auto [job, response] = getProject(args.pack->addonId.toString(), askRetry);
auto [job, response] = getProject(args.pack->addonId.toString());
QObject::connect(job.get(), &NetJob::succeeded, [this, response, callbacks, args] {
auto pack = args.pack;
@ -284,7 +284,7 @@ QString ResourceAPI::mapMCVersionToModrinth(Version v) const
return verStr;
}
std::pair<Task::Ptr, QByteArray*> ResourceAPI::getProject(QString addonId, bool askRetry) const
std::pair<Task::Ptr, QByteArray*> ResourceAPI::getProject(QString addonId) const
{
auto project_url_optional = getInfoURL(addonId);
if (!project_url_optional.has_value())
@ -293,7 +293,6 @@ std::pair<Task::Ptr, QByteArray*> ResourceAPI::getProject(QString addonId, bool
auto project_url = project_url_optional.value();
auto netJob = makeShared<NetJob>(QString("%1::GetProject").arg(addonId), APPLICATION->network());
netJob->setAskRetry(askRetry);
auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(project_url));
netJob->addNetAction(action);

View file

@ -115,10 +115,10 @@ class ResourceAPI {
public slots:
virtual Task::Ptr searchProjects(SearchArgs&&, Callback<QList<ModPlatform::IndexedPack::Ptr>>&&) const;
virtual std::pair<Task::Ptr, QByteArray*> getProject(QString addonId, bool askRetry = true) const;
virtual std::pair<Task::Ptr, QByteArray*> getProject(QString addonId) const;
virtual std::pair<Task::Ptr, QByteArray*> getProjects(QStringList addonIds) const = 0;
virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback<ModPlatform::IndexedPack::Ptr>&&, bool askRetry = true) const;
virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback<ModPlatform::IndexedPack::Ptr>&&) const;
Task::Ptr getProjectVersions(VersionSearchArgs&& args, Callback<QVector<ModPlatform::IndexedVersion>>&& callbacks) const;
virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, Callback<ModPlatform::IndexedVersion>&&) const;

View file

@ -38,6 +38,7 @@
#include "Validator.h"
#include <QCryptographicHash>
#include <QFile>
namespace Net {
class ChecksumValidator : public Validator {
@ -68,10 +69,10 @@ class ChecksumValidator : public Validator {
return true;
}
auto validate(QNetworkReply& reply) -> bool override
auto validate(QNetworkReply&) -> bool override
{
if (!m_expected.isEmpty() && m_expected != hash()) {
qWarning() << "Checksum mismatch for URL:" << reply.url().toString() << "expected:" << m_expected << "got:" << hash();
if (m_expected.size() && m_expected != hash()) {
qWarning() << "Checksum mismatch, download is bad.";
return false;
}
return true;

View file

@ -69,15 +69,11 @@ void NetJob::executeNextSubTask()
// We're finished, check for failures and retry if we can (up to 3 times)
if (isRunning() && m_queue.isEmpty() && m_doing.isEmpty() && !m_failed.isEmpty() && m_try < 3) {
m_try += 1;
m_failed.removeIf([this](QHash<Task*, Task::Ptr>::iterator task) {
// there is no point in retying on 404 Not Found
if (static_cast<Net::NetRequest*>(task->get())->replyStatusCode() == 404) {
return false;
}
m_done.remove(task->get());
m_queue.enqueue(*task);
return true;
});
while (!m_failed.isEmpty()) {
auto task = m_failed.take(*m_failed.keyBegin());
m_done.remove(task.get());
m_queue.enqueue(task);
}
}
ConcurrentTask::executeNextSubTask();
}
@ -104,18 +100,13 @@ auto NetJob::canAbort() const -> bool
auto NetJob::abort() -> bool
{
bool fullyAborted = true;
// fail all downloads on the queue
for (auto task : m_queue)
m_failed.insert(task.get(), task);
m_queue.clear();
if (m_doing.isEmpty()) {
// no downloads to abort, NetJob is not running
return true;
}
bool fullyAborted = true;
// abort active downloads
auto toKill = m_doing.values();
for (auto part : toKill) {

View file

@ -118,7 +118,7 @@ auto ImgurUpload::Sink::finalize(QNetworkReply&) -> Task::State
Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot)
{
auto up = makeShared<ImgurUpload>(m_shot->m_file);
up->m_url = BuildConfig.IMGUR_BASE_URL + "image";
up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "image");
up->m_sink.reset(new Sink(m_shot));
up->addHeaderProxy(std::make_unique<Net::RawHeaderProxy>(QList<Net::HeaderPair>{
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() }, { "Accept", "application/json" } }));

View file

@ -48,13 +48,6 @@ Task::Task(bool show_debug) : m_show_debug(show_debug)
setAutoDelete(false);
}
Task::~Task()
{
if (isRunning()) {
qCWarning(taskLogC) << "Task" << describe() << "disposed while running!";
}
}
void Task::setStatus(const QString& new_status)
{
if (m_status != new_status) {

View file

@ -94,7 +94,7 @@ class Task : public QObject, public QRunnable {
public:
explicit Task(bool show_debug_log = true);
~Task() override;
virtual ~Task() = default;
bool isRunning() const;
bool isFinished() const;
@ -165,7 +165,7 @@ class Task : public QObject, public QRunnable {
//! used by external code to ask the task to abort
virtual bool abort()
{
if (canAbort() && isRunning())
if (canAbort())
emitAborted();
return canAbort();
}

View file

@ -105,7 +105,6 @@
#include "ui/dialogs/NewInstanceDialog.h"
#include "ui/dialogs/NewsDialog.h"
#include "ui/dialogs/ProgressDialog.h"
#include "ui/dialogs/skins/SkinManageDialog.h"
#include "ui/instanceview/InstanceDelegate.h"
#include "ui/instanceview/InstanceProxyModel.h"
#include "ui/instanceview/InstanceView.h"
@ -181,7 +180,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
ui->instanceToolBar->insertSeparator(ui->actionLaunchInstance);
// restore the instance toolbar settings
const auto setting_name = QString("WideBarVisibility_%1").arg(ui->instanceToolBar->objectName());
auto const setting_name = QString("WideBarVisibility_%1").arg(ui->instanceToolBar->objectName());
instanceToolbarSetting = APPLICATION->settings()->getOrRegisterSetting(setting_name);
ui->instanceToolBar->setVisibilityState(QByteArray::fromBase64(instanceToolbarSetting->get().toString().toUtf8()));
@ -397,7 +396,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
// Shouldn't have to use lambdas here like this, but if I don't, the compiler throws a fit.
// Template hell sucks...
connect(APPLICATION->accounts(), &AccountList::defaultAccountChanged, [this] { defaultAccountChanged(); });
connect(APPLICATION->accounts(), &AccountList::listActivityChanged, [this] { defaultAccountChanged(); });
connect(APPLICATION->accounts(), &AccountList::listChanged, [this] { defaultAccountChanged(); });
// Show initial account
@ -655,9 +653,6 @@ void MainWindow::repopulateAccountsMenu()
auto accounts = APPLICATION->accounts();
MinecraftAccountPtr defaultAccount = accounts->defaultAccount();
bool canChangeSkin = defaultAccount && (defaultAccount->accountType() == AccountType::MSA) && !defaultAccount->isActive();
ui->actionManageSkins->setEnabled(canChangeSkin);
QString active_profileId = "";
if (defaultAccount) {
@ -714,7 +709,6 @@ void MainWindow::repopulateAccountsMenu()
connect(ui->actionNoDefaultAccount, &QAction::triggered, this, &MainWindow::changeActiveAccount);
ui->accountsMenu->addSeparator();
ui->accountsMenu->addAction(ui->actionManageSkins);
ui->accountsMenu->addAction(ui->actionManageAccounts);
accountsButtonMenu->addActions(ui->accountsMenu->actions());
@ -948,7 +942,9 @@ void MainWindow::processURLs(QList<QUrl> urls)
QUrl local_url;
if (!url.isLocalFile()) { // download the remote resource and identify
const bool isExternalURLImport = (url.host().toLower() == "import") || (url.path().startsWith("/import", Qt::CaseInsensitive));
const bool isExternalURLImport =
(url.host().toLower() == "import") ||
(url.path().startsWith("/import", Qt::CaseInsensitive));
QUrl dl_url;
if (url.scheme() == "curseforge" || (url.scheme() == BuildConfig.LAUNCHER_APP_BINARY_NAME && url.host() == "install")) {
@ -956,7 +952,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
// format of url curseforge://install?addonId=IDHERE&fileId=IDHERE
// format of url binaryname://install?platform=curseforge&addonId=IDHERE&fileId=IDHERE
QUrlQuery query(url);
// check if this is a binaryname:// url
if (url.scheme() == BuildConfig.LAUNCHER_APP_BINARY_NAME) {
// check this is an curseforge platform request
@ -1019,7 +1015,8 @@ void MainWindow::processURLs(QList<QUrl> urls)
receivedData.insert(it->first, it->second);
emit APPLICATION->oauthReplyRecieved(receivedData);
continue;
} else if ((url.scheme() == "prismlauncher" || url.scheme() == BuildConfig.LAUNCHER_APP_BINARY_NAME) && isExternalURLImport) {
} else if ((url.scheme() == "prismlauncher" || url.scheme() == BuildConfig.LAUNCHER_APP_BINARY_NAME)
&& isExternalURLImport) {
// PrismLauncher URL protocol modpack import
// works for any prism fork
// preferred import format: prismlauncher://import?url=ENCODED
@ -1038,6 +1035,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
// alternative import format: prismlauncher://import/ENCODED
if (encodedTarget.isEmpty()) {
QString p = path;
if (p.startsWith("/import/", Qt::CaseInsensitive)) {
@ -1052,9 +1050,12 @@ void MainWindow::processURLs(QList<QUrl> urls)
}
if (encodedTarget.isEmpty()) {
CustomMessageBox::selectable(this, tr("Error"), tr("Invalid import link: missing 'url' parameter."),
QMessageBox::Critical)
->show();
CustomMessageBox::selectable(
this,
tr("Error"),
tr("Invalid import link: missing 'url' parameter."),
QMessageBox::Critical
)->show();
continue;
}
@ -1064,15 +1065,23 @@ void MainWindow::processURLs(QList<QUrl> urls)
// Validate: only allow http(s)
if (!target.isValid() || (target.scheme() != "https" && target.scheme() != "http")) {
CustomMessageBox::selectable(this, tr("Error"), tr("Invalid import link: URL must be http(s)."), QMessageBox::Critical)
->show();
CustomMessageBox::selectable(
this,
tr("Error"),
tr("Invalid import link: URL must be http(s)."),
QMessageBox::Critical
)->show();
continue;
}
const auto res = QMessageBox::question(
this, tr("Install modpack"),
tr("Do you want to download and import a modpack from:\n%1\n\nURL:\n%2").arg(target.host(), target.toString()),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
this,
tr("Install modpack"),
tr("Do you want to download and import a modpack from:\n%1\n\nURL:\n%2")
.arg(target.host(), target.toString()),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes
);
if (res != QMessageBox::Yes) {
continue;
}
@ -1387,16 +1396,6 @@ void MainWindow::on_actionEditInstance_triggered()
}
}
void MainWindow::on_actionManageSkins_triggered()
{
auto account = APPLICATION->accounts()->defaultAccount();
if (account && (account->accountType() == AccountType::MSA) && !account->isActive()) {
SkinManageDialog dialog(this, account);
dialog.exec();
}
}
void MainWindow::on_actionManageAccounts_triggered()
{
APPLICATION->ShowGlobalSettings(this, "accounts");

View file

@ -130,8 +130,6 @@ class MainWindow : public QMainWindow {
void on_actionSettings_triggered();
void on_actionManageSkins_triggered();
void on_actionManageAccounts_triggered();
void on_actionReportBug_triggered();

View file

@ -322,14 +322,6 @@
<enum>QAction::PreferencesRole</enum>
</property>
</action>
<action name="actionManageSkins">
<property name="icon">
<iconset theme="settings"/>
</property>
<property name="text">
<string>Manage &amp;Skins...</string>
</property>
</action>
<action name="actionManageAccounts">
<property name="icon">
<iconset theme="accounts"/>

View file

@ -101,7 +101,7 @@ InstallLoaderDialog::InstallLoaderDialog(PackProfile* profile, const QString& ui
buttonLayout->setContentsMargins(0, 0, 6, 6);
#endif
auto refreshButton = new QPushButton(tr("&Refresh"), this);
connect(refreshButton, &QPushButton::clicked, this, [this] { pageCast(container->selectedPage())->loadList(true); });
connect(refreshButton, &QPushButton::clicked, this, [this] { pageCast(container->selectedPage())->loadList(); });
buttonLayout->addWidget(refreshButton);
buttons->setOrientation(Qt::Horizontal);

View file

@ -252,7 +252,10 @@ void ProgressDialog::changeStepProgress(TaskStepProgress const& task_progress)
task_bar->setValue(mapped_current);
task_bar->setStatus(task_progress.status);
task_bar->setDetails(task_progress.details);
task_bar->setVisible(!task_progress.isDone());
if (task_progress.isDone()) {
task_bar->setVisible(false);
}
}
void ProgressDialog::changeProgress(qint64 current, qint64 total)

View file

@ -144,7 +144,7 @@ BaseVersion::Ptr VersionSelectDialog::selectedVersion() const
void VersionSelectDialog::on_refreshButton_clicked()
{
m_versionWidget->loadList(true);
m_versionWidget->loadList();
}
void VersionSelectDialog::setExactFilter(BaseVersionList::ModelRoles role, QString filter)

View file

@ -121,8 +121,8 @@ class InstallJavaPage : public QWidget, public BasePage {
void selectSearch() { javaVersionSelect->selectSearch(); }
void loadList()
{
majorVersionSelect->loadList(true);
javaVersionSelect->loadList(true);
majorVersionSelect->loadList();
javaVersionSelect->loadList();
}
public slots:

View file

@ -33,9 +33,9 @@ VersionList::VersionList(Meta::Version::Ptr version, QObject* parent) : BaseVers
sortVersions();
}
Task::Ptr VersionList::getLoadTask(bool forceReload)
Task::Ptr VersionList::getLoadTask()
{
auto task = m_version->loadTask(Net::Mode::Online, forceReload);
auto task = m_version->loadTask(Net::Mode::Online);
connect(task.get(), &Task::finished, this, &VersionList::sortVersions);
return task;
}

View file

@ -30,7 +30,7 @@ class VersionList : public BaseVersionList {
public:
explicit VersionList(Meta::Version::Ptr m_version, QObject* parent = 0);
Task::Ptr getLoadTask(bool forceReload = false) override;
Task::Ptr getLoadTask() override;
bool isLoaded() override;
const BaseVersion::Ptr at(int i) const override;
int count() const override;

View file

@ -143,7 +143,6 @@ void APIPage::loadSettings()
ui->msaClientID->setText(msaClientID);
QString metaURL = s->get("MetaURLOverride").toString();
ui->metaURL->setText(metaURL);
ui->metaRefreshOnLaunchCB->setCheckState(s->get("MetaRefreshOnLaunch").toBool() ? Qt::Checked : Qt::Unchecked);
QString resourceURL = s->get("ResourceURLOverride").toString();
ui->resourceURL->setText(resourceURL);
QString fmlLibsURL = s->get("LegacyFMLLibsURLOverride").toString();
@ -195,7 +194,6 @@ void APIPage::applySettings()
s->set("FallbackMRBlockedMods", ui->FallbackMRBlockedMods->checkState());
s->set("MetaURLOverride", metaURL.toString());
s->set("MetaRefreshOnLaunch", ui->metaRefreshOnLaunchCB->checkState() == Qt::Checked);
s->set("ResourceURLOverride", resourceURL.toString());
s->set("LegacyFMLLibsURLOverride", fmlLibsURL.toString());
QString flameKey = ui->flameKey->text();

View file

@ -32,9 +32,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>825</width>
<height>1236</height>
<y>-262</y>
<width>820</width>
<height>908</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@ -126,13 +126,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="metaRefreshOnLaunchCB">
<property name="text">
<string>Refresh on launch</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View file

@ -45,6 +45,7 @@
#include <QFileSystemModel>
#include <QKeyEvent>
#include <QLineEdit>
#include <QMap>
#include <QMenu>
#include <QModelIndex>
#include <QMutableListIterator>
@ -52,8 +53,6 @@
#include <QRegularExpression>
#include <QSet>
#include <QStyledItemDelegate>
#include <memory>
#include <utility>
#include <Application.h>
#include "settings/SettingsObject.h"
@ -71,14 +70,9 @@
#include "RWStorage.h"
class ScreenshotsFSModel : public QFileSystemModel {
public:
bool canDropMimeData(const QMimeData* data,
const Qt::DropAction action,
const int row,
const int column,
const QModelIndex& parent) const override
bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override
{
const QUrl root = QUrl::fromLocalFile(rootPath());
QUrl root = QUrl::fromLocalFile(rootPath());
// this disables reordering items inside the model
// by rejecting drops if the file is already inside the folder
if (data->hasUrls()) {
@ -98,8 +92,8 @@ using SharedIconCachePtr = std::shared_ptr<SharedIconCache>;
class ThumbnailingResult : public QObject {
Q_OBJECT
public slots:
void emitResultsReady(const QString& path) { emit resultsReady(path); }
void emitResultsFailed(const QString& path) { emit resultsFailed(path); }
inline void emitResultsReady(const QString& path) { emit resultsReady(path); }
inline void emitResultsFailed(const QString& path) { emit resultsFailed(path); }
signals:
void resultsReady(const QString& path);
void resultsFailed(const QString& path);
@ -107,32 +101,32 @@ class ThumbnailingResult : public QObject {
class ThumbnailRunnable : public QRunnable {
public:
ThumbnailRunnable(QString path, SharedIconCachePtr cache) : m_path(std::move(path)), m_cache(std::move(cache)) {}
void run() override
ThumbnailRunnable(QString path, SharedIconCachePtr cache)
{
const QFileInfo info(m_path);
if (info.isDir()) {
m_path = path;
m_cache = cache;
}
void run()
{
QFileInfo info(m_path);
if (info.isDir())
return;
}
if (info.suffix().compare("png", Qt::CaseInsensitive) != 0) {
if ((info.suffix().compare("png", Qt::CaseInsensitive) != 0))
return;
}
if (!m_cache->stale(m_path)) {
if (!m_cache->stale(m_path))
return;
}
const QImage image(m_path);
QImage image(m_path);
if (image.isNull()) {
m_resultEmitter.emitResultsFailed(m_path);
qDebug() << "Error loading screenshot (perhaps too large?):" + m_path;
return;
}
QImage small;
if (image.width() > image.height()) {
if (image.width() > image.height())
small = image.scaledToWidth(512).scaledToWidth(256, Qt::SmoothTransformation);
} else {
else
small = image.scaledToHeight(512).scaledToHeight(256, Qt::SmoothTransformation);
}
const QPoint offset((256 - small.width()) / 2, (256 - small.height()) / 2);
QPoint offset((256 - small.width()) / 2, (256 - small.height()) / 2);
QImage square(QSize(256, 256), QImage::Format_ARGB32);
square.fill(Qt::transparent);
@ -140,7 +134,7 @@ class ThumbnailRunnable : public QRunnable {
painter.drawImage(offset, small);
painter.end();
const QIcon icon(QPixmap::fromImage(square));
QIcon icon(QPixmap::fromImage(square));
m_cache->add(m_path, icon);
m_resultEmitter.emitResultsReady(m_path);
}
@ -154,62 +148,59 @@ class ThumbnailRunnable : public QRunnable {
class FilterModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit FilterModel(QObject* parent = nullptr) : QIdentityProxyModel(parent)
explicit FilterModel(QObject* parent = 0) : QIdentityProxyModel(parent)
{
m_thumbnailingPool.setMaxThreadCount(4);
m_thumbnailCache = std::make_shared<SharedIconCache>();
m_thumbnailCache->add("placeholder", QIcon::fromTheme("screenshot-placeholder"));
connect(&watcher, &QFileSystemWatcher::fileChanged, this, &FilterModel::fileChanged);
}
~FilterModel() override
virtual ~FilterModel()
{
m_thumbnailingPool.clear();
if (!m_thumbnailingPool.waitForDone(500)) {
if (!m_thumbnailingPool.waitForDone(500))
qDebug() << "Thumbnail pool took longer than 500ms to finish";
}
}
QVariant data(const QModelIndex& proxyIndex, const int role = Qt::DisplayRole) const override // NOLINT(*-default-arguments)
virtual QVariant data(const QModelIndex& proxyIndex, int role = Qt::DisplayRole) const
{
const auto* model = sourceModel();
if (!model) {
return {};
}
auto model = sourceModel();
if (!model)
return QVariant();
if (role == Qt::DisplayRole || role == Qt::EditRole) {
const QVariant result = model->data(mapToSource(proxyIndex), role);
QVariant result = sourceModel()->data(mapToSource(proxyIndex), role);
static const QRegularExpression s_removeChars("\\.png$");
return result.toString().remove(s_removeChars);
}
if (role == Qt::DecorationRole) {
const QVariant result = model->data(mapToSource(proxyIndex), QFileSystemModel::FilePathRole);
const QString filePath = result.toString();
QVariant result = sourceModel()->data(mapToSource(proxyIndex), QFileSystemModel::FilePathRole);
QString filePath = result.toString();
QIcon temp;
if (!watched.contains(filePath)) {
const_cast<QFileSystemWatcher&>(watcher).addPath(filePath);
const_cast<QSet<QString>&>(watched).insert(filePath);
((QFileSystemWatcher&)watcher).addPath(filePath);
((QSet<QString>&)watched).insert(filePath);
}
if (QIcon temp; m_thumbnailCache->get(filePath, temp)) {
if (m_thumbnailCache->get(filePath, temp)) {
return temp;
}
if (!m_failed.contains(filePath)) {
const_cast<FilterModel*>(this)->thumbnailImage(filePath);
((FilterModel*)this)->thumbnailImage(filePath);
}
return (m_thumbnailCache->get("placeholder"));
}
return model->data(mapToSource(proxyIndex), role);
return sourceModel()->data(mapToSource(proxyIndex), role);
}
bool setData(const QModelIndex& index, const QVariant& value, const int role = Qt::EditRole) override // NOLINT(*-default-arguments)
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole)
{
auto* model = sourceModel();
if (!model) {
auto model = sourceModel();
if (!model)
return false;
}
if (role != Qt::EditRole) {
if (role != Qt::EditRole)
return false;
}
// FIXME: this is a workaround for a bug in QFileSystemModel, where it doesn't
// sort after renames
{
static_cast<QFileSystemModel*>(model)->setNameFilterDisables(true);
static_cast<QFileSystemModel*>(model)->setNameFilterDisables(false);
((QFileSystemModel*)model)->setNameFilterDisables(true);
((QFileSystemModel*)model)->setNameFilterDisables(false);
}
return model->setData(mapToSource(index), value.toString() + ".png", role);
}
@ -217,15 +208,15 @@ class FilterModel : public QIdentityProxyModel {
private:
void thumbnailImage(QString path)
{
auto* runnable = new ThumbnailRunnable(std::move(path), m_thumbnailCache);
auto runnable = new ThumbnailRunnable(path, m_thumbnailCache);
connect(&runnable->m_resultEmitter, &ThumbnailingResult::resultsReady, this, &FilterModel::thumbnailReady);
connect(&runnable->m_resultEmitter, &ThumbnailingResult::resultsFailed, this, &FilterModel::thumbnailFailed);
m_thumbnailingPool.start(runnable);
}
private slots:
void thumbnailReady(const QString& /*path*/) { emit layoutChanged(); }
void thumbnailFailed(const QString& path) { m_failed.insert(path); }
void fileChanged(const QString& filepath)
void thumbnailReady(QString path) { emit layoutChanged(); }
void thumbnailFailed(QString path) { m_failed.insert(path); }
void fileChanged(QString filepath)
{
m_thumbnailCache->setStale(filepath);
// reinsert the path...
@ -246,12 +237,13 @@ class FilterModel : public QIdentityProxyModel {
class CenteredEditingDelegate : public QStyledItemDelegate {
public:
explicit CenteredEditingDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {}
~CenteredEditingDelegate() override = default;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
explicit CenteredEditingDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) {}
virtual ~CenteredEditingDelegate() {}
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto* widget = QStyledItemDelegate::createEditor(parent, option, index);
if (auto* foo = dynamic_cast<QLineEdit*>(widget)) {
auto widget = QStyledItemDelegate::createEditor(parent, option, index);
auto foo = dynamic_cast<QLineEdit*>(widget);
if (foo) {
foo->setAlignment(Qt::AlignHCenter);
foo->setFrame(true);
foo->setMaximumWidth(192);
@ -260,11 +252,10 @@ class CenteredEditingDelegate : public QStyledItemDelegate {
}
};
ScreenshotsPage::ScreenshotsPage(QString path, QWidget* parent)
: QMainWindow(parent), ui(new Ui::ScreenshotsPage), m_folder(std::move(path))
ScreenshotsPage::ScreenshotsPage(QString path, QWidget* parent) : QMainWindow(parent), ui(new Ui::ScreenshotsPage)
{
m_model = std::make_shared<ScreenshotsFSModel>();
m_filterModel = std::make_shared<FilterModel>();
m_model.reset(new ScreenshotsFSModel());
m_filterModel.reset(new FilterModel());
m_filterModel->setSourceModel(m_model.get());
m_model->setFilter(QDir::Files);
m_model->setReadOnly(false);
@ -275,6 +266,7 @@ ScreenshotsPage::ScreenshotsPage(QString path, QWidget* parent)
constexpr int file_modified_column_index = 3;
m_model->sort(file_modified_column_index, Qt::DescendingOrder);
m_folder = path;
m_valid = FS::ensureFolderPathExists(m_folder);
ui->setupUi(this);
@ -291,19 +283,18 @@ ScreenshotsPage::ScreenshotsPage(QString path, QWidget* parent)
ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->listView->setItemDelegate(new CenteredEditingDelegate(this));
ui->listView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->listView, &QListView::customContextMenuRequested, this, &ScreenshotsPage::showContextMenu);
connect(ui->listView, &QListView::customContextMenuRequested, this, &ScreenshotsPage::ShowContextMenu);
connect(ui->listView, &QAbstractItemView::activated, this, &ScreenshotsPage::onItemActivated);
}
bool ScreenshotsPage::eventFilter(QObject* obj, QEvent* evt)
{
if (obj != ui->listView) {
if (obj != ui->listView)
return QWidget::eventFilter(obj, evt);
}
if (evt->type() != QEvent::KeyPress) {
return QWidget::eventFilter(obj, evt);
}
const auto* keyEvent = static_cast<QKeyEvent*>(evt);
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(evt);
if (keyEvent->matches(QKeySequence::Copy)) {
on_actionCopy_File_s_triggered();
@ -333,11 +324,11 @@ ScreenshotsPage::~ScreenshotsPage()
delete ui;
}
void ScreenshotsPage::showContextMenu(const QPoint& pos)
void ScreenshotsPage::ShowContextMenu(const QPoint& pos)
{
auto* menu = ui->toolBar->createContextMenu(this, tr("Context menu"));
auto menu = ui->toolBar->createContextMenu(this, tr("Context menu"));
if (ui->listView->selectionModel()->selectedIndexes().size() > 1) {
if (ui->listView->selectionModel()->selectedRows().size() > 1) {
menu->removeAction(ui->actionCopy_Image);
}
@ -352,75 +343,66 @@ QMenu* ScreenshotsPage::createPopupMenu()
return filteredMenu;
}
void ScreenshotsPage::onItemActivated(QModelIndex index) const
void ScreenshotsPage::onItemActivated(QModelIndex index)
{
if (!index.isValid()) {
if (!index.isValid())
return;
}
const auto info = m_model->fileInfo(index);
auto info = m_model->fileInfo(index);
DesktopServices::openPath(info);
}
void ScreenshotsPage::onCurrentSelectionChanged(const QItemSelection& /*selected*/) const
void ScreenshotsPage::onCurrentSelectionChanged(const QItemSelection& selected)
{
const auto selected = ui->listView->selectionModel()->selectedIndexes();
bool allReadable = !selected.isEmpty();
bool allWritable = !selected.isEmpty();
for (auto index : selected) {
if (!index.isValid()) {
for (auto index : selected.indexes()) {
if (!index.isValid())
break;
}
auto info = m_model->fileInfo(index);
if (!info.isReadable()) {
if (!info.isReadable())
allReadable = false;
}
if (!info.isWritable()) {
if (!info.isWritable())
allWritable = false;
}
}
ui->actionUpload->setEnabled(allReadable);
ui->actionCopy_Image->setEnabled(allReadable && selected.size() == 1);
ui->actionCopy_Image->setEnabled(allReadable);
ui->actionCopy_File_s->setEnabled(allReadable);
ui->actionDelete->setEnabled(allWritable);
ui->actionRename->setEnabled(allWritable);
}
void ScreenshotsPage::on_actionView_Folder_triggered() const
void ScreenshotsPage::on_actionView_Folder_triggered()
{
DesktopServices::openPath(m_folder, true);
}
void ScreenshotsPage::on_actionUpload_triggered()
{
auto selection = ui->listView->selectionModel()->selectedIndexes();
if (selection.isEmpty()) {
auto selection = ui->listView->selectionModel()->selectedRows();
if (selection.isEmpty())
return;
}
QString text;
const QUrl baseUrl(BuildConfig.IMGUR_BASE_URL);
if (selection.size() > 1) {
QUrl baseUrl(BuildConfig.IMGUR_BASE_URL);
if (selection.size() > 1)
text = tr("You are about to upload %1 screenshots to %2.\n"
"You should double-check for personal information.\n\n"
"Are you sure?")
.arg(QString::number(selection.size()), baseUrl.host());
} else {
else
text = tr("You are about to upload the selected screenshot to %1.\n"
"You should double-check for personal information.\n\n"
"Are you sure?")
.arg(baseUrl.host());
}
auto response = CustomMessageBox::selectable(this, "Confirm Upload", text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No,
QMessageBox::No)
->exec();
if (response != QMessageBox::Yes) {
if (response != QMessageBox::Yes)
return;
}
QList<ScreenShot::Ptr> uploaded;
auto job = NetJob::Ptr(new NetJob("Screenshot Upload", APPLICATION->network()));
@ -434,7 +416,7 @@ void ScreenshotsPage::on_actionUpload_triggered()
auto screenshot = std::make_shared<ScreenShot>(info);
job->addNetAction(ImgurUpload::make(screenshot));
connect(job.get(), &Task::failed, [this](const QString& reason) {
connect(job.get(), &Task::failed, [this](QString reason) {
CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), reason, QMessageBox::Critical)->show();
});
connect(job.get(), &Task::aborted, [this] {
@ -475,7 +457,7 @@ void ScreenshotsPage::on_actionUpload_triggered()
task.addTask(job);
task.addTask(albumTask);
connect(&task, &Task::failed, [this](const QString& reason) {
connect(&task, &Task::failed, [this](QString reason) {
CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), reason, QMessageBox::Critical)->show();
});
connect(&task, &Task::aborted, [this] {
@ -503,24 +485,24 @@ void ScreenshotsPage::on_actionUpload_triggered()
m_uploadActive = false;
}
void ScreenshotsPage::on_actionCopy_Image_triggered() const
void ScreenshotsPage::on_actionCopy_Image_triggered()
{
auto selection = ui->listView->selectionModel()->selectedIndexes();
auto selection = ui->listView->selectionModel()->selectedRows();
if (selection.size() < 1) {
return;
}
// You can only copy one image to the clipboard. In the case of multiple selected files, only the first one gets copied.
const auto item = selection.first();
const auto info = m_model->fileInfo(item);
const QImage image(info.absoluteFilePath());
auto item = selection[0];
auto info = m_model->fileInfo(item);
QImage image(info.absoluteFilePath());
Q_ASSERT(!image.isNull());
QApplication::clipboard()->setImage(image, QClipboard::Clipboard);
}
void ScreenshotsPage::on_actionCopy_File_s_triggered() const
void ScreenshotsPage::on_actionCopy_File_s_triggered()
{
auto selection = ui->listView->selectionModel()->selectedIndexes();
auto selection = ui->listView->selectionModel()->selectedRows();
if (selection.size() < 1) {
// Don't do anything so we don't empty the users clipboard
return;
@ -531,7 +513,7 @@ void ScreenshotsPage::on_actionCopy_File_s_triggered() const
auto info = m_model->fileInfo(item);
buf += "file:///" + info.absoluteFilePath() + "\r\n";
}
auto* mimeData = new QMimeData();
QMimeData* mimeData = new QMimeData();
mimeData->setData("text/uri-list", buf.toLocal8Bit());
QApplication::clipboard()->setMimeData(mimeData);
}
@ -540,43 +522,39 @@ void ScreenshotsPage::on_actionDelete_triggered()
{
auto selected = ui->listView->selectionModel()->selectedIndexes();
const qsizetype count = selected.size();
int count = ui->listView->selectionModel()->selectedRows().size();
QString text;
if (count > 1) {
if (count > 1)
text = tr("You are about to delete %1 screenshots.\n"
"This may be permanent and they will be gone from the folder.\n\n"
"Are you sure?")
.arg(count);
} else {
text =
tr("You are about to delete the selected screenshot.\n"
"This may be permanent and it will be gone from the folder.\n\n"
"Are you sure?");
}
else
text = tr("You are about to delete the selected screenshot.\n"
"This may be permanent and it will be gone from the folder.\n\n"
"Are you sure?")
.arg(count);
const auto response =
auto response =
CustomMessageBox::selectable(this, tr("Confirm Deletion"), text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No)->exec();
if (response != QMessageBox::Yes) {
if (response != QMessageBox::Yes)
return;
}
for (auto item : selected) {
if (FS::trash(m_model->filePath(item))) {
if (FS::trash(m_model->filePath(item)))
continue;
}
m_model->remove(item);
}
}
void ScreenshotsPage::on_actionRename_triggered() const
void ScreenshotsPage::on_actionRename_triggered()
{
auto selection = ui->listView->selectionModel()->selectedIndexes();
if (selection.isEmpty()) {
if (selection.isEmpty())
return;
}
ui->listView->edit(selection.first());
ui->listView->edit(selection[0]);
// TODO: mass renaming
}
@ -586,8 +564,8 @@ void ScreenshotsPage::openedImpl()
m_valid = FS::ensureFolderPathExists(m_folder);
}
if (m_valid) {
const QString path = QDir(m_folder).absolutePath();
const auto idx = m_model->setRootPath(path);
QString path = QDir(m_folder).absolutePath();
auto idx = m_model->setRootPath(path);
if (idx.isValid()) {
ui->listView->setModel(m_filterModel.get());
connect(ui->listView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
@ -599,7 +577,7 @@ void ScreenshotsPage::openedImpl()
}
}
const auto setting_name = QString("WideBarVisibility_%1").arg(id());
auto const setting_name = QString("WideBarVisibility_%1").arg(id());
m_wide_bar_setting = APPLICATION->settings()->getOrRegisterSetting(setting_name);
ui->toolBar->setVisibilityState(QByteArray::fromBase64(m_wide_bar_setting->get().toString().toUtf8()));

View file

@ -78,14 +78,14 @@ class ScreenshotsPage : public QMainWindow, public BasePage {
private slots:
void on_actionUpload_triggered();
void on_actionCopy_Image_triggered() const;
void on_actionCopy_File_s_triggered() const;
void on_actionCopy_Image_triggered();
void on_actionCopy_File_s_triggered();
void on_actionDelete_triggered();
void on_actionRename_triggered() const;
void on_actionView_Folder_triggered() const;
void onItemActivated(QModelIndex) const;
void onCurrentSelectionChanged(const QItemSelection& selected) const;
void showContextMenu(const QPoint& pos);
void on_actionRename_triggered();
void on_actionView_Folder_triggered();
void onItemActivated(QModelIndex);
void onCurrentSelectionChanged(const QItemSelection& selected);
void ShowContextMenu(const QPoint& pos);
private:
Ui::ScreenshotsPage* ui;

View file

@ -80,14 +80,14 @@ void CustomPage::openedImpl()
void CustomPage::refresh()
{
ui->versionList->loadList(true);
ui->versionList->loadList();
}
void CustomPage::loaderRefresh()
{
if (ui->noneFilter->isChecked())
return;
ui->loaderVersionList->loadList(true);
ui->loaderVersionList->loadList();
}
void CustomPage::filterChanged()

View file

@ -139,18 +139,15 @@ void ResourceModel::search()
if (hasActiveSearchJob())
return;
if (m_search_state != SearchState::ResetRequested && m_search_term.startsWith("#")) {
if (m_search_term.startsWith("#")) {
auto projectId = m_search_term.mid(1);
if (!projectId.isEmpty()) {
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
callbacks.on_fail = [this](QString reason, int network_error_code) {
callbacks.on_fail = [this](QString reason, int) {
if (!s_running_models.constFind(this).value())
return;
if (network_error_code == 404) {
m_search_state = SearchState::ResetRequested;
}
searchRequestFailed(reason, network_error_code);
searchRequestFailed(reason, -1);
};
callbacks.on_abort = [this] {
if (!s_running_models.constFind(this).value())
@ -165,7 +162,7 @@ void ResourceModel::search()
};
auto project = std::make_shared<ModPlatform::IndexedPack>();
project->addonId = projectId;
if (auto job = m_api->getProjectInfo({ project }, std::move(callbacks), false); job)
if (auto job = m_api->getProjectInfo({ project }, std::move(callbacks)); job)
runSearchJob(job);
return;
}
@ -410,9 +407,6 @@ void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int net
// Network error
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load mods."));
break;
case 404:
// 404 Not Found, some APIs return this when nothing is found, no need to bother the user
break;
case 409:
// 409 Gone, notify user to update
QMessageBox::critical(nullptr, tr("Error"),
@ -420,14 +414,7 @@ void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int net
break;
}
if (m_search_state == SearchState::ResetRequested) {
clearData();
m_next_search_offset = 0;
search();
} else {
m_search_state = SearchState::Finished;
}
m_search_state = SearchState::Finished;
}
void ResourceModel::searchRequestAborted()

View file

@ -165,20 +165,12 @@ void ListModel::fetchMore(const QModelIndex& parent)
void ListModel::performPaginatedSearch()
{
static const FlameAPI api;
// activate search by id only for numerical values because all CurseForge ids are numerical
static const QRegularExpression s_projectIdExpr("^\\#[0-9]+$");
if (m_searchState != ResetRequested && s_projectIdExpr.match(m_currentSearchTerm).hasMatch()) {
if (m_currentSearchTerm.startsWith("#")) {
auto projectId = m_currentSearchTerm.mid(1);
if (!projectId.isEmpty()) {
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
callbacks.on_fail = [this](QString reason, int network_error_code) {
if (network_error_code == 404) {
m_searchState = ResetRequested;
}
searchRequestFailed(reason);
};
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
callbacks.on_abort = [this] {
qCritical() << "Search task aborted by an unknown reason!";
@ -186,7 +178,7 @@ void ListModel::performPaginatedSearch()
};
auto project = std::make_shared<ModPlatform::IndexedPack>();
project->addonId = projectId;
if (auto job = api.getProjectInfo({ project }, std::move(callbacks), false); job) {
if (auto job = api.getProjectInfo({ project }, std::move(callbacks)); job) {
m_jobPtr = job;
m_jobPtr->start();
}

View file

@ -135,26 +135,20 @@ void ModpackListModel::performPaginatedSearch()
return;
static const ModrinthAPI api;
// Modrinth ids are not limited to numbers and can be any length
if (m_searchState != ResetRequested && m_currentSearchTerm.startsWith("#")) {
if (m_currentSearchTerm.startsWith("#")) {
auto projectId = m_currentSearchTerm.mid(1);
if (!projectId.isEmpty()) {
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
callbacks.on_fail = [this](QString reason, int network_error_code) {
if (network_error_code == 404) {
m_searchState = ResetRequested;
}
searchRequestFailed(reason, network_error_code);
};
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
callbacks.on_abort = [this] {
qCritical() << "Search task aborted by an unknown reason!";
searchRequestFailed("Aborted", 0);
searchRequestFailed("Aborted");
};
auto project = std::make_shared<ModPlatform::IndexedPack>();
project->addonId = projectId;
if (auto job = api.getProjectInfo({ project }, std::move(callbacks), false); job) {
if (auto job = api.getProjectInfo({ project }, std::move(callbacks)); job) {
m_jobPtr = job;
m_jobPtr->start();
}
@ -167,10 +161,10 @@ void ModpackListModel::performPaginatedSearch()
ResourceAPI::Callback<QList<ModPlatform::IndexedPack::Ptr>> callbacks{};
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
callbacks.on_fail = [this](QString reason, int network_error_code) { searchRequestFailed(reason, network_error_code); };
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
callbacks.on_abort = [this] {
qCritical() << "Search task aborted by an unknown reason!";
searchRequestFailed("Aborted", 0);
searchRequestFailed("Aborted");
};
auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders,
@ -322,12 +316,13 @@ void ModpackListModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack::Pt
endInsertRows();
}
void ModpackListModel::searchRequestFailed(QString reason, int network_error_code)
void ModpackListModel::searchRequestFailed(QString)
{
if (network_error_code == -1) {
// Unknown error in network stack
auto failed_action = dynamic_cast<NetJob*>(m_jobPtr.get())->getFailedActions().at(0);
if (failed_action->replyStatusCode() == -1) {
// Network error
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load modpacks."));
} else if (network_error_code == 409) {
} else if (failed_action->replyStatusCode() == 409) {
// 409 Gone, notify user to update
QMessageBox::critical(nullptr, tr("Error"),
//: %1 refers to the launcher itself

View file

@ -85,7 +85,7 @@ class ModpackListModel : public QAbstractListModel {
public slots:
void searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr>& doc_all);
void searchRequestFailed(QString reason, int network_error_code);
void searchRequestFailed(QString reason);
void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr);
protected slots:

View file

@ -142,7 +142,7 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o
description_y -= opt.fontMetrics.height();
// On the bottom, aligned to the left after the icon, and featuring at most two lines of text (with some margin space to spare)
painter->drawText(description_x, description_y, remaining_width, num_lines * opt.fontMetrics.height(), Qt::TextWordWrap,
painter->drawText(description_x, description_y, remaining_width, cut_text.size() * opt.fontMetrics.height(), Qt::TextWordWrap,
description);
}

View file

@ -127,9 +127,9 @@ void VersionSelectWidget::closeEvent(QCloseEvent* event)
QWidget::closeEvent(event);
}
void VersionSelectWidget::loadList(bool forceReload)
void VersionSelectWidget::loadList()
{
m_load_task = m_vlist->getLoadTask(forceReload);
m_load_task = m_vlist->getLoadTask();
connect(m_load_task.get(), &Task::succeeded, this, &VersionSelectWidget::onTaskSucceeded);
connect(m_load_task.get(), &Task::failed, this, &VersionSelectWidget::onTaskFailed);
connect(m_load_task.get(), &Task::progress, this, &VersionSelectWidget::changeProgress);

View file

@ -57,7 +57,7 @@ class VersionSelectWidget : public QWidget {
void initialize(BaseVersionList* vlist, bool forceLoad = false);
//! Starts a task that loads the list.
void loadList(bool forceReload = false);
void loadList();
bool hasVersions() const;
BaseVersion::Ptr selectedVersion() const;

View file

@ -1160,6 +1160,8 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page)
m_current_task.reset(download);
connect(download.get(), &Net::Download::finished, this, [this]() {
qDebug() << "Download" << m_current_task->getUid().toString() << "finished";
m_current_task.reset();
m_current_url = "";
});
QCoreApplication::processEvents();

View file

@ -4,7 +4,6 @@
!include "x64.nsh"
AllowSkipFiles off
Unicode true
Name "@Launcher_DisplayName@"