@@ -48,28 +51,48 @@ MSALoginDialog::MSALoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MS
{
ui->setupUi(this);
- ui->cancel->setEnabled(false);
- ui->link->setVisible(false);
- ui->copy->setVisible(false);
- ui->progressBar->setVisible(false);
+ // make font monospace
+ QFont font;
+ font.setPixelSize(ui->code->fontInfo().pixelSize());
+ font.setFamily(APPLICATION->settings()->get("ConsoleFont").toString());
+ font.setStyleHint(QFont::Monospace);
+ font.setFixedPitch(true);
+ ui->code->setFont(font);
- connect(ui->cancel, &QPushButton::pressed, this, &QDialog::reject);
- connect(ui->copy, &QPushButton::pressed, this, &MSALoginDialog::copyUrl);
+ connect(ui->copyCode, &QPushButton::clicked, this, [this] { QApplication::clipboard()->setText(ui->code->text()); });
+ ui->qr->setPixmap(QIcon((":/documents/login-qr.svg")).pixmap(QSize(150, 150)));
+ connect(ui->loginButton, &QPushButton::clicked, this, [this] {
+ if (m_url.isValid()) {
+ if (!DesktopServices::openUrl(m_url)) {
+ QApplication::clipboard()->setText(m_url.toString());
+ }
+ }
+ });
}
int MSALoginDialog::exec()
{
// Setup the login task and start it
m_account = MinecraftAccount::createBlankMSA();
- m_task = m_account->login(m_using_device_code);
- connect(m_task.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed);
- connect(m_task.get(), &Task::succeeded, this, &MSALoginDialog::onTaskSucceeded);
- connect(m_task.get(), &Task::status, this, &MSALoginDialog::onTaskStatus);
- connect(m_task.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser);
- connect(m_task.get(), &AuthFlow::authorizeWithBrowserWithExtra, this, &MSALoginDialog::authorizeWithBrowserWithExtra);
- connect(ui->cancel, &QPushButton::pressed, m_task.get(), &Task::abort);
- connect(&m_external_timer, &QTimer::timeout, this, &MSALoginDialog::externalLoginTick);
- m_task->start();
+ m_authflow_task = m_account->login(false);
+ connect(m_authflow_task.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed);
+ connect(m_authflow_task.get(), &Task::succeeded, this, &QDialog::accept);
+ connect(m_authflow_task.get(), &Task::aborted, this, &MSALoginDialog::reject);
+ connect(m_authflow_task.get(), &Task::status, this, &MSALoginDialog::onTaskStatus);
+ connect(m_authflow_task.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser);
+ connect(m_authflow_task.get(), &AuthFlow::authorizeWithBrowserWithExtra, this, &MSALoginDialog::authorizeWithBrowserWithExtra);
+ connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_authflow_task.get(), &Task::abort);
+
+ m_devicecode_task.reset(new AuthFlow(m_account->accountData(), AuthFlow::Action::DeviceCode, this));
+ connect(m_devicecode_task.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed);
+ connect(m_devicecode_task.get(), &Task::succeeded, this, &QDialog::accept);
+ connect(m_devicecode_task.get(), &Task::aborted, this, &MSALoginDialog::reject);
+ connect(m_devicecode_task.get(), &Task::status, this, &MSALoginDialog::onTaskStatus);
+ connect(m_devicecode_task.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser);
+ connect(m_devicecode_task.get(), &AuthFlow::authorizeWithBrowserWithExtra, this, &MSALoginDialog::authorizeWithBrowserWithExtra);
+ connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_devicecode_task.get(), &Task::abort);
+ QMetaObject::invokeMethod(m_authflow_task.get(), &Task::start, Qt::QueuedConnection);
+ QMetaObject::invokeMethod(m_devicecode_task.get(), &Task::start, Qt::QueuedConnection);
return QDialog::exec();
}
@@ -79,9 +102,12 @@ MSALoginDialog::~MSALoginDialog()
delete ui;
}
-void MSALoginDialog::onTaskFailed(const QString& reason)
+void MSALoginDialog::onTaskFailed(QString reason)
{
// Set message
+ m_authflow_task->disconnect();
+ m_devicecode_task->disconnect();
+ ui->stackedWidget->setCurrentIndex(0);
auto lines = reason.split('\n');
QString processed;
for (auto line : lines) {
@@ -91,91 +117,53 @@ void MSALoginDialog::onTaskFailed(const QString& reason)
processed += "
";
}
}
- ui->message->setText(processed);
-}
-
-void MSALoginDialog::onTaskSucceeded()
-{
- QDialog::accept();
-}
-
-void MSALoginDialog::onTaskStatus(const QString& status)
-{
- ui->message->setText(status);
- ui->cancel->setEnabled(false);
- ui->link->setVisible(false);
- ui->copy->setVisible(false);
- ui->progressBar->setVisible(false);
-}
-
-// Public interface
-MinecraftAccountPtr MSALoginDialog::newAccount(QWidget* parent, QString msg, bool usingDeviceCode)
-{
- MSALoginDialog dlg(parent);
- dlg.m_using_device_code = usingDeviceCode;
- dlg.ui->message->setText(msg);
- if (dlg.exec() == QDialog::Accepted) {
- return dlg.m_account;
+ ui->status->setText(processed);
+ auto task = m_authflow_task;
+ if (task->failReason().isEmpty()) {
+ task = m_devicecode_task;
}
- return nullptr;
+ if (task) {
+ ui->loadingLabel->setText(task->getStatus());
+ }
+ disconnect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_authflow_task.get(), &Task::abort);
+ disconnect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_devicecode_task.get(), &Task::abort);
+ connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &MSALoginDialog::reject);
}
void MSALoginDialog::authorizeWithBrowser(const QUrl& url)
{
- ui->cancel->setEnabled(true);
- ui->link->setVisible(true);
- ui->copy->setVisible(true);
- DesktopServices::openUrl(url);
- ui->link->setText(url.toDisplayString());
- ui->message->setText(
- tr("Browser opened to complete the login process."
- "
"
- "If your browser hasn't opened, please manually open the below link in your browser:"));
-}
-
-void MSALoginDialog::copyUrl()
-{
- QClipboard* cb = QApplication::clipboard();
- cb->setText(ui->link->text());
+ ui->stackedWidget->setCurrentIndex(1);
+ ui->loginButton->setToolTip(QString("%1
").arg(url.toString()));
+ m_url = url;
}
void MSALoginDialog::authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn)
{
- m_external_elapsed = 0;
- m_external_timeout = expiresIn;
+ ui->stackedWidget->setCurrentIndex(1);
- m_external_timer.setInterval(1000);
- m_external_timer.setSingleShot(false);
- m_external_timer.start();
-
- ui->progressBar->setMaximum(expiresIn);
- ui->progressBar->setValue(m_external_elapsed);
-
- QString linkString = QString("%2").arg(url, url);
- if (url == "https://www.microsoft.com/link" && !code.isEmpty()) {
- url += QString("?otc=%1").arg(code);
- ui->message->setText(tr("Please login in the opened browser. If no browser was opened, please open up %1 in "
- "a browser and put in the code %2 to proceed with login.
")
- .arg(linkString, code));
+ const auto linkString = QString("%2").arg(url, url);
+ ui->code->setText(code);
+ auto isDefaultUrl = url == "https://www.microsoft.com/link";
+ ui->qr->setVisible(isDefaultUrl);
+ if (isDefaultUrl) {
+ ui->qrMessage->setText(tr("Open %1 or scan the QR and enter the above code.").arg(linkString));
} else {
- ui->message->setText(
- tr("Please open up %1 in a browser and put in the code %2 to proceed with login.
").arg(linkString, code));
+ ui->qrMessage->setText(tr("Open %1 and enter the above code.").arg(linkString));
}
- ui->cancel->setEnabled(true);
- ui->link->setVisible(true);
- ui->copy->setVisible(true);
- ui->progressBar->setVisible(true);
- DesktopServices::openUrl(url);
- ui->link->setText(code);
}
-void MSALoginDialog::externalLoginTick()
+void MSALoginDialog::onTaskStatus(QString status)
{
- m_external_elapsed++;
- ui->progressBar->setValue(m_external_elapsed);
- ui->progressBar->repaint();
+ ui->stackedWidget->setCurrentIndex(0);
+ ui->status->setText(status);
+}
- if (m_external_elapsed >= m_external_timeout) {
- m_external_timer.stop();
+// Public interface
+MinecraftAccountPtr MSALoginDialog::newAccount(QWidget* parent)
+{
+ MSALoginDialog dlg(parent);
+ if (dlg.exec() == QDialog::Accepted) {
+ return dlg.m_account;
}
+ return nullptr;
}
\ No newline at end of file
diff --git a/launcher/ui/dialogs/MSALoginDialog.h b/launcher/ui/dialogs/MSALoginDialog.h
index cef647ee4..70f480ca9 100644
--- a/launcher/ui/dialogs/MSALoginDialog.h
+++ b/launcher/ui/dialogs/MSALoginDialog.h
@@ -32,29 +32,23 @@ class MSALoginDialog : public QDialog {
public:
~MSALoginDialog();
- static MinecraftAccountPtr newAccount(QWidget* parent, QString message, bool usingDeviceCode = false);
+ static MinecraftAccountPtr newAccount(QWidget* parent);
int exec() override;
private:
explicit MSALoginDialog(QWidget* parent = 0);
protected slots:
- void onTaskFailed(const QString& reason);
- void onTaskSucceeded();
- void onTaskStatus(const QString& status);
+ void onTaskFailed(QString reason);
+ void onTaskStatus(QString status);
void authorizeWithBrowser(const QUrl& url);
void authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn);
- void copyUrl();
- void externalLoginTick();
private:
Ui::MSALoginDialog* ui;
MinecraftAccountPtr m_account;
- shared_qobject_ptr m_task;
+ shared_qobject_ptr m_devicecode_task;
+ shared_qobject_ptr m_authflow_task;
- int m_external_elapsed;
- int m_external_timeout;
- QTimer m_external_timer;
-
- bool m_using_device_code = false;
+ QUrl m_url;
};
diff --git a/launcher/ui/dialogs/MSALoginDialog.ui b/launcher/ui/dialogs/MSALoginDialog.ui
index df1b32044..c6821782f 100644
--- a/launcher/ui/dialogs/MSALoginDialog.ui
+++ b/launcher/ui/dialogs/MSALoginDialog.ui
@@ -6,87 +6,345 @@
0
0
- 491
- 208
+ 440
+ 430
-
-
- 0
- 0
-
+
+
+ 0
+ 430
+
Add Microsoft Account
-
+
-
-
-
-
- 0
- 0
-
-
-
-
- 500
- 500
-
-
-
-
-
-
- Qt::RichText
-
-
- true
-
-
- true
-
-
- Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+
+ 1
+
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+
+ 16
+ 75
+ true
+
+
+
+ Please wait...
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+ -
+
+
+ Status
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 250
+ 40
+
+
+
+ Sign in with Microsoft
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 16
+
+
+
+ Or
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 150
+ 150
+
+
+
+
+ 150
+ 150
+
+
+
+
+
+
+ true
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 30
+ 75
+ true
+
+
+
+ IBeamCursor
+
+
+ CODE
+
+
+ Qt::AlignCenter
+
+
+ Qt::TextBrowserInteraction
+
+
+
+ -
+
+
+ Copy code to clipboard
+
+
+
+
+
+
+ ..
+
+
+
+ 22
+ 22
+
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Info
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+ true
+
+
+ Qt::TextBrowserInteraction
+
+
+
+
+
-
-
-
-
-
-
- false
-
-
-
- -
-
-
-
-
-
-
- ..
-
-
-
-
-
- -
-
-
- 24
-
-
- false
-
-
-
- -
-
-
- Cancel
+
+
+ QDialogButtonBox::Cancel
diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp
index 3d04aca6b..f906cfcea 100644
--- a/launcher/ui/dialogs/ModUpdateDialog.cpp
+++ b/launcher/ui/dialogs/ModUpdateDialog.cpp
@@ -1,4 +1,5 @@
#include "ModUpdateDialog.h"
+#include "Application.h"
#include "ChooseProviderDialog.h"
#include "CustomMessageBox.h"
#include "ProgressDialog.h"
@@ -30,9 +31,9 @@ static std::list mcVersions(BaseInstance* inst)
return { static_cast(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() };
}
-static std::optional mcLoaders(BaseInstance* inst)
+static QList mcLoadersList(BaseInstance* inst)
{
- return { static_cast(inst)->getPackProfile()->getSupportedModLoaders() };
+ return static_cast(inst)->getPackProfile()->getModLoadersList();
}
ModUpdateDialog::ModUpdateDialog(QWidget* parent,
@@ -86,19 +87,19 @@ void ModUpdateDialog::checkCandidates()
}
auto versions = mcVersions(m_instance);
- auto loaders = mcLoaders(m_instance);
+ auto loadersList = mcLoadersList(m_instance);
SequentialTask check_task(m_parent, tr("Checking for updates"));
if (!m_modrinth_to_update.empty()) {
- m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model));
+ m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loadersList, m_mod_model));
connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this,
[this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({ mod, reason, recover_url }); });
check_task.addTask(m_modrinth_check_task);
}
if (!m_flame_to_update.empty()) {
- m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model));
+ m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loadersList, m_mod_model));
connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this,
[this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({ mod, reason, recover_url }); });
check_task.addTask(m_flame_check_task);
diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp
index 082b16101..90d3b79a9 100644
--- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp
+++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp
@@ -145,6 +145,7 @@ void ResourceDownloadDialog::confirm()
confirm_dialog->retranslateUi(resourcesString());
QHash dependencyExtraInfo;
+ QStringList depNames;
if (auto task = getModDependenciesTask(); task) {
connect(task.get(), &Task::failed, this,
[&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
@@ -167,8 +168,10 @@ void ResourceDownloadDialog::confirm()
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
return;
} else {
- for (auto dep : task->getDependecies())
+ for (auto dep : task->getDependecies()) {
addResource(dep->pack, dep->version);
+ depNames << dep->pack->name;
+ }
dependencyExtraInfo = task->getExtraInfo();
}
}
@@ -193,6 +196,9 @@ void ResourceDownloadDialog::confirm()
}
this->accept();
+ } else {
+ for (auto name : depNames)
+ removeResource(name);
}
}
diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui
index 93591e440..a7f3f3f72 100644
--- a/launcher/ui/pages/global/APIPage.ui
+++ b/launcher/ui/pages/global/APIPage.ui
@@ -207,7 +207,7 @@
-
- <html><head/><body><p>Note: you only need to set this to access private data. Read the <a href="https://docs.modrinth.com/api-spec/#section/Authentication">documentation</a> for more information.</p></body></html>
+ <html><head/><body><p>Note: you only need to set this to access private data. Read the <a href="https://docs.modrinth.com/#section/Authentication">documentation</a> for more information.</p></body></html>
true
diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp
index 8d0b66004..041b8faff 100644
--- a/launcher/ui/pages/global/AccountListPage.cpp
+++ b/launcher/ui/pages/global/AccountListPage.cpp
@@ -130,20 +130,7 @@ void AccountListPage::listChanged()
void AccountListPage::on_actionAddMicrosoft_triggered()
{
- QMessageBox box(this);
- box.setWindowTitle(tr("Add account"));
- box.setText(tr("How do you want to login?"));
- box.setIcon(QMessageBox::Question);
- auto deviceCode = box.addButton(tr("Legacy"), QMessageBox::ButtonRole::YesRole);
- auto authCode = box.addButton(tr("Recommended"), QMessageBox::ButtonRole::NoRole);
- auto cancel = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::RejectRole);
- box.setDefaultButton(authCode);
- box.exec();
- if ((box.clickedButton() != deviceCode && box.clickedButton() != authCode) || box.clickedButton() == cancel)
- return;
- MinecraftAccountPtr account = MSALoginDialog::newAccount(
- this, tr("Please enter your Mojang account email and password to add your account."), box.clickedButton() == deviceCode);
-
+ auto account = MSALoginDialog::newAccount(this);
if (account) {
m_accounts->addAccount(account);
if (m_accounts->count() == 1) {
diff --git a/launcher/ui/pages/global/AccountListPage.h b/launcher/ui/pages/global/AccountListPage.h
index dd49a95d6..4f02b7df5 100644
--- a/launcher/ui/pages/global/AccountListPage.h
+++ b/launcher/ui/pages/global/AccountListPage.h
@@ -66,7 +66,7 @@ class AccountListPage : public QMainWindow, public BasePage {
return icon;
}
QString id() const override { return "accounts"; }
- QString helpPage() const override { return "Getting-Started#adding-an-account"; }
+ QString helpPage() const override { return "/getting-started/adding-an-account"; }
void retranslate() override;
public slots:
diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp
index 04e27fd76..6a240389a 100644
--- a/launcher/ui/pages/global/LauncherPage.cpp
+++ b/launcher/ui/pages/global/LauncherPage.cpp
@@ -203,6 +203,8 @@ void LauncherPage::applySettings()
s->set("NumberOfConcurrentTasks", ui->numberOfConcurrentTasksSpinBox->value());
s->set("NumberOfConcurrentDownloads", ui->numberOfConcurrentDownloadsSpinBox->value());
+ s->set("NumberOfManualRetries", ui->numberOfManualRetriesSpinBox->value());
+ s->set("RequestTimeout", ui->timeoutSecondsSpinBox->value());
// Console settings
s->set("ShowConsole", ui->showConsoleCheck->isChecked());
@@ -240,6 +242,7 @@ void LauncherPage::applySettings()
// Mods
s->set("ModMetadataDisabled", ui->metadataDisableBtn->isChecked());
s->set("ModDependenciesDisabled", ui->dependenciesDisableBtn->isChecked());
+ s->set("SkipModpackUpdatePrompt", ui->skipModpackUpdatePromptBtn->isChecked());
}
void LauncherPage::loadSettings()
{
@@ -259,6 +262,8 @@ void LauncherPage::loadSettings()
ui->numberOfConcurrentTasksSpinBox->setValue(s->get("NumberOfConcurrentTasks").toInt());
ui->numberOfConcurrentDownloadsSpinBox->setValue(s->get("NumberOfConcurrentDownloads").toInt());
+ ui->numberOfManualRetriesSpinBox->setValue(s->get("NumberOfManualRetries").toInt());
+ ui->timeoutSecondsSpinBox->setValue(s->get("RequestTimeout").toInt());
// Console settings
ui->showConsoleCheck->setChecked(s->get("ShowConsole").toBool());
@@ -301,6 +306,7 @@ void LauncherPage::loadSettings()
ui->metadataDisableBtn->setChecked(s->get("ModMetadataDisabled").toBool());
ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked());
ui->dependenciesDisableBtn->setChecked(s->get("ModDependenciesDisabled").toBool());
+ ui->skipModpackUpdatePromptBtn->setChecked(s->get("SkipModpackUpdatePrompt").toBool());
}
void LauncherPage::refreshFontPreview()
diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui
index 2d96c3a84..72039488b 100644
--- a/launcher/ui/pages/global/LauncherPage.ui
+++ b/launcher/ui/pages/global/LauncherPage.ui
@@ -7,7 +7,7 @@
0
0
511
- 629
+ 691
@@ -243,6 +243,16 @@
+ -
+
+
+ When creating a new modpack instance, do not suggest updating existing instances instead.
+
+
+ Skip modpack update prompt
+
+
+
@@ -252,6 +262,13 @@
Miscellaneous
+ -
+
+
+ 1
+
+
+
-
@@ -273,10 +290,34 @@
- -
-
+
-
+
+
+ Number of manual retries
+
+
+
+ -
+
- 1
+ 0
+
+
+
+ -
+
+
+ Seconds to wait until the requests are terminated
+
+
+ Timeout for HTTP requests
+
+
+
+ -
+
+
+ s
diff --git a/launcher/ui/pages/instance/ManagedPackPage.h b/launcher/ui/pages/instance/ManagedPackPage.h
index fe5a200de..c44f77070 100644
--- a/launcher/ui/pages/instance/ManagedPackPage.h
+++ b/launcher/ui/pages/instance/ManagedPackPage.h
@@ -119,6 +119,7 @@ class ModrinthManagedPackPage final : public ManagedPackPage {
void parseManagedPack() override;
[[nodiscard]] QString url() const override;
+ [[nodiscard]] QString helpPage() const override { return "modrinth-managed-pack"; }
public slots:
void suggestVersion() override;
@@ -142,6 +143,7 @@ class FlameManagedPackPage final : public ManagedPackPage {
void parseManagedPack() override;
[[nodiscard]] QString url() const override;
+ [[nodiscard]] QString helpPage() const override { return "curseforge-managed-pack"; }
public slots:
void suggestVersion() override;
diff --git a/launcher/ui/pages/instance/OtherLogsPage.h b/launcher/ui/pages/instance/OtherLogsPage.h
index de42f5a23..85a3a2dbc 100644
--- a/launcher/ui/pages/instance/OtherLogsPage.h
+++ b/launcher/ui/pages/instance/OtherLogsPage.h
@@ -57,7 +57,7 @@ class OtherLogsPage : public QWidget, public BasePage {
QString id() const override { return "logs"; }
QString displayName() const override { return tr("Other logs"); }
QIcon icon() const override { return APPLICATION->getThemedIcon("log"); }
- QString helpPage() const override { return "Minecraft-Logs"; }
+ QString helpPage() const override { return "other-Logs"; }
void retranslate() override;
void openedImpl() override;
diff --git a/launcher/ui/pages/instance/ShaderPackPage.h b/launcher/ui/pages/instance/ShaderPackPage.h
index 7c43a3756..d134e67ad 100644
--- a/launcher/ui/pages/instance/ShaderPackPage.h
+++ b/launcher/ui/pages/instance/ShaderPackPage.h
@@ -48,7 +48,7 @@ class ShaderPackPage : public ExternalResourcesPage {
QString displayName() const override { return tr("Shader packs"); }
QIcon icon() const override { return APPLICATION->getThemedIcon("shaderpacks"); }
QString id() const override { return "shaderpacks"; }
- QString helpPage() const override { return "Resource-packs"; }
+ QString helpPage() const override { return "shader-packs"; }
bool shouldDisplay() const override { return true; }
diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp
index 38208f5b0..1efc6199e 100644
--- a/launcher/ui/pages/modplatform/ImportPage.cpp
+++ b/launcher/ui/pages/modplatform/ImportPage.cpp
@@ -104,7 +104,7 @@ void ImportPage::updateState()
return;
}
if (ui->modpackEdit->hasAcceptableInput()) {
- QString input = ui->modpackEdit->text();
+ QString input = ui->modpackEdit->text().trimmed();
auto url = QUrl::fromUserInput(input);
if (url.isLocalFile()) {
// FIXME: actually do some validation of what's inside here... this is fake AF
diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp
index e85bba9c4..c8eb91570 100644
--- a/launcher/ui/pages/modplatform/ResourceModel.cpp
+++ b/launcher/ui/pages/modplatform/ResourceModel.cpp
@@ -317,8 +317,10 @@ std::optional ResourceModel::getIcon(QModelIndex& index, const QUrl& url)
if (QPixmapCache::find(url.toString(), &pixmap))
return { pixmap };
- if (!m_current_icon_job)
+ if (!m_current_icon_job) {
m_current_icon_job.reset(new NetJob("IconJob", APPLICATION->network()));
+ m_current_icon_job->setAskRetry(false);
+ }
if (m_currently_running_icon_actions.contains(url))
return {};
diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.h b/launcher/ui/pages/modplatform/ResourcePackPage.h
index 6015aec0b..440d91ab0 100644
--- a/launcher/ui/pages/modplatform/ResourcePackPage.h
+++ b/launcher/ui/pages/modplatform/ResourcePackPage.h
@@ -40,6 +40,8 @@ class ResourcePackResourcePage : public ResourcePage {
[[nodiscard]] QMap urlHandlers() const override;
+ [[nodiscard]] inline auto helpPage() const -> QString override { return "resourcepack-platform"; }
+
protected:
ResourcePackResourcePage(ResourceDownloadDialog* dialog, BaseInstance& instance);
diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.h b/launcher/ui/pages/modplatform/ShaderPackPage.h
index c29317e15..4b92c33dc 100644
--- a/launcher/ui/pages/modplatform/ShaderPackPage.h
+++ b/launcher/ui/pages/modplatform/ShaderPackPage.h
@@ -42,6 +42,8 @@ class ShaderPackResourcePage : public ResourcePage {
[[nodiscard]] QMap urlHandlers() const override;
+ [[nodiscard]] inline auto helpPage() const -> QString override { return "shaderpack-platform"; }
+
protected:
ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance);
diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp
index d46b97af1..f116ca915 100644
--- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp
+++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp
@@ -195,6 +195,7 @@ void ListModel::requestLogo(QString file, QString url)
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file));
auto job = new NetJob(QString("ATLauncher Icon Download %1").arg(file), APPLICATION->network());
+ job->setAskRetry(false);
job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry));
auto fullPath = entry->getFullPath();
diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp
index 3b266bcef..a92d5b579 100644
--- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp
+++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp
@@ -110,6 +110,7 @@ void ListModel::requestLogo(QString logo, QString url)
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo));
auto job = new NetJob(QString("Flame Icon Download %1").arg(logo), APPLICATION->network());
+ job->setAskRetry(false);
job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry));
auto fullPath = entry->getFullPath();
@@ -172,7 +173,7 @@ void ListModel::performPaginatedSearch()
callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); };
callbacks.on_abort = [this] {
qCritical() << "Search task aborted by an unknown reason!";
- searchRequestFailed("Abborted");
+ searchRequestFailed("Aborted");
};
static const FlameAPI api;
if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) {
diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h
index 8e9661272..00f013f6f 100644
--- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h
+++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h
@@ -44,7 +44,7 @@ class ImportFTBPage : public QWidget, public BasePage {
QString displayName() const override { return tr("FTB App Import"); }
QIcon icon() const override { return APPLICATION->getThemedIcon("ftb_logo"); }
QString id() const override { return "import_ftb"; }
- QString helpPage() const override { return "FTB-platform"; }
+ QString helpPage() const override { return "FTB-import"; }
bool shouldDisplay() const override { return true; }
void openedImpl() override;
void retranslate() override;
diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp
index 49666cf6e..98922123c 100644
--- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp
+++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp
@@ -264,6 +264,7 @@ void ListModel::requestLogo(QString file)
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file));
NetJob* job = new NetJob(QString("FTB Icon Download for %1").arg(file), APPLICATION->network());
+ job->setAskRetry(false);
job->addNetAction(Net::ApiDownload::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry));
auto fullPath = entry->getFullPath();
diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.h b/launcher/ui/pages/modplatform/legacy_ftb/Page.h
index 4d317b7c0..daef23342 100644
--- a/launcher/ui/pages/modplatform/legacy_ftb/Page.h
+++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.h
@@ -66,7 +66,7 @@ class Page : public QWidget, public BasePage {
QString displayName() const override { return "FTB Legacy"; }
QIcon icon() const override { return APPLICATION->getThemedIcon("ftb_logo"); }
QString id() const override { return "legacy_ftb"; }
- QString helpPage() const override { return "FTB-platform"; }
+ QString helpPage() const override { return "FTB-legacy"; }
bool shouldDisplay() const override;
void openedImpl() override;
void retranslate() override;
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
index ccfe7eccb..b53eea4ef 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
@@ -254,6 +254,7 @@ void ModpackListModel::requestLogo(QString logo, QString url)
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo));
auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network());
+ job->setAskRetry(false);
job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry));
auto fullPath = entry->getFullPath();
diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
index 6f1810d71..4181edab6 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
+++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
@@ -292,6 +292,7 @@ void Technic::ListModel::requestLogo(QString logo, QString url)
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("TechnicPacks", QString("logos/%1").arg(logo));
auto job = new NetJob(QString("Technic Icon Download %1").arg(logo), APPLICATION->network());
+ job->setAskRetry(false);
job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry));
auto fullPath = entry->getFullPath();
diff --git a/launcher/ui/themes/BrightTheme.cpp b/launcher/ui/themes/BrightTheme.cpp
index ffccdaab1..39a5bfd14 100644
--- a/launcher/ui/themes/BrightTheme.cpp
+++ b/launcher/ui/themes/BrightTheme.cpp
@@ -1,3 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2024 Tayou
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#include "BrightTheme.h"
#include
@@ -55,3 +89,7 @@ QString BrightTheme::appStyleSheet()
{
return QString();
}
+QString BrightTheme::tooltip()
+{
+ return QString();
+}
diff --git a/launcher/ui/themes/BrightTheme.h b/launcher/ui/themes/BrightTheme.h
index 44a767492..750e7bfc5 100644
--- a/launcher/ui/themes/BrightTheme.h
+++ b/launcher/ui/themes/BrightTheme.h
@@ -1,3 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2024 Tayou
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#pragma once
#include "FusionTheme.h"
@@ -8,6 +42,7 @@ class BrightTheme : public FusionTheme {
QString id() override;
QString name() override;
+ QString tooltip() override;
bool hasStyleSheet() override;
QString appStyleSheet() override;
bool hasColorScheme() override;
diff --git a/launcher/ui/themes/CustomTheme.cpp b/launcher/ui/themes/CustomTheme.cpp
index 4859983c6..22b366b62 100644
--- a/launcher/ui/themes/CustomTheme.cpp
+++ b/launcher/ui/themes/CustomTheme.cpp
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 Tayou
+ * Copyright (C) 2024 Tayou
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -285,3 +285,7 @@ QString CustomTheme::qtTheme()
{
return m_widgets;
}
+QString CustomTheme::tooltip()
+{
+ return m_tooltip;
+}
diff --git a/launcher/ui/themes/CustomTheme.h b/launcher/ui/themes/CustomTheme.h
index 3ec4cafa2..761a2bd90 100644
--- a/launcher/ui/themes/CustomTheme.h
+++ b/launcher/ui/themes/CustomTheme.h
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 Tayou
+ * Copyright (C) 2024 Tayou
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -44,6 +44,7 @@ class CustomTheme : public ITheme {
QString id() override;
QString name() override;
+ QString tooltip() override;
bool hasStyleSheet() override;
QString appStyleSheet() override;
bool hasColorScheme() override;
@@ -62,4 +63,10 @@ class CustomTheme : public ITheme {
QString m_id;
QString m_widgets;
QString m_qssFilePath;
+ /**
+ * The tooltip could be defined in the theme json,
+ * or composed of other fields that could be in there.
+ * like author, license, etc.
+ */
+ QString m_tooltip = "";
};
diff --git a/launcher/ui/themes/DarkTheme.cpp b/launcher/ui/themes/DarkTheme.cpp
index c3a68a2d4..429d046ac 100644
--- a/launcher/ui/themes/DarkTheme.cpp
+++ b/launcher/ui/themes/DarkTheme.cpp
@@ -1,3 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2024 Tayou
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#include "DarkTheme.h"
#include
@@ -56,3 +90,7 @@ QString DarkTheme::appStyleSheet()
{
return "QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }";
}
+QString DarkTheme::tooltip()
+{
+ return "";
+}
diff --git a/launcher/ui/themes/DarkTheme.h b/launcher/ui/themes/DarkTheme.h
index 431e9a735..819f6a934 100644
--- a/launcher/ui/themes/DarkTheme.h
+++ b/launcher/ui/themes/DarkTheme.h
@@ -1,3 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2024 Tayou
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#pragma once
#include "FusionTheme.h"
@@ -8,6 +42,7 @@ class DarkTheme : public FusionTheme {
QString id() override;
QString name() override;
+ QString tooltip() override;
bool hasStyleSheet() override;
QString appStyleSheet() override;
bool hasColorScheme() override;
diff --git a/launcher/ui/themes/ITheme.h b/launcher/ui/themes/ITheme.h
index d85e7f983..45d3e2739 100644
--- a/launcher/ui/themes/ITheme.h
+++ b/launcher/ui/themes/ITheme.h
@@ -44,6 +44,7 @@ class ITheme {
virtual void apply(bool initial);
virtual QString id() = 0;
virtual QString name() = 0;
+ virtual QString tooltip() = 0;
virtual bool hasStyleSheet() = 0;
virtual QString appStyleSheet() = 0;
virtual QString qtTheme() = 0;
diff --git a/launcher/ui/themes/SystemTheme.cpp b/launcher/ui/themes/SystemTheme.cpp
index cefe664db..70de21894 100644
--- a/launcher/ui/themes/SystemTheme.cpp
+++ b/launcher/ui/themes/SystemTheme.cpp
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 Tayou
+ * Copyright (C) 2024 Tayou
* Copyright (C) 2024 TheKodeToad
*
* This program is free software: you can redistribute it and/or modify
@@ -35,31 +35,16 @@
*/
#include "SystemTheme.h"
#include
-#include
#include
#include
#include "HintOverrideProxyStyle.h"
#include "ThemeManager.h"
-SystemTheme::SystemTheme()
+SystemTheme::SystemTheme(QString& styleName, bool isSystemTheme)
{
- themeDebugLog() << "Determining System Theme...";
- const auto& style = QApplication::style();
- systemPalette = QApplication::palette();
- QString lowerThemeName = style->objectName();
- themeDebugLog() << "System theme seems to be:" << lowerThemeName;
- QStringList styles = QStyleFactory::keys();
- for (auto& st : styles) {
- themeDebugLog() << "Considering theme from theme factory:" << st.toLower();
- if (st.toLower() == lowerThemeName) {
- systemTheme = st;
- themeDebugLog() << "System theme has been determined to be:" << systemTheme;
- return;
- }
- }
- // fall back to fusion if we can't find the current theme.
- systemTheme = "Fusion";
- themeDebugLog() << "System theme not found, defaulted to Fusion";
+ themeName = isSystemTheme ? "system" : styleName;
+ widgetTheme = styleName;
+ colorPalette = QApplication::palette();
}
void SystemTheme::apply(bool initial)
@@ -76,22 +61,49 @@ void SystemTheme::apply(bool initial)
QString SystemTheme::id()
{
- return "system";
+ return themeName;
}
QString SystemTheme::name()
{
- return QObject::tr("System");
+ if (themeName.toLower() == "windowsvista") {
+ return QObject::tr("Windows Vista");
+ } else if (themeName.toLower() == "windows") {
+ return QObject::tr("Windows 9x");
+ } else if (themeName.toLower() == "windows11") {
+ return QObject::tr("Windows 11");
+ } else if (themeName.toLower() == "system") {
+ return QObject::tr("System");
+ } else {
+ return themeName;
+ }
+}
+
+QString SystemTheme::tooltip()
+{
+ if (themeName.toLower() == "windowsvista") {
+ return QObject::tr("Widget style trying to look like your win32 theme");
+ } else if (themeName.toLower() == "windows") {
+ return QObject::tr("Windows 9x inspired widget style");
+ } else if (themeName.toLower() == "windows11") {
+ return QObject::tr("WinUI 3 inspired Qt widget style");
+ } else if (themeName.toLower() == "fusion") {
+ return QObject::tr("The default Qt widget style");
+ } else if (themeName.toLower() == "system") {
+ return QObject::tr("Your current system theme");
+ } else {
+ return "";
+ }
}
QString SystemTheme::qtTheme()
{
- return systemTheme;
+ return widgetTheme;
}
QPalette SystemTheme::colorScheme()
{
- return systemPalette;
+ return colorPalette;
}
QString SystemTheme::appStyleSheet()
diff --git a/launcher/ui/themes/SystemTheme.h b/launcher/ui/themes/SystemTheme.h
index 4f7d83e57..5c58856cb 100644
--- a/launcher/ui/themes/SystemTheme.h
+++ b/launcher/ui/themes/SystemTheme.h
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 Tayou
+ * Copyright (C) 2024 Tayou
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -38,12 +38,13 @@
class SystemTheme : public ITheme {
public:
- SystemTheme();
+ SystemTheme(QString& themeName, bool isSystemTheme = false);
virtual ~SystemTheme() {}
void apply(bool initial) override;
QString id() override;
QString name() override;
+ QString tooltip() override;
QString qtTheme() override;
bool hasStyleSheet() override;
QString appStyleSheet() override;
@@ -53,6 +54,7 @@ class SystemTheme : public ITheme {
QColor fadeColor() override;
private:
- QPalette systemPalette;
- QString systemTheme;
+ QPalette colorPalette;
+ QString widgetTheme;
+ QString themeName;
};
diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp
index 1cb83ca26..d57e166f4 100644
--- a/launcher/ui/themes/ThemeManager.cpp
+++ b/launcher/ui/themes/ThemeManager.cpp
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 Tayou
+ * Copyright (C) 2024 Tayou
* Copyright (C) 2023 TheKodeToad
*
* This program is free software: you can redistribute it and/or modify
@@ -23,6 +23,8 @@
#include
#include
#include
+#include
+#include
#include "Exception.h"
#include "ui/themes/BrightTheme.h"
#include "ui/themes/CatPack.h"
@@ -119,14 +121,30 @@ void ThemeManager::initializeIcons()
void ThemeManager::initializeWidgets()
{
+ themeDebugLog() << "Determining System Widget Theme...";
+ const auto& style = QApplication::style();
+ currentlySelectedSystemTheme = style->objectName();
+ themeDebugLog() << "System theme seems to be:" << currentlySelectedSystemTheme;
+
themeDebugLog() << "<> Initializing Widget Themes";
- themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique());
+ themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique(currentlySelectedSystemTheme, true));
auto darkThemeId = addTheme(std::make_unique());
themeDebugLog() << "Loading Built-in Theme:" << darkThemeId;
themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique());
- // TODO: need some way to differentiate same name themes in different subdirectories (maybe smaller grey text next to theme name in
- // dropdown?)
+ themeDebugLog() << "<> Initializing System Widget Themes";
+ QStringList styles = QStyleFactory::keys();
+ for (auto& st : styles) {
+#ifdef Q_OS_WINDOWS
+ if (QSysInfo::productVersion() != "11" && st == "windows11") {
+ continue;
+ }
+#endif
+ themeDebugLog() << "Loading System Theme:" << addTheme(std::make_unique(st));
+ }
+
+ // TODO: need some way to differentiate same name themes in different subdirectories
+ // (maybe smaller grey text next to theme name in dropdown?)
if (!m_applicationThemeFolder.mkpath("."))
themeWarningLog() << "Couldn't create theme folder";
@@ -238,7 +256,11 @@ void ThemeManager::applyCurrentlySelectedTheme(bool initial)
auto settings = APPLICATION->settings();
setIconTheme(settings->get("IconTheme").toString());
themeDebugLog() << "<> Icon theme set.";
- setApplicationTheme(settings->get("ApplicationTheme").toString(), initial);
+ auto applicationTheme = settings->get("ApplicationTheme").toString();
+ if (applicationTheme == "") {
+ applicationTheme = currentlySelectedSystemTheme;
+ }
+ setApplicationTheme(applicationTheme, initial);
themeDebugLog() << "<> Application theme set.";
}
diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h
index 47b9589e3..9d01d38e7 100644
--- a/launcher/ui/themes/ThemeManager.h
+++ b/launcher/ui/themes/ThemeManager.h
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 Tayou
+ * Copyright (C) 2024 Tayou
* Copyright (C) 2023 TheKodeToad
*
* This program is free software: you can redistribute it and/or modify
@@ -64,6 +64,7 @@ class ThemeManager {
QDir m_applicationThemeFolder{ "themes" };
QDir m_catPacksFolder{ "catpacks" };
std::map> m_cat_packs;
+ QString currentlySelectedSystemTheme;
void initializeThemes();
void initializeCatPacks();
diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp
index 9c5df0067..7a54bd390 100644
--- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp
+++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 Tayou
+ * Copyright (C) 2024 Tayou
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -151,6 +151,10 @@ void ThemeCustomizationWidget::loadSettings()
int idx = 0;
for (auto& theme : themes) {
ui->widgetStyleComboBox->addItem(theme->name(), theme->id());
+ if (theme->tooltip() != "") {
+ int index = ui->widgetStyleComboBox->count() - 1;
+ ui->widgetStyleComboBox->setItemData(index, theme->tooltip(), Qt::ToolTipRole);
+ }
if (currentTheme == theme->id()) {
ui->widgetStyleComboBox->setCurrentIndex(idx);
}
diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp
index 3dd9d5634..9723a2c56 100644
--- a/launcher/ui/widgets/VariableSizedImageObject.cpp
+++ b/launcher/ui/widgets/VariableSizedImageObject.cpp
@@ -80,7 +80,7 @@ void VariableSizedImageObject::drawObject(QPainter* painter,
{
if (!format.hasProperty(ImageData)) {
QUrl image_url{ qvariant_cast(format.property(QTextFormat::ImageName)) };
- if (m_fetching_images.contains(image_url))
+ if (m_fetching_images.contains(image_url) || image_url.isEmpty())
return;
auto meta = std::make_shared();
@@ -140,6 +140,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, std::shared_ptrurl.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex())));
auto job = new NetJob(QString("Load Image: %1").arg(meta->url.fileName()), APPLICATION->network());
+ job->setAskRetry(false);
job->addNetAction(Net::ApiDownload::makeCached(meta->url, entry));
auto full_entry_path = entry->getFullPath();
diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp
index dc1aae872..69774dc04 100644
--- a/launcher/updater/PrismExternalUpdater.cpp
+++ b/launcher/updater/PrismExternalUpdater.cpp
@@ -85,6 +85,11 @@ PrismExternalUpdater::~PrismExternalUpdater()
}
void PrismExternalUpdater::checkForUpdates()
+{
+ checkForUpdates(true);
+}
+
+void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
{
QProgressDialog progress(tr("Checking for updates..."), "", 0, 0, priv->parent);
progress.setCancelButton(nullptr);
@@ -160,7 +165,7 @@ void PrismExternalUpdater::checkForUpdates()
switch (exit_code) {
case 0:
// no update available
- {
+ if (triggeredByUser) {
qDebug() << "No update available";
auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version."),
QMessageBox::Ok, priv->parent);
@@ -288,7 +293,7 @@ void PrismExternalUpdater::disconnectTimer()
void PrismExternalUpdater::autoCheckTimerFired()
{
qDebug() << "Auto update Timer fired";
- checkForUpdates();
+ checkForUpdates(false);
}
void PrismExternalUpdater::offerUpdate(const QString& version_name, const QString& version_tag, const QString& release_notes)
diff --git a/launcher/updater/PrismExternalUpdater.h b/launcher/updater/PrismExternalUpdater.h
index bfe94c149..b88676028 100644
--- a/launcher/updater/PrismExternalUpdater.h
+++ b/launcher/updater/PrismExternalUpdater.h
@@ -41,6 +41,7 @@ class PrismExternalUpdater : public ExternalUpdater {
* Check for updates manually, showing the user a progress bar and an alert if no updates are found.
*/
void checkForUpdates() override;
+ void checkForUpdates(bool triggeredByUser);
/*!
* Indicates whether or not to check for updates automatically.
diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp
index 83d8efa22..6b9754864 100644
--- a/launcher/updater/prismupdater/PrismUpdater.cpp
+++ b/launcher/updater/prismupdater/PrismUpdater.cpp
@@ -244,8 +244,9 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
auto updater_executable = QCoreApplication::applicationFilePath();
- if (BuildConfig.BUILD_ARTIFACT.toLower() == "macos")
- showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support installations on MacOS"));
+#ifdef Q_OS_MACOS
+ showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support installations on MacOS"));
+#endif
if (updater_executable.startsWith("/tmp/.mount_")) {
m_isAppimage = true;
@@ -327,6 +328,19 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
// on command line
adjustedBy = "Command line";
m_dataPath = dirParam;
+#ifndef Q_OS_MACOS
+ if (QDir(FS::PathCombine(m_rootPath, "UserData")).exists()) {
+ m_isPortable = true;
+ }
+ if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
+ m_isPortable = true;
+ }
+#endif
+ } else if (auto dataDirEnv =
+ QProcessEnvironment::systemEnvironment().value(QString("%1_DATA_DIR").arg(BuildConfig.LAUNCHER_NAME.toUpper()));
+ !dataDirEnv.isEmpty()) {
+ adjustedBy = "System environment";
+ m_dataPath = dataDirEnv;
#ifndef Q_OS_MACOS
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
m_isPortable = true;
@@ -338,7 +352,11 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
adjustedBy = "Persistent data path";
#ifndef Q_OS_MACOS
- if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
+ if (auto portableUserData = FS::PathCombine(m_rootPath, "UserData"); QDir(portableUserData).exists()) {
+ m_dataPath = portableUserData;
+ adjustedBy = "Portable user data path";
+ m_isPortable = true;
+ } else if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
m_dataPath = m_rootPath;
adjustedBy = "Portable data path";
m_isPortable = true;
@@ -580,12 +598,6 @@ void PrismUpdaterApp::run()
return exit(result ? 0 : 1);
}
- if (BuildConfig.BUILD_ARTIFACT.toLower() == "linux" && !m_isPortable) {
- showFatalErrorMessage(tr("Updating Not Supported"),
- tr("Updating non-portable linux installations is not supported. Please use your system package manager"));
- return;
- }
-
if (need_update || m_forceUpdate || !m_userSelectedVersion.isEmpty()) {
GitHubRelease update_release = latest;
if (!m_userSelectedVersion.isEmpty()) {
@@ -787,6 +799,10 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel
if (BuildConfig.BUILD_ARTIFACT.isEmpty())
qWarning() << "Build platform is not set!";
for (auto asset : release.assets) {
+ if (asset.name.endsWith(".zsync")) {
+ qDebug() << "Rejecting zsync file" << asset.name;
+ continue;
+ }
if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) {
qDebug() << "Rejecting" << asset.name << "because it is an AppImage";
continue;
@@ -1020,7 +1036,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file)
FS::write(changelog_path, m_install_release.body.toUtf8());
logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name));
- if (m_isPortable || file.suffix().toLower() == "zip") {
+ if (m_isPortable || file.fileName().endsWith(".zip") || file.fileName().endsWith(".tar.gz")) {
write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, m_rootPath, m_dataPath);
logUpdate(tr("Updating portable install at %1").arg(m_rootPath));
unpackAndInstall(file);
@@ -1094,7 +1110,7 @@ void PrismUpdaterApp::backupAppDir()
if (file_list.isEmpty()) {
// best guess
- if (BuildConfig.BUILD_ARTIFACT.toLower() == "linux") {
+ if (BuildConfig.BUILD_ARTIFACT.toLower().contains("linux")) {
file_list.append({ "PrismLauncher", "bin", "share", "lib" });
} else { // windows by process of elimination
file_list.append({
diff --git a/program_info/org.prismlauncher.PrismLauncher.desktop.in b/program_info/org.prismlauncher.PrismLauncher.desktop.in
index a2846b06e..c0e4e9458 100644
--- a/program_info/org.prismlauncher.PrismLauncher.desktop.in
+++ b/program_info/org.prismlauncher.PrismLauncher.desktop.in
@@ -1,13 +1,13 @@
[Desktop Entry]
Version=1.0
-Name=Prism Launcher
+Name=@Launcher_DisplayName@
Comment=Discover, manage, and play Minecraft instances
Type=Application
Terminal=false
Exec=@Launcher_APP_BINARY_NAME@ %U
StartupNotify=true
-Icon=org.prismlauncher.PrismLauncher
+Icon=org.@Launcher_APP_BINARY_NAME@.@Launcher_CommonName@
Categories=Game;ActionGame;AdventureGame;Simulation;
Keywords=game;minecraft;mc;
-StartupWMClass=PrismLauncher
-MimeType=application/zip;application/x-modrinth-modpack+zip;x-scheme-handler/curseforge;
+StartupWMClass=@Launcher_CommonName@
+MimeType=application/zip;application/x-modrinth-modpack+zip;x-scheme-handler/curseforge;x-scheme-handler/@Launcher_APP_BINARY_NAME@;
diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in
index eda85821b..cc56b9bd5 100644
--- a/program_info/win_install.nsi.in
+++ b/program_info/win_install.nsi.in
@@ -368,6 +368,10 @@ Section "@Launcher_DisplayName@"
WriteRegStr HKCU Software\Classes\curseforge "URL Protocol" ""
WriteRegStr HKCU Software\Classes\curseforge\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
+; Write the URL Handler into registry for prismlauncher
+ WriteRegStr HKCU Software\Classes\prismlauncher "URL Protocol" ""
+ WriteRegStr HKCU Software\Classes\prismlauncher\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
+
; Write the uninstall keys for Windows
${GetParameters} $R0
${GetOptions} $R0 "/NoUninstaller" $R1