aboutsummaryrefslogtreecommitdiff
path: root/launcher/ui
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/ui')
-rw-r--r--launcher/ui/InstanceWindow.cpp6
-rw-r--r--launcher/ui/MainWindow.cpp237
-rw-r--r--launcher/ui/MainWindow.h2
-rw-r--r--launcher/ui/dialogs/BlockedModsDialog.cpp82
-rw-r--r--launcher/ui/dialogs/BlockedModsDialog.h63
-rw-r--r--launcher/ui/dialogs/BlockedModsDialog.ui38
-rw-r--r--launcher/ui/dialogs/IconPickerDialog.cpp6
-rw-r--r--launcher/ui/dialogs/IconPickerDialog.h1
-rw-r--r--launcher/ui/dialogs/ImportResourcePackDialog.cpp66
-rw-r--r--launcher/ui/dialogs/ImportResourcePackDialog.h27
-rw-r--r--launcher/ui/dialogs/ImportResourcePackDialog.ui74
-rw-r--r--launcher/ui/pages/BasePageContainer.h3
-rw-r--r--launcher/ui/pages/global/JavaPage.cpp34
-rw-r--r--launcher/ui/pages/global/JavaPage.h3
-rw-r--r--launcher/ui/pages/global/JavaPage.ui82
-rw-r--r--launcher/ui/pages/instance/ExternalResourcesPage.cpp21
-rw-r--r--launcher/ui/pages/instance/ExternalResourcesPage.h4
-rw-r--r--launcher/ui/pages/instance/InstanceSettingsPage.cpp35
-rw-r--r--launcher/ui/pages/instance/InstanceSettingsPage.h3
-rw-r--r--launcher/ui/pages/instance/InstanceSettingsPage.ui67
-rw-r--r--launcher/ui/pages/instance/ManagedPackPage.cpp433
-rw-r--r--launcher/ui/pages/instance/ManagedPackPage.h152
-rw-r--r--launcher/ui/pages/instance/ManagedPackPage.ui193
-rw-r--r--launcher/ui/pages/instance/ModFolderPage.cpp2
-rw-r--r--launcher/ui/pages/instance/ModFolderPage.h2
-rw-r--r--launcher/ui/pages/instance/ScreenshotsPage.cpp13
-rw-r--r--launcher/ui/pages/instance/ScreenshotsPage.h7
-rw-r--r--launcher/ui/pages/instance/ServersPage.cpp10
-rw-r--r--launcher/ui/pages/instance/ServersPage.h6
-rw-r--r--launcher/ui/pages/instance/VersionPage.cpp31
-rw-r--r--launcher/ui/pages/instance/VersionPage.h9
-rw-r--r--launcher/ui/pages/instance/VersionPage.ui9
-rw-r--r--launcher/ui/pages/instance/WorldListPage.cpp10
-rw-r--r--launcher/ui/pages/instance/WorldListPage.h4
-rw-r--r--launcher/ui/pages/modplatform/flame/FlamePage.cpp23
-rw-r--r--launcher/ui/pages/modplatform/flame/FlamePage.h2
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp6
-rw-r--r--launcher/ui/themes/ITheme.cpp13
-rw-r--r--launcher/ui/widgets/JavaSettingsWidget.cpp42
-rw-r--r--launcher/ui/widgets/JavaSettingsWidget.h9
-rw-r--r--launcher/ui/widgets/PageContainer.cpp5
-rw-r--r--launcher/ui/widgets/PageContainer.h1
-rw-r--r--launcher/ui/widgets/WideBar.cpp250
-rw-r--r--launcher/ui/widgets/WideBar.h30
44 files changed, 1841 insertions, 275 deletions
diff --git a/launcher/ui/InstanceWindow.cpp b/launcher/ui/InstanceWindow.cpp
index 09ce0d67..c62b370f 100644
--- a/launcher/ui/InstanceWindow.cpp
+++ b/launcher/ui/InstanceWindow.cpp
@@ -132,6 +132,12 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent)
{
connect(m_instance.get(), &BaseInstance::statusChanged, this, &InstanceWindow::on_instanceStatusChanged);
}
+
+ // add ourself as the modpack page's instance window
+ {
+ static_cast<ManagedPackPage*>(m_container->getPage("managed_pack"))->setInstanceWindow(this);
+ }
+
show();
}
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index 0595634f..6eaaac97 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -39,6 +39,7 @@
#include "Application.h"
#include "BuildConfig.h"
+#include "FileSystem.h"
#include "MainWindow.h"
@@ -71,6 +72,7 @@
#include <BaseInstance.h>
#include <InstanceList.h>
+#include <minecraft/MinecraftInstance.h>
#include <MMCZip.h>
#include <icons/IconList.h>
#include <java/JavaUtils.h>
@@ -103,8 +105,14 @@
#include "ui/dialogs/CopyInstanceDialog.h"
#include "ui/dialogs/EditAccountDialog.h"
#include "ui/dialogs/ExportInstanceDialog.h"
+#include "ui/dialogs/ImportResourcePackDialog.h"
#include "ui/themes/ITheme.h"
+#include <minecraft/mod/ResourcePackFolderModel.h>
+#include <minecraft/mod/tasks/LocalResourcePackParseTask.h>
+#include <minecraft/mod/TexturePackFolderModel.h>
+#include <minecraft/mod/tasks/LocalTexturePackParseTask.h>
+
#include "KonamiCode.h"
#include "InstanceImportTask.h"
@@ -235,6 +243,7 @@ public:
TranslatedAction actionLaunchInstanceOffline;
TranslatedAction actionLaunchInstanceDemo;
TranslatedAction actionExportInstance;
+ TranslatedAction actionCreateInstanceShortcut;
QVector<TranslatedAction *> all_actions;
LabeledToolButton *renameButton = nullptr;
@@ -535,8 +544,9 @@ public:
fileMenu->addAction(actionChangeInstGroup);
fileMenu->addAction(actionViewSelectedInstFolder);
fileMenu->addAction(actionExportInstance);
- fileMenu->addAction(actionDeleteInstance);
fileMenu->addAction(actionCopyInstance);
+ fileMenu->addAction(actionDeleteInstance);
+ fileMenu->addAction(actionCreateInstanceShortcut);
fileMenu->addSeparator();
fileMenu->addAction(actionSettings);
@@ -622,6 +632,7 @@ public:
actionExportInstance->setEnabled(enabled);
actionDeleteInstance->setEnabled(enabled);
actionCopyInstance->setEnabled(enabled);
+ actionCreateInstanceShortcut->setEnabled(enabled);
}
void createStatusBar(QMainWindow *MainWindow)
@@ -760,6 +771,13 @@ public:
actionCopyInstance->setIcon(APPLICATION->getThemedIcon("copy"));
all_actions.append(&actionCopyInstance);
+ actionCreateInstanceShortcut = TranslatedAction(MainWindow);
+ actionCreateInstanceShortcut->setObjectName(QStringLiteral("actionCreateInstanceShortcut"));
+ actionCreateInstanceShortcut.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Create Shortcut"));
+ actionCreateInstanceShortcut.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Creates a shortcut on your desktop to launch the selected instance."));
+ actionCreateInstanceShortcut->setIcon(APPLICATION->getThemedIcon("shortcut"));
+ all_actions.append(&actionCreateInstanceShortcut);
+
setInstanceActionsEnabled(false);
}
@@ -798,6 +816,8 @@ public:
instanceToolBar->addAction(actionCopyInstance);
instanceToolBar->addAction(actionDeleteInstance);
+ instanceToolBar->addAction(actionCreateInstanceShortcut); // TODO find better position for this
+
QLayout * lay = instanceToolBar->layout();
for(int i = 0; i < lay->count(); i++)
{
@@ -1586,7 +1606,7 @@ InstanceView
background-image: url(:/backgrounds/%1);
background-attachment: fixed;
background-clip: padding;
- background-position: bottom left;
+ background-position: bottom right;
background-repeat: none;
background-color:palette(base);
})")
@@ -1717,17 +1737,41 @@ void MainWindow::on_actionAddInstance_triggered()
void MainWindow::droppedURLs(QList<QUrl> urls)
{
- for(auto & url:urls)
- {
- if(url.isLocalFile())
- {
- addInstance(url.toLocalFile());
- }
- else
- {
+ // NOTE: This loop only processes one dropped file!
+ for (auto& url : urls) {
+ // The isLocalFile() check below doesn't work as intended without an explicit scheme.
+ if (url.scheme().isEmpty())
+ url.setScheme("file");
+
+ if (!url.isLocalFile()) { // probably instance/modpack
addInstance(url.toString());
+ break;
+ }
+
+ auto localFileName = url.toLocalFile();
+ QFileInfo localFileInfo(localFileName);
+
+ bool isResourcePack = ResourcePackUtils::validate(localFileInfo);
+ bool isTexturePack = TexturePackUtils::validate(localFileInfo);
+
+ if (!isResourcePack && !isTexturePack) { // probably instance/modpack
+ addInstance(localFileName);
+ break;
}
- // Only process one dropped file...
+
+ ImportResourcePackDialog dlg(this);
+
+ if (dlg.exec() != QDialog::Accepted)
+ break;
+
+ qDebug() << "Adding resource/texture pack" << localFileName << "to" << dlg.selectedInstanceKey;
+
+ auto inst = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey);
+ auto minecraftInst = std::dynamic_pointer_cast<MinecraftInstance>(inst);
+ if (isResourcePack)
+ minecraftInst->resourcePackList()->installResource(localFileName);
+ else if (isTexturePack)
+ minecraftInst->texturePackList()->installResource(localFileName);
break;
}
}
@@ -1965,27 +2009,25 @@ void MainWindow::on_actionAbout_triggered()
void MainWindow::on_actionDeleteInstance_triggered()
{
- if (!m_selectedInstance)
- {
+ if (!m_selectedInstance) {
return;
}
auto id = m_selectedInstance->id();
- if (APPLICATION->instances()->trashInstance(id)) {
- ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
- return;
- }
-
- auto response = CustomMessageBox::selectable(
- this,
- tr("CAREFUL!"),
- tr("About to delete: %1\nThis is permanent and will completely delete the instance.\n\nAre you sure?").arg(m_selectedInstance->name()),
- QMessageBox::Warning,
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::No
- )->exec();
- if (response == QMessageBox::Yes)
- {
+
+ auto response =
+ CustomMessageBox::selectable(this, tr("CAREFUL!"),
+ tr("About to delete: %1\nThis may be permanent and will completely delete the instance.\n\nAre you sure?")
+ .arg(m_selectedInstance->name()),
+ QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
+ ->exec();
+
+ if (response == QMessageBox::Yes) {
+ if (APPLICATION->instances()->trashInstance(id)) {
+ ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
+ return;
+ }
+
APPLICATION->instances()->deleteInstance(id);
}
}
@@ -2083,6 +2125,145 @@ void MainWindow::on_actionKillInstance_triggered()
}
}
+void MainWindow::on_actionCreateInstanceShortcut_triggered()
+{
+ if (m_selectedInstance)
+ {
+ auto desktopPath = FS::getDesktopDir();
+ if (desktopPath.isEmpty()) {
+ // TODO come up with an alternative solution (open "save file" dialog)
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Couldn't find desktop?!"));
+ return;
+ }
+
+#if defined(Q_OS_MACOS)
+ QString appPath = QApplication::applicationFilePath();
+ if (appPath.startsWith("/private/var/")) {
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts."));
+ return;
+ }
+
+ if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()),
+ appPath, { "--launch", m_selectedInstance->id() },
+ m_selectedInstance->name(), "")) {
+ QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
+ }
+ else
+ {
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!"));
+ }
+#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
+ QString appPath = QApplication::applicationFilePath();
+ if (appPath.startsWith("/tmp/.mount_")) {
+ // AppImage!
+ appPath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
+ if (appPath.isEmpty())
+ {
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Launcher is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)"));
+ }
+ else if (appPath.endsWith("/"))
+ {
+ appPath.chop(1);
+ }
+ }
+
+ auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey());
+ if (icon == nullptr)
+ {
+ icon = APPLICATION->icons()->icon("grass");
+ }
+
+ QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.png");
+
+ QFile iconFile(iconPath);
+ if (!iconFile.open(QFile::WriteOnly))
+ {
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
+ return;
+ }
+ bool success = icon->icon().pixmap(64, 64).save(&iconFile, "PNG");
+ iconFile.close();
+
+ if (!success)
+ {
+ iconFile.remove();
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
+ return;
+ }
+
+ QString desktopFilePath = FS::PathCombine(desktopPath, m_selectedInstance->name() + ".desktop");
+ QStringList args;
+ if (DesktopServices::isFlatpak()) {
+ QFileDialog fileDialog;
+ // workaround to make sure the portal file dialog opens in the desktop directory
+ fileDialog.setDirectoryUrl(desktopPath);
+ desktopFilePath = fileDialog.getSaveFileName(
+ this, tr("Create Shortcut"), desktopFilePath,
+ tr("Desktop Entries (*.desktop)"));
+ if (desktopFilePath.isEmpty())
+ return; // file dialog canceled by user
+ appPath = "flatpak";
+ QString flatpakAppId = BuildConfig.LAUNCHER_DESKTOPFILENAME;
+ flatpakAppId.remove(".desktop");
+ args.append({ "run", flatpakAppId });
+ }
+ args.append({ "--launch", m_selectedInstance->id() });
+ if (FS::createShortcut(desktopFilePath, appPath, args, m_selectedInstance->name(), iconPath)) {
+ QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
+ }
+ else
+ {
+ iconFile.remove();
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!"));
+ }
+#elif defined(Q_OS_WIN)
+ auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey());
+ if (icon == nullptr)
+ {
+ icon = APPLICATION->icons()->icon("grass");
+ }
+
+ QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico");
+
+ // part of fix for weird bug involving the window icon being replaced
+ // dunno why it happens, but this 2-line fix seems to be enough, so w/e
+ auto appIcon = APPLICATION->getThemedIcon("logo");
+
+ QFile iconFile(iconPath);
+ if (!iconFile.open(QFile::WriteOnly))
+ {
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
+ return;
+ }
+ bool success = icon->icon().pixmap(64, 64).save(&iconFile, "ICO");
+ iconFile.close();
+
+ // restore original window icon
+ QGuiApplication::setWindowIcon(appIcon);
+
+ if (!success)
+ {
+ iconFile.remove();
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
+ return;
+ }
+
+ if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()),
+ QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() },
+ m_selectedInstance->name(), iconPath)) {
+ QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
+ }
+ else
+ {
+ iconFile.remove();
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!"));
+ }
+#else
+ QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on your platform!"));
+#endif
+ }
+}
+
void MainWindow::taskEnd()
{
QObject *sender = QObject::sender();
diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h
index 53db4919..babc5e03 100644
--- a/launcher/ui/MainWindow.h
+++ b/launcher/ui/MainWindow.h
@@ -160,6 +160,8 @@ private slots:
void on_actionEditInstance_triggered();
+ void on_actionCreateInstanceShortcut_triggered();
+
void taskEnd();
/**
diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp
index eeeeb709..0480ced7 100644
--- a/launcher/ui/dialogs/BlockedModsDialog.cpp
+++ b/launcher/ui/dialogs/BlockedModsDialog.cpp
@@ -1,7 +1,33 @@
+// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
+// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+// SPDX-FileCopyrightText: 2022 kumquat-ir <66188216+kumquat-ir@users.noreply.github.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ * Copyright (C) 2022 kumquat-ir <66188216+kumquat-ir@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
#include "BlockedModsDialog.h"
#include "ui_BlockedModsDialog.h"
#include "Application.h"
+#include "modplatform/helpers/HashUtils.h"
#include <QDebug>
#include <QDesktopServices>
@@ -13,6 +39,7 @@
#include <QFileInfo>
#include <QMimeData>
#include <QPushButton>
+#include <QMimeData>
#include <QStandardPaths>
BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods)
@@ -23,8 +50,8 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons
ui->setupUi(this);
- auto openAllButton = ui->buttonBox->addButton(tr("Open All"), QDialogButtonBox::ActionRole);
- connect(openAllButton, &QPushButton::clicked, this, &BlockedModsDialog::openAll);
+ m_openMissingButton = ui->buttonBox->addButton(tr("Open Missing"), QDialogButtonBox::ActionRole);
+ connect(m_openMissingButton, &QPushButton::clicked, this, [this]() { openAll(true); });
auto downloadFolderButton = ui->buttonBox->addButton(tr("Add Download Folder"), QDialogButtonBox::ActionRole);
connect(downloadFolderButton, &QPushButton::clicked, this, &BlockedModsDialog::addDownloadFolder);
@@ -38,15 +65,8 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons
this->setWindowTitle(title);
ui->labelDescription->setText(text);
- ui->labelExplain->setText(
- QString(tr("Your configured global mods folder and default downloads folder "
- "are automatically checked for the downloaded mods and they will be copied to the instance if found.<br/>"
- "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch "
- "if you did not download the mods to a default location."))
- .arg(APPLICATION->settings()->get("CentralModsDir").toString(),
- QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)));
-
- // force all URL handeling as external
+
+ // force all URL handling as external
connect(ui->textBrowserWatched, &QTextBrowser::anchorClicked, this, [](const QUrl url) { QDesktopServices::openUrl(url); });
setAcceptDrops(true);
@@ -68,7 +88,15 @@ void BlockedModsDialog::dragEnterEvent(QDragEnterEvent* e)
void BlockedModsDialog::dropEvent(QDropEvent* e)
{
- for (const QUrl& url : e->mimeData()->urls()) {
+ for (QUrl& url : e->mimeData()->urls()) {
+ if (url.scheme().isEmpty()) { // ensure isLocalFile() works correctly
+ url.setScheme("file");
+ }
+
+ if (!url.isLocalFile()) { // can't drop external files here.
+ continue;
+ }
+
QString filePath = url.toLocalFile();
qDebug() << "[Blocked Mods Dialog] Dropped file:" << filePath;
addHashTask(filePath);
@@ -83,10 +111,18 @@ void BlockedModsDialog::dropEvent(QDropEvent* e)
update();
}
-void BlockedModsDialog::openAll()
+void BlockedModsDialog::done(int r)
+{
+ QDialog::done(r);
+ disconnect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &BlockedModsDialog::directoryChanged);
+}
+
+void BlockedModsDialog::openAll(bool missingOnly)
{
for (auto& mod : m_mods) {
- QDesktopServices::openUrl(mod.websiteUrl);
+ if (!missingOnly || !mod.matched) {
+ QDesktopServices::openUrl(mod.websiteUrl);
+ }
}
}
@@ -129,8 +165,10 @@ void BlockedModsDialog::update()
if (allModsMatched()) {
ui->labelModsFound->setText("<span style=\"color:green\">✔</span>" + tr("All mods found"));
+ m_openMissingButton->setDisabled(true);
} else {
ui->labelModsFound->setText(tr("Please download the missing mods."));
+ m_openMissingButton->setDisabled(false);
}
}
@@ -241,14 +279,24 @@ void BlockedModsDialog::checkMatchHash(QString hash, QString path)
/// @return boolean: did the path match the name of a blocked mod?
bool BlockedModsDialog::checkValidPath(QString path)
{
- QFileInfo file = QFileInfo(path);
- QString filename = file.fileName();
+ const QFileInfo file = QFileInfo(path);
+ const QString filename = file.fileName();
+ QString laxFilename(filename);
+ laxFilename.replace('+', ' ');
+
+ auto compare = [](QString fsfilename, QString metadataFilename) {
+ return metadataFilename.compare(fsfilename, Qt::CaseInsensitive) == 0;
+ };
for (auto& mod : m_mods) {
- if (mod.name.compare(filename, Qt::CaseInsensitive) == 0) {
+ if (compare(filename, mod.name)) {
qDebug() << "[Blocked Mods Dialog] Name match found:" << mod.name << "| From path:" << path;
return true;
}
+ if (compare(laxFilename, mod.name)) {
+ qDebug() << "[Blocked Mods Dialog] Lax name match found:" << mod.name << "| From path:" << path;
+ return true;
+ }
}
return false;
diff --git a/launcher/ui/dialogs/BlockedModsDialog.h b/launcher/ui/dialogs/BlockedModsDialog.h
index dac43cba..014f488a 100644
--- a/launcher/ui/dialogs/BlockedModsDialog.h
+++ b/launcher/ui/dialogs/BlockedModsDialog.h
@@ -1,49 +1,80 @@
+// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
+// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+// SPDX-FileCopyrightText: 2022 kumquat-ir <66188216+kumquat-ir@users.noreply.github.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ * Copyright (C) 2022 kumquat-ir <66188216+kumquat-ir@users.noreply.github.com>
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
#pragma once
#include <QDialog>
-#include <QString>
#include <QList>
+#include <QString>
#include <QFileSystemWatcher>
-#include "modplatform/helpers/HashUtils.h"
-
#include "tasks/ConcurrentTask.h"
+class QPushButton;
+
struct BlockedMod {
QString name;
QString websiteUrl;
QString hash;
bool matched;
QString localPath;
-
+ QString targetFolder;
};
QT_BEGIN_NAMESPACE
-namespace Ui { class BlockedModsDialog; }
+namespace Ui {
+class BlockedModsDialog;
+}
QT_END_NAMESPACE
class BlockedModsDialog : public QDialog {
-Q_OBJECT
+ Q_OBJECT
-public:
- BlockedModsDialog(QWidget *parent, const QString &title, const QString &text, QList<BlockedMod> &mods);
+ public:
+ BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods);
~BlockedModsDialog() override;
-protected:
- void dragEnterEvent(QDragEnterEvent *event) override;
- void dropEvent(QDropEvent *event) override;
+ protected:
+ void dragEnterEvent(QDragEnterEvent* event) override;
+ void dropEvent(QDropEvent* event) override;
+
+ protected slots:
+ void done(int r) override;
-private:
- Ui::BlockedModsDialog *ui;
- QList<BlockedMod> &m_mods;
+ private:
+ Ui::BlockedModsDialog* ui;
+ QList<BlockedMod>& m_mods;
QFileSystemWatcher m_watcher;
shared_qobject_ptr<ConcurrentTask> m_hashing_task;
QSet<QString> m_pending_hash_paths;
bool m_rehash_pending;
+ QPushButton* m_openMissingButton;
- void openAll();
+ void openAll(bool missingOnly);
void addDownloadFolder();
void update();
void directoryChanged(QString path);
@@ -61,4 +92,4 @@ private:
bool allModsMatched();
};
-QDebug operator<<(QDebug debug, const BlockedMod &m);
+QDebug operator<<(QDebug debug, const BlockedMod& m);
diff --git a/launcher/ui/dialogs/BlockedModsDialog.ui b/launcher/ui/dialogs/BlockedModsDialog.ui
index 88105178..2292b99c 100644
--- a/launcher/ui/dialogs/BlockedModsDialog.ui
+++ b/launcher/ui/dialogs/BlockedModsDialog.ui
@@ -7,17 +7,23 @@
<x>0</x>
<y>0</y>
<width>400</width>
- <height>455</height>
+ <height>400</height>
</rect>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>350</height>
+ </size>
+ </property>
<property name="windowTitle">
<string notr="true">BlockedModsDialog</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,3,0,1,0">
<item>
<widget class="QLabel" name="labelDescription">
<property name="text">
- <string notr="true"/>
+ <string notr="true">Placeholder description</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
@@ -30,7 +36,7 @@
<item>
<widget class="QLabel" name="labelExplain">
<property name="text">
- <string/>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Your configured global mods folder and default downloads folder are automatically checked for the downloaded mods and they will be copied to the instance if found.&lt;/p&gt;&lt;p&gt;Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch if you did not download the mods to a default location.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -42,12 +48,6 @@
</item>
<item>
<widget class="QTextBrowser" name="textBrowserModsListing">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>165</height>
- </size>
- </property>
<property name="acceptRichText">
<bool>true</bool>
</property>
@@ -58,12 +58,6 @@
</item>
<item>
<widget class="QLabel" name="labelWatched">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>1</verstretch>
- </sizepolicy>
- </property>
<property name="text">
<string>Watched Folders:</string>
</property>
@@ -71,18 +65,6 @@
</item>
<item>
<widget class="QTextBrowser" name="textBrowserWatched">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>16</height>
- </size>
- </property>
<property name="baseSize">
<size>
<width>0</width>
diff --git a/launcher/ui/dialogs/IconPickerDialog.cpp b/launcher/ui/dialogs/IconPickerDialog.cpp
index fcb645db..0551a1ef 100644
--- a/launcher/ui/dialogs/IconPickerDialog.cpp
+++ b/launcher/ui/dialogs/IconPickerDialog.cpp
@@ -63,7 +63,7 @@ IconPickerDialog::IconPickerDialog(QWidget *parent)
// NOTE: ResetRole forces the button to be on the left, while the OK/Cancel ones are on the right. We win.
auto buttonAdd = ui->buttonBox->addButton(tr("Add Icon"), QDialogButtonBox::ResetRole);
- auto buttonRemove = ui->buttonBox->addButton(tr("Remove Icon"), QDialogButtonBox::ResetRole);
+ buttonRemove = ui->buttonBox->addButton(tr("Remove Icon"), QDialogButtonBox::ResetRole);
connect(buttonAdd, SIGNAL(clicked(bool)), SLOT(addNewIcon()));
connect(buttonRemove, SIGNAL(clicked(bool)), SLOT(removeSelectedIcon()));
@@ -111,6 +111,9 @@ void IconPickerDialog::addNewIcon()
void IconPickerDialog::removeSelectedIcon()
{
+ if (APPLICATION->icons()->trashIcon(selectedIconKey))
+ return;
+
APPLICATION->icons()->deleteIcon(selectedIconKey);
}
@@ -129,6 +132,7 @@ void IconPickerDialog::selectionChanged(QItemSelection selected, QItemSelection
if (!key.isEmpty()) {
selectedIconKey = key;
}
+ buttonRemove->setEnabled(APPLICATION->icons()->iconFileExists(selectedIconKey));
}
int IconPickerDialog::execWithSelection(QString selection)
diff --git a/launcher/ui/dialogs/IconPickerDialog.h b/launcher/ui/dialogs/IconPickerDialog.h
index 9af6a678..c93f565f 100644
--- a/launcher/ui/dialogs/IconPickerDialog.h
+++ b/launcher/ui/dialogs/IconPickerDialog.h
@@ -37,6 +37,7 @@ protected:
private:
Ui::IconPickerDialog *ui;
+ QPushButton *buttonRemove;
private
slots:
diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.cpp b/launcher/ui/dialogs/ImportResourcePackDialog.cpp
new file mode 100644
index 00000000..e807e926
--- /dev/null
+++ b/launcher/ui/dialogs/ImportResourcePackDialog.cpp
@@ -0,0 +1,66 @@
+#include "ImportResourcePackDialog.h"
+#include "ui_ImportResourcePackDialog.h"
+
+#include <QFileDialog>
+#include <QPushButton>
+
+#include "Application.h"
+#include "InstanceList.h"
+
+#include <InstanceList.h>
+#include "ui/instanceview/InstanceProxyModel.h"
+#include "ui/instanceview/InstanceDelegate.h"
+
+ImportResourcePackDialog::ImportResourcePackDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ImportResourcePackDialog)
+{
+ ui->setupUi(this);
+ setWindowModality(Qt::WindowModal);
+
+ auto contentsWidget = ui->instanceView;
+ contentsWidget->setViewMode(QListView::ListMode);
+ contentsWidget->setFlow(QListView::LeftToRight);
+ contentsWidget->setIconSize(QSize(48, 48));
+ contentsWidget->setMovement(QListView::Static);
+ contentsWidget->setResizeMode(QListView::Adjust);
+ contentsWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+ contentsWidget->setSpacing(5);
+ contentsWidget->setWordWrap(true);
+ contentsWidget->setWrapping(true);
+ // NOTE: We can't have uniform sizes because the text may wrap if it's too long. If we set this, it will cut off the wrapped text.
+ contentsWidget->setUniformItemSizes(false);
+ contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ contentsWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ contentsWidget->setItemDelegate(new ListViewDelegate());
+
+ proxyModel = new InstanceProxyModel(this);
+ proxyModel->setSourceModel(APPLICATION->instances().get());
+ proxyModel->sort(0);
+ contentsWidget->setModel(proxyModel);
+
+ connect(contentsWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(activated(QModelIndex)));
+ connect(contentsWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ SLOT(selectionChanged(QItemSelection, QItemSelection)));
+}
+
+void ImportResourcePackDialog::activated(QModelIndex index)
+{
+ selectedInstanceKey = index.data(InstanceList::InstanceIDRole).toString();
+ accept();
+}
+
+void ImportResourcePackDialog::selectionChanged(QItemSelection selected, QItemSelection deselected)
+{
+ if (selected.empty())
+ return;
+
+ QString key = selected.first().indexes().first().data(InstanceList::InstanceIDRole).toString();
+ if (!key.isEmpty()) {
+ selectedInstanceKey = key;
+ }
+}
+
+ImportResourcePackDialog::~ImportResourcePackDialog()
+{
+ delete ui;
+}
diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.h b/launcher/ui/dialogs/ImportResourcePackDialog.h
new file mode 100644
index 00000000..8356f204
--- /dev/null
+++ b/launcher/ui/dialogs/ImportResourcePackDialog.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <QDialog>
+#include <QItemSelection>
+
+#include "ui/instanceview/InstanceProxyModel.h"
+
+namespace Ui {
+class ImportResourcePackDialog;
+}
+
+class ImportResourcePackDialog : public QDialog {
+ Q_OBJECT
+
+ public:
+ explicit ImportResourcePackDialog(QWidget* parent = 0);
+ ~ImportResourcePackDialog();
+ InstanceProxyModel* proxyModel;
+ QString selectedInstanceKey;
+
+ private:
+ Ui::ImportResourcePackDialog* ui;
+
+ private slots:
+ void selectionChanged(QItemSelection, QItemSelection);
+ void activated(QModelIndex);
+};
diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.ui b/launcher/ui/dialogs/ImportResourcePackDialog.ui
new file mode 100644
index 00000000..20cb9177
--- /dev/null
+++ b/launcher/ui/dialogs/ImportResourcePackDialog.ui
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ImportResourcePackDialog</class>
+ <widget class="QDialog" name="ImportResourcePackDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>676</width>
+ <height>555</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Choose instance to import</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Choose the instance you would like to import this resource pack to.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QListView" name="instanceView"/>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ImportResourcePackDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ImportResourcePackDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/launcher/ui/pages/BasePageContainer.h b/launcher/ui/pages/BasePageContainer.h
index f8c7adeb..b41fe12a 100644
--- a/launcher/ui/pages/BasePageContainer.h
+++ b/launcher/ui/pages/BasePageContainer.h
@@ -1,10 +1,13 @@
#pragma once
+class BasePage;
+
class BasePageContainer
{
public:
virtual ~BasePageContainer(){};
virtual bool selectPage(QString pageId) = 0;
+ virtual BasePage* getPage(QString pageId) { return nullptr; };
virtual void refreshContainer() = 0;
virtual bool requestClose() = 0;
};
diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp
index 2cee15bf..81dd4cc1 100644
--- a/launcher/ui/pages/global/JavaPage.cpp
+++ b/launcher/ui/pages/global/JavaPage.cpp
@@ -58,9 +58,8 @@ JavaPage::JavaPage(QWidget *parent) : QWidget(parent), ui(new Ui::JavaPage)
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
- auto sysMiB = Sys::getSystemRam() / Sys::mebibyte;
- ui->maxMemSpinBox->setMaximum(sysMiB);
loadSettings();
+ updateThresholds();
}
JavaPage::~JavaPage()
@@ -177,6 +176,11 @@ void JavaPage::on_javaTestBtn_clicked()
checker->run();
}
+void JavaPage::on_maxMemSpinBox_valueChanged(int i)
+{
+ updateThresholds();
+}
+
void JavaPage::checkerFinished()
{
checker.reset();
@@ -186,3 +190,29 @@ void JavaPage::retranslate()
{
ui->retranslateUi(this);
}
+
+void JavaPage::updateThresholds()
+{
+ auto sysMiB = Sys::getSystemRam() / Sys::mebibyte;
+ unsigned int maxMem = ui->maxMemSpinBox->value();
+
+ QString iconName;
+
+ if (maxMem >= sysMiB) {
+ iconName = "status-bad";
+ ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity."));
+ } else if (maxMem > (sysMiB * 0.9)) {
+ iconName = "status-yellow";
+ ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
+ } else {
+ iconName = "status-good";
+ ui->labelMaxMemIcon->setToolTip("");
+ }
+
+ {
+ auto height = ui->labelMaxMemIcon->fontInfo().pixelSize();
+ QIcon icon = APPLICATION->getThemedIcon(iconName);
+ QPixmap pix = icon.pixmap(height, height);
+ ui->labelMaxMemIcon->setPixmap(pix);
+ }
+}
diff --git a/launcher/ui/pages/global/JavaPage.h b/launcher/ui/pages/global/JavaPage.h
index 64d4098e..2ef6d749 100644
--- a/launcher/ui/pages/global/JavaPage.h
+++ b/launcher/ui/pages/global/JavaPage.h
@@ -76,6 +76,8 @@ public:
bool apply() override;
void retranslate() override;
+ void updateThresholds();
+
private:
void applySettings();
void loadSettings();
@@ -85,6 +87,7 @@ slots:
void on_javaDetectBtn_clicked();
void on_javaTestBtn_clicked();
void on_javaBrowseBtn_clicked();
+ void on_maxMemSpinBox_valueChanged(int i);
void checkerFinished();
private:
diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui
index 6ccffed4..6749cbe4 100644
--- a/launcher/ui/pages/global/JavaPage.ui
+++ b/launcher/ui/pages/global/JavaPage.ui
@@ -44,50 +44,38 @@
<property name="title">
<string>Memory</string>
</property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="1" column="1">
- <widget class="QSpinBox" name="maxMemSpinBox">
- <property name="toolTip">
- <string>The maximum amount of memory Minecraft is allowed to use.</string>
- </property>
- <property name="suffix">
- <string notr="true"> MiB</string>
- </property>
- <property name="minimum">
- <number>128</number>
- </property>
- <property name="maximum">
- <number>65536</number>
- </property>
- <property name="singleStep">
- <number>128</number>
+ <layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0,0,0">
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelMaxMem">
+ <property name="text">
+ <string>Ma&amp;ximum memory allocation:</string>
</property>
- <property name="value">
- <number>1024</number>
+ <property name="buddy">
+ <cstring>maxMemSpinBox</cstring>
</property>
</widget>
</item>
- <item row="0" column="0">
- <widget class="QLabel" name="labelMinMem">
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelPermGen">
<property name="text">
- <string>&amp;Minimum memory allocation:</string>
+ <string notr="true">&amp;PermGen:</string>
</property>
<property name="buddy">
- <cstring>minMemSpinBox</cstring>
+ <cstring>permGenSpinBox</cstring>
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="labelMaxMem">
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelMinMem">
<property name="text">
- <string>Ma&amp;ximum memory allocation:</string>
+ <string>&amp;Minimum memory allocation:</string>
</property>
<property name="buddy">
- <cstring>maxMemSpinBox</cstring>
+ <cstring>minMemSpinBox</cstring>
</property>
</widget>
</item>
- <item row="0" column="1">
+ <item row="0" column="2">
<widget class="QSpinBox" name="minMemSpinBox">
<property name="toolTip">
<string>The amount of memory Minecraft is started with.</string>
@@ -99,7 +87,7 @@
<number>128</number>
</property>
<property name="maximum">
- <number>65536</number>
+ <number>1048576</number>
</property>
<property name="singleStep">
<number>128</number>
@@ -109,17 +97,29 @@
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="labelPermGen">
- <property name="text">
- <string notr="true">&amp;PermGen:</string>
+ <item row="1" column="2">
+ <widget class="QSpinBox" name="maxMemSpinBox">
+ <property name="toolTip">
+ <string>The maximum amount of memory Minecraft is allowed to use.</string>
</property>
- <property name="buddy">
- <cstring>permGenSpinBox</cstring>
+ <property name="suffix">
+ <string notr="true"> MiB</string>
+ </property>
+ <property name="minimum">
+ <number>128</number>
+ </property>
+ <property name="maximum">
+ <number>1048576</number>
+ </property>
+ <property name="singleStep">
+ <number>128</number>
+ </property>
+ <property name="value">
+ <number>1024</number>
</property>
</widget>
</item>
- <item row="2" column="1">
+ <item row="2" column="2">
<widget class="QSpinBox" name="permGenSpinBox">
<property name="toolTip">
<string>The amount of memory available to store loaded Java classes.</string>
@@ -141,6 +141,16 @@
</property>
</widget>
</item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="labelMaxMemIcon">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="buddy">
+ <cstring>maxMemSpinBox</cstring>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp
index b6c873cc..c66d1368 100644
--- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp
+++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp
@@ -14,8 +14,6 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
{
ui->setupUi(this);
- ExternalResourcesPage::runningStateChanged(m_instance && m_instance->isRunning());
-
ui->actionsToolbar->insertSpacer(ui->actionViewConfigs);
m_filterModel = model->createFilterProxyModel(this);
@@ -45,7 +43,6 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
auto selection_model = ui->treeView->selectionModel();
connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current);
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged);
- connect(m_instance, &BaseInstance::runningStatusChanged, this, &ExternalResourcesPage::runningStateChanged);
}
ExternalResourcesPage::~ExternalResourcesPage()
@@ -70,11 +67,21 @@ void ExternalResourcesPage::ShowContextMenu(const QPoint& pos)
void ExternalResourcesPage::openedImpl()
{
m_model->startWatching();
+
+ auto const setting_name = QString("WideBarVisibility_%1").arg(id());
+ if (!APPLICATION->settings()->contains(setting_name))
+ m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name);
+ else
+ m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name);
+
+ ui->actionsToolbar->setVisibilityState(m_wide_bar_setting->get().toByteArray());
}
void ExternalResourcesPage::closedImpl()
{
m_model->stopWatching();
+
+ m_wide_bar_setting->set(ui->actionsToolbar->getVisibilityState());
}
void ExternalResourcesPage::retranslate()
@@ -97,14 +104,6 @@ void ExternalResourcesPage::filterTextChanged(const QString& newContents)
m_filterModel->setFilterRegularExpression(m_viewFilter);
}
-void ExternalResourcesPage::runningStateChanged(bool running)
-{
- if (m_controlsEnabled == !running)
- return;
-
- m_controlsEnabled = !running;
-}
-
bool ExternalResourcesPage::shouldDisplay() const
{
return true;
diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h
index 8e352cef..2d1a5b51 100644
--- a/launcher/ui/pages/instance/ExternalResourcesPage.h
+++ b/launcher/ui/pages/instance/ExternalResourcesPage.h
@@ -4,6 +4,7 @@
#include <QSortFilterProxyModel>
#include "Application.h"
+#include "settings/Setting.h"
#include "minecraft/MinecraftInstance.h"
#include "ui/pages/BasePage.h"
@@ -47,7 +48,6 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
protected slots:
void itemActivated(const QModelIndex& index);
void filterTextChanged(const QString& newContents);
- virtual void runningStateChanged(bool running);
virtual void addItem();
virtual void removeItem();
@@ -71,4 +71,6 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
QString m_viewFilter;
bool m_controlsEnabled = true;
+
+ std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
};
diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp
index 5da7f19f..af2ba7c8 100644
--- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp
+++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp
@@ -59,12 +59,12 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent)
{
m_settings = inst->settings();
ui->setupUi(this);
- auto sysMB = Sys::getSystemRam() / Sys::mebibyte;
- ui->maxMemSpinBox->setMaximum(sysMB);
+
connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked);
connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings);
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings);
loadSettings();
+ updateThresholds();
}
bool InstanceSettingsPage::shouldDisplay() const
@@ -437,6 +437,11 @@ void InstanceSettingsPage::on_javaTestBtn_clicked()
checker->run();
}
+void InstanceSettingsPage::on_maxMemSpinBox_valueChanged(int i)
+{
+ updateThresholds();
+}
+
void InstanceSettingsPage::checkerFinished()
{
checker.reset();
@@ -447,3 +452,29 @@ void InstanceSettingsPage::retranslate()
ui->retranslateUi(this);
ui->customCommands->retranslate(); // TODO: why is this seperate from the others?
}
+
+void InstanceSettingsPage::updateThresholds()
+{
+ auto sysMiB = Sys::getSystemRam() / Sys::mebibyte;
+ unsigned int maxMem = ui->maxMemSpinBox->value();
+
+ QString iconName;
+
+ if (maxMem >= sysMiB) {
+ iconName = "status-bad";
+ ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity."));
+ } else if (maxMem > (sysMiB * 0.9)) {
+ iconName = "status-yellow";
+ ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
+ } else {
+ iconName = "status-good";
+ ui->labelMaxMemIcon->setToolTip("");
+ }
+
+ {
+ auto height = ui->labelMaxMemIcon->fontInfo().pixelSize();
+ QIcon icon = APPLICATION->getThemedIcon(iconName);
+ QPixmap pix = icon.pixmap(height, height);
+ ui->labelMaxMemIcon->setPixmap(pix);
+ }
+}
diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h
index 97d1296f..7450188d 100644
--- a/launcher/ui/pages/instance/InstanceSettingsPage.h
+++ b/launcher/ui/pages/instance/InstanceSettingsPage.h
@@ -77,10 +77,13 @@ public:
virtual bool shouldDisplay() const override;
void retranslate() override;
+ void updateThresholds();
+
private slots:
void on_javaDetectBtn_clicked();
void on_javaTestBtn_clicked();
void on_javaBrowseBtn_clicked();
+ void on_maxMemSpinBox_valueChanged(int i);
void applySettings();
void loadSettings();
diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui
index 8b3c3370..b064367d 100644
--- a/launcher/ui/pages/instance/InstanceSettingsPage.ui
+++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui
@@ -112,7 +112,14 @@
<property name="checked">
<bool>false</bool>
</property>
- <layout class="QGridLayout" name="gridLayout_2">
+ <layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0,0,0">
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelPermGen">
+ <property name="text">
+ <string notr="true">PermGen:</string>
+ </property>
+ </widget>
+ </item>
<item row="0" column="0">
<widget class="QLabel" name="labelMinMem">
<property name="text">
@@ -120,10 +127,24 @@
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QSpinBox" name="maxMemSpinBox">
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelMaxMem">
+ <property name="text">
+ <string>Maximum memory allocation:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="3">
+ <widget class="QLabel" name="labelPermgenNote">
+ <property name="text">
+ <string>Note: Permgen is set automatically by Java 8 and later</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSpinBox" name="minMemSpinBox">
<property name="toolTip">
- <string>The maximum amount of memory Minecraft is allowed to use.</string>
+ <string>The amount of memory Minecraft is started with.</string>
</property>
<property name="suffix">
<string notr="true"> MiB</string>
@@ -132,20 +153,20 @@
<number>128</number>
</property>
<property name="maximum">
- <number>65536</number>
+ <number>1048576</number>
</property>
<property name="singleStep">
<number>128</number>
</property>
<property name="value">
- <number>1024</number>
+ <number>256</number>
</property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QSpinBox" name="minMemSpinBox">
+ <item row="1" column="2">
+ <widget class="QSpinBox" name="maxMemSpinBox">
<property name="toolTip">
- <string>The amount of memory Minecraft is started with.</string>
+ <string>The maximum amount of memory Minecraft is allowed to use.</string>
</property>
<property name="suffix">
<string notr="true"> MiB</string>
@@ -154,17 +175,17 @@
<number>128</number>
</property>
<property name="maximum">
- <number>65536</number>
+ <number>1048576</number>
</property>
<property name="singleStep">
<number>128</number>
</property>
<property name="value">
- <number>256</number>
+ <number>1024</number>
</property>
</widget>
</item>
- <item row="2" column="1">
+ <item row="2" column="2">
<widget class="QSpinBox" name="permGenSpinBox">
<property name="toolTip">
<string>The amount of memory available to store loaded Java classes.</string>
@@ -186,24 +207,16 @@
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="labelPermGen">
+ <item row="1" column="3">
+ <widget class="QLabel" name="labelMaxMemIcon">
<property name="text">
- <string notr="true">PermGen:</string>
+ <string notr="true"/>
</property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="labelMaxMem">
- <property name="text">
- <string>Maximum memory allocation:</string>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
</property>
- </widget>
- </item>
- <item row="3" column="0" colspan="2">
- <widget class="QLabel" name="labelPermgenNote">
- <property name="text">
- <string>Note: Permgen is set automatically by Java 8 and later</string>
+ <property name="buddy">
+ <cstring>maxMemSpinBox</cstring>
</property>
</widget>
</item>
diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp
new file mode 100644
index 00000000..4de80468
--- /dev/null
+++ b/launcher/ui/pages/instance/ManagedPackPage.cpp
@@ -0,0 +1,433 @@
+// SPDX-FileCopyrightText: 2022 flow <flowlnlnln@gmail.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+#include "ManagedPackPage.h"
+#include "ui_ManagedPackPage.h"
+
+#include <QListView>
+#include <QProxyStyle>
+#include <QStyleFactory>
+
+#include <HoeDown.h>
+
+#include "Application.h"
+#include "BuildConfig.h"
+#include "InstanceImportTask.h"
+#include "InstanceList.h"
+#include "InstanceTask.h"
+#include "Json.h"
+
+#include "modplatform/modrinth/ModrinthPackManifest.h"
+
+#include "ui/InstanceWindow.h"
+#include "ui/dialogs/CustomMessageBox.h"
+#include "ui/dialogs/ProgressDialog.h"
+
+/** This is just to override the combo box popup behavior so that the combo box doesn't take the whole screen.
+ * ... thanks Qt.
+ */
+class NoBigComboBoxStyle : public QProxyStyle {
+ Q_OBJECT
+
+ public:
+ NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {}
+
+ // clang-format off
+ int styleHint(QStyle::StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override
+ {
+ if (hint == QStyle::SH_ComboBox_Popup)
+ return false;
+
+ return QProxyStyle::styleHint(hint, option, widget, returnData);
+ }
+ // clang-format on
+};
+
+ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent)
+{
+ if (type == "modrinth")
+ return new ModrinthManagedPackPage(inst, nullptr, parent);
+ if (type == "flame" && (APPLICATION->capabilities() & Application::SupportsFlame))
+ return new FlameManagedPackPage(inst, nullptr, parent);
+
+ return new GenericManagedPackPage(inst, nullptr, parent);
+}
+
+ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
+ : QWidget(parent), m_instance_window(instance_window), ui(new Ui::ManagedPackPage), m_inst(inst)
+{
+ Q_ASSERT(inst);
+
+ ui->setupUi(this);
+
+ // NOTE: GTK2 themes crash with the proxy style.
+ // This seems like an upstream bug, so there's not much else that can be done.
+ if (!QStyleFactory::keys().contains("gtk2"))
+ ui->versionsComboBox->setStyle(new NoBigComboBoxStyle(ui->versionsComboBox->style()));
+
+ ui->reloadButton->setVisible(false);
+ connect(ui->reloadButton, &QPushButton::clicked, this, [this](bool){
+ ui->reloadButton->setVisible(false);
+
+ m_loaded = false;
+ // Pretend we're opening the page again
+ openedImpl();
+ });
+}
+
+ManagedPackPage::~ManagedPackPage()
+{
+ delete ui;
+}
+
+void ManagedPackPage::openedImpl()
+{
+ ui->packName->setText(m_inst->getManagedPackName());
+ ui->packVersion->setText(m_inst->getManagedPackVersionName());
+ ui->packOrigin->setText(tr("Website: <a href=%1>%2</a> | Pack ID: %3 | Version ID: %4")
+ .arg(url(), displayName(), m_inst->getManagedPackID(), m_inst->getManagedPackVersionID()));
+
+ parseManagedPack();
+}
+
+QString ManagedPackPage::displayName() const
+{
+ auto type = m_inst->getManagedPackType();
+ if (type.isEmpty())
+ return {};
+ if (type == "flame")
+ type = "CurseForge";
+ return type.replace(0, 1, type[0].toUpper());
+}
+
+QIcon ManagedPackPage::icon() const
+{
+ return APPLICATION->getThemedIcon(m_inst->getManagedPackType());
+}
+
+QString ManagedPackPage::helpPage() const
+{
+ return {};
+}
+
+void ManagedPackPage::retranslate()
+{
+ ui->retranslateUi(this);
+}
+
+bool ManagedPackPage::shouldDisplay() const
+{
+ return m_inst->isManagedPack();
+}
+
+bool ManagedPackPage::runUpdateTask(InstanceTask* task)
+{
+ Q_ASSERT(task);
+
+ unique_qobject_ptr<Task> wrapped_task(APPLICATION->instances()->wrapInstanceTask(task));
+
+ connect(task, &Task::failed,
+ [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
+ connect(task, &Task::succeeded, [this, task]() {
+ QStringList warnings = task->warnings();
+ if (warnings.count())
+ CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
+ });
+ connect(task, &Task::aborted, [this] {
+ CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information)
+ ->show();
+ });
+
+ ProgressDialog loadDialog(this);
+ loadDialog.setSkipButton(true, tr("Abort"));
+ loadDialog.execWithTask(task);
+
+ return task->wasSuccessful();
+}
+
+void ManagedPackPage::suggestVersion()
+{
+ ui->updateButton->setText(tr("Update pack"));
+ ui->updateButton->setDisabled(false);
+}
+
+void ManagedPackPage::setFailState()
+{
+ qDebug() << "Setting fail state!";
+
+ // We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
+ ui->versionsComboBox->blockSignals(true);
+ ui->versionsComboBox->clear();
+ ui->versionsComboBox->addItem(tr("Failed to search for available versions."), {});
+ ui->versionsComboBox->blockSignals(false);
+
+ ui->changelogTextBrowser->setText(tr("Failed to request changelog data for this modpack."));
+
+ ui->updateButton->setText(tr("Cannot update!"));
+ ui->updateButton->setDisabled(true);
+
+ ui->reloadButton->setVisible(true);
+}
+
+ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
+ : ManagedPackPage(inst, instance_window, parent)
+{
+ Q_ASSERT(inst->isManagedPack());
+ connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
+ connect(ui->updateButton, &QPushButton::pressed, this, &ModrinthManagedPackPage::update);
+}
+
+// MODRINTH
+
+void ModrinthManagedPackPage::parseManagedPack()
+{
+ qDebug() << "Parsing Modrinth pack";
+
+ // No need for the extra work because we already have everything we need.
+ if (m_loaded)
+ return;
+
+ if (m_fetch_job && m_fetch_job->isRunning())
+ m_fetch_job->abort();
+
+ m_fetch_job.reset(new NetJob(QString("Modrinth::PackVersions(%1)").arg(m_inst->getManagedPackName()), APPLICATION->network()));
+ auto response = std::make_shared<QByteArray>();
+
+ QString id = m_inst->getManagedPackID();
+
+ m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response.get()));
+
+ QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
+ QJsonParseError parse_error{};
+ QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
+ if (parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset
+ << " reason: " << parse_error.errorString();
+ qWarning() << *response;
+
+ setFailState();
+
+ return;
+ }
+
+ try {
+ Modrinth::loadIndexedVersions(m_pack, doc);
+ } catch (const JSONValidationError& e) {
+ qDebug() << *response;
+ qWarning() << "Error while reading modrinth modpack version: " << e.cause();
+
+ setFailState();
+ return;
+ }
+
+ // We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
+ ui->versionsComboBox->blockSignals(true);
+ ui->versionsComboBox->clear();
+ ui->versionsComboBox->blockSignals(false);
+
+ for (auto version : m_pack.versions) {
+ QString name = version.version;
+
+ if (!version.name.contains(version.version))
+ name = QString("%1 — %2").arg(version.name, version.version);
+
+ // NOTE: the id from version isn't the same id in the modpack format spec...
+ // e.g. HexMC's 4.4.0 has versionId 4.0.0 in the modpack index..............
+ if (version.version == m_inst->getManagedPackVersionName())
+ name = tr("%1 (Current)").arg(name);
+
+
+ ui->versionsComboBox->addItem(name, QVariant(version.id));
+ }
+
+ suggestVersion();
+
+ m_loaded = true;
+ });
+ QObject::connect(m_fetch_job.get(), &NetJob::failed, this, &ModrinthManagedPackPage::setFailState);
+ QObject::connect(m_fetch_job.get(), &NetJob::aborted, this, &ModrinthManagedPackPage::setFailState);
+
+ ui->changelogTextBrowser->setText(tr("Fetching changelogs..."));
+
+ m_fetch_job->start();
+}
+
+QString ModrinthManagedPackPage::url() const
+{
+ return "https://modrinth.com/mod/" + m_inst->getManagedPackID();
+}
+
+void ModrinthManagedPackPage::suggestVersion()
+{
+ auto index = ui->versionsComboBox->currentIndex();
+ auto version = m_pack.versions.at(index);
+
+ HoeDown md_parser;
+ ui->changelogTextBrowser->setHtml(md_parser.process(version.changelog.toUtf8()));
+
+ ManagedPackPage::suggestVersion();
+}
+
+void ModrinthManagedPackPage::update()
+{
+ auto index = ui->versionsComboBox->currentIndex();
+ auto version = m_pack.versions.at(index);
+
+ QMap<QString, QString> extra_info;
+ // NOTE: Don't use 'm_pack.id' here, since we didn't completely parse all the metadata for the pack, including this field.
+ extra_info.insert("pack_id", m_inst->getManagedPackID());
+ extra_info.insert("pack_version_id", version.id);
+ extra_info.insert("original_instance_id", m_inst->id());
+
+ auto extracted = new InstanceImportTask(version.download_url, this, std::move(extra_info));
+
+ InstanceName inst_name(m_inst->getManagedPackName(), version.version);
+ inst_name.setName(m_inst->name().replace(m_inst->getManagedPackVersionName(), version.version));
+ extracted->setName(inst_name);
+
+ extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
+ extracted->setIcon(m_inst->iconKey());
+ extracted->setConfirmUpdate(false);
+
+ auto did_succeed = runUpdateTask(extracted);
+
+ if (m_instance_window && did_succeed)
+ m_instance_window->close();
+}
+
+// FLAME
+
+FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
+ : ManagedPackPage(inst, instance_window, parent)
+{
+ Q_ASSERT(inst->isManagedPack());
+ connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
+ connect(ui->updateButton, &QPushButton::pressed, this, &FlameManagedPackPage::update);
+}
+
+void FlameManagedPackPage::parseManagedPack()
+{
+ qDebug() << "Parsing Flame pack";
+
+ // We need to tell the user to redownload the pack, since we didn't save the required info previously
+ if (m_inst->getManagedPackID().isEmpty()) {
+ setFailState();
+ QString message =
+ tr("<h1>Hey there!</h1>"
+ "<h4>"
+ "It seems like your Pack ID is null. This is because of a bug in older versions of the launcher.<br/>"
+ "Unfortunately, we can't do the proper API requests without this information.<br/>"
+ "<br/>"
+ "So, in order for this feature to work, you will need to re-download the modpack from the built-in downloader.<br/>"
+ "<br/>"
+ "Don't worry though, it will ask you to update this instance instead, so you'll not lose this instance!"
+ "</h4>");
+
+ ui->changelogTextBrowser->setHtml(message);
+ return;
+ }
+
+ // No need for the extra work because we already have everything we need.
+ if (m_loaded)
+ return;
+
+ if (m_fetch_job && m_fetch_job->isRunning())
+ m_fetch_job->abort();
+
+ m_fetch_job.reset(new NetJob(QString("Flame::PackVersions(%1)").arg(m_inst->getManagedPackName()), APPLICATION->network()));
+ auto response = std::make_shared<QByteArray>();
+
+ QString id = m_inst->getManagedPackID();
+
+ m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response.get()));
+
+ QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
+ QJsonParseError parse_error{};
+ QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
+ if (parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Flame at " << parse_error.offset
+ << " reason: " << parse_error.errorString();
+ qWarning() << *response;
+
+ setFailState();
+
+ return;
+ }
+
+ try {
+ auto obj = doc.object();
+ auto data = Json::ensureArray(obj, "data");
+ Flame::loadIndexedPackVersions(m_pack, data);
+ } catch (const JSONValidationError& e) {
+ qDebug() << *response;
+ qWarning() << "Error while reading flame modpack version: " << e.cause();
+
+ setFailState();
+ return;
+ }
+
+ // We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
+ ui->versionsComboBox->blockSignals(true);
+ ui->versionsComboBox->clear();
+ ui->versionsComboBox->blockSignals(false);
+
+ for (auto version : m_pack.versions) {
+ QString name = version.version;
+
+ if (version.fileId == m_inst->getManagedPackVersionID().toInt())
+ name = tr("%1 (Current)").arg(name);
+
+ ui->versionsComboBox->addItem(name, QVariant(version.fileId));
+ }
+
+ suggestVersion();
+
+ m_loaded = true;
+ });
+ QObject::connect(m_fetch_job.get(), &NetJob::failed, this, &FlameManagedPackPage::setFailState);
+ QObject::connect(m_fetch_job.get(), &NetJob::aborted, this, &FlameManagedPackPage::setFailState);
+
+ m_fetch_job->start();
+}
+
+QString FlameManagedPackPage::url() const
+{
+ // FIXME: We should display the websiteUrl field, but this requires doing the API request first :(
+ return {};
+}
+
+void FlameManagedPackPage::suggestVersion()
+{
+ auto index = ui->versionsComboBox->currentIndex();
+ auto version = m_pack.versions.at(index);
+
+ ui->changelogTextBrowser->setHtml(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId));
+
+ ManagedPackPage::suggestVersion();
+}
+
+void FlameManagedPackPage::update()
+{
+ auto index = ui->versionsComboBox->currentIndex();
+ auto version = m_pack.versions.at(index);
+
+ QMap<QString, QString> extra_info;
+ extra_info.insert("pack_id", m_inst->getManagedPackID());
+ extra_info.insert("pack_version_id", QString::number(version.fileId));
+ extra_info.insert("original_instance_id", m_inst->id());
+
+ auto extracted = new InstanceImportTask(version.downloadUrl, this, std::move(extra_info));
+
+ extracted->setName(m_inst->name());
+ extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
+ extracted->setIcon(m_inst->iconKey());
+ extracted->setConfirmUpdate(false);
+
+ auto did_succeed = runUpdateTask(extracted);
+
+ if (m_instance_window && did_succeed)
+ m_instance_window->close();
+}
+
+#include "ManagedPackPage.moc"
diff --git a/launcher/ui/pages/instance/ManagedPackPage.h b/launcher/ui/pages/instance/ManagedPackPage.h
new file mode 100644
index 00000000..d29a5e88
--- /dev/null
+++ b/launcher/ui/pages/instance/ManagedPackPage.h
@@ -0,0 +1,152 @@
+// SPDX-FileCopyrightText: 2022 flow <flowlnlnln@gmail.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+#pragma once
+
+#include "BaseInstance.h"
+
+#include "modplatform/modrinth/ModrinthAPI.h"
+#include "modplatform/modrinth/ModrinthPackManifest.h"
+
+#include "modplatform/flame/FlameAPI.h"
+#include "modplatform/flame/FlamePackIndex.h"
+
+#include "ui/pages/BasePage.h"
+
+#include <QWidget>
+
+namespace Ui {
+class ManagedPackPage;
+}
+
+class InstanceTask;
+class InstanceWindow;
+
+class ManagedPackPage : public QWidget, public BasePage {
+ Q_OBJECT
+
+ public:
+ inline static ManagedPackPage* createPage(BaseInstance* inst, QWidget* parent = nullptr)
+ {
+ return ManagedPackPage::createPage(inst, inst->getManagedPackType(), parent);
+ }
+
+ static ManagedPackPage* createPage(BaseInstance* inst, QString type, QWidget* parent = nullptr);
+ ~ManagedPackPage() override;
+
+ [[nodiscard]] QString displayName() const override;
+ [[nodiscard]] QIcon icon() const override;
+ [[nodiscard]] QString helpPage() const override;
+ [[nodiscard]] QString id() const override { return "managed_pack"; }
+ [[nodiscard]] bool shouldDisplay() const override;
+
+ void openedImpl() override;
+
+ bool apply() override { return true; }
+ void retranslate() override;
+
+ /** Gets the necessary information about the managed pack, such as
+ * available versions*/
+ virtual void parseManagedPack(){};
+
+ /** URL of the managed pack.
+ * Not the version-specific one.
+ */
+ [[nodiscard]] virtual QString url() const { return {}; };
+
+ void setInstanceWindow(InstanceWindow* window) { m_instance_window = window; }
+
+ public slots:
+ /** Gets the current version selection and update the UI, including the update button and the changelog.
+ */
+ virtual void suggestVersion();
+
+ virtual void update(){};
+
+ protected slots:
+ /** Does the necessary UI changes for when something failed.
+ *
+ * This includes:
+ * - Setting an appropriate text on the version selector to indicate a fail;
+ * - Setting an appropriate text on the changelog text browser to indicate a fail;
+ * - Disable the update button.
+ */
+ void setFailState();
+
+ protected:
+ ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
+
+ /** Run the InstanceTask, with a progress dialog and all.
+ * Similar to MainWindow::instanceFromInstanceTask
+ *
+ * Returns whether the task was successful.
+ */
+ bool runUpdateTask(InstanceTask*);
+
+ protected:
+ InstanceWindow* m_instance_window = nullptr;
+
+ Ui::ManagedPackPage* ui;
+ BaseInstance* m_inst;
+
+ bool m_loaded = false;
+};
+
+/** Simple page for when we aren't a managed pack. */
+class GenericManagedPackPage final : public ManagedPackPage {
+ Q_OBJECT
+
+ public:
+ GenericManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr)
+ : ManagedPackPage(inst, instance_window, parent)
+ {}
+ ~GenericManagedPackPage() override = default;
+
+ // TODO: We may want to show this page with some useful info at some point.
+ [[nodiscard]] bool shouldDisplay() const override { return false; };
+};
+
+class ModrinthManagedPackPage final : public ManagedPackPage {
+ Q_OBJECT
+
+ public:
+ ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
+ ~ModrinthManagedPackPage() override = default;
+
+ void parseManagedPack() override;
+ [[nodiscard]] QString url() const override;
+
+ public slots:
+ void suggestVersion() override;
+
+ void update() override;
+
+ private:
+ NetJob::Ptr m_fetch_job = nullptr;
+
+ Modrinth::Modpack m_pack;
+ ModrinthAPI m_api;
+};
+
+class FlameManagedPackPage final : public ManagedPackPage {
+ Q_OBJECT
+
+ public:
+ FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
+ ~FlameManagedPackPage() override = default;
+
+ void parseManagedPack() override;
+ [[nodiscard]] QString url() const override;
+
+ public slots:
+ void suggestVersion() override;
+
+ void update() override;
+
+ private:
+ NetJob::Ptr m_fetch_job = nullptr;
+
+ Flame::IndexedPack m_pack;
+ FlameAPI m_api;
+};
diff --git a/launcher/ui/pages/instance/ManagedPackPage.ui b/launcher/ui/pages/instance/ManagedPackPage.ui
new file mode 100644
index 00000000..bbe44a94
--- /dev/null
+++ b/launcher/ui/pages/instance/ManagedPackPage.ui
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ManagedPackPage</class>
+ <widget class="QWidget" name="ManagedPackPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>731</width>
+ <height>538</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="packInformationBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Pack information</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <layout class="QHBoxLayout" name="packNameLayout">
+ <item>
+ <widget class="QLabel" name="packNameLabel">
+ <property name="text">
+ <string>Pack name:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="packName">
+ <property name="text">
+ <string notr="true">placeholder</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <layout class="QHBoxLayout" name="packVersionLayout">
+ <item>
+ <widget class="QLabel" name="packVersionLabel">
+ <property name="text">
+ <string>Current version:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="packVersion">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string notr="true">placeholder</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <layout class="QHBoxLayout" name="packOriginLayout">
+ <item>
+ <widget class="QLabel" name="packOriginLabel">
+ <property name="text">
+ <string>Provider information:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="packOrigin">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string notr="true">placeholder</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="updateToVersionLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Update to version:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="versionsComboBox"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="updateButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Fetching versions...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="changelogBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Changelog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QTextBrowser" name="changelogTextBrowser">
+ <property name="placeholderText">
+ <string>No changelog available for this version!</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QPushButton" name="reloadButton">
+ <property name="text">
+ <string>Reload page</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp
index f0106066..0a2e6155 100644
--- a/launcher/ui/pages/instance/ModFolderPage.cpp
+++ b/launcher/ui/pages/instance/ModFolderPage.cpp
@@ -108,13 +108,13 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
disconnect(mods.get(), &ModFolderModel::updateFinished, this, 0);
});
+ connect(m_instance, &BaseInstance::runningStatusChanged, this, &ModFolderPage::runningStateChanged);
ModFolderPage::runningStateChanged(m_instance && m_instance->isRunning());
}
}
void ModFolderPage::runningStateChanged(bool running)
{
- ExternalResourcesPage::runningStateChanged(running);
ui->actionDownloadItem->setEnabled(!running);
ui->actionUpdateItem->setEnabled(!running);
ui->actionAddItem->setEnabled(!running);
diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h
index c9a55bde..f20adf34 100644
--- a/launcher/ui/pages/instance/ModFolderPage.h
+++ b/launcher/ui/pages/instance/ModFolderPage.h
@@ -53,12 +53,12 @@ class ModFolderPage : public ExternalResourcesPage {
virtual QString helpPage() const override { return "Loader-mods"; }
virtual bool shouldDisplay() const override;
- void runningStateChanged(bool running) override;
public slots:
bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override;
private slots:
+ void runningStateChanged(bool running);
void removeItem() override;
void installMods();
diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp
index c97253e4..0092aef3 100644
--- a/launcher/ui/pages/instance/ScreenshotsPage.cpp
+++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp
@@ -537,6 +537,19 @@ void ScreenshotsPage::openedImpl()
ui->listView->setModel(nullptr);
}
}
+
+ auto const setting_name = QString("WideBarVisibility_%1").arg(id());
+ if (!APPLICATION->settings()->contains(setting_name))
+ m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name);
+ else
+ m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name);
+
+ ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray());
+}
+
+void ScreenshotsPage::closedImpl()
+{
+ m_wide_bar_setting->set(ui->toolBar->getVisibilityState());
}
#include "ScreenshotsPage.moc"
diff --git a/launcher/ui/pages/instance/ScreenshotsPage.h b/launcher/ui/pages/instance/ScreenshotsPage.h
index cdd53cc9..89611b6d 100644
--- a/launcher/ui/pages/instance/ScreenshotsPage.h
+++ b/launcher/ui/pages/instance/ScreenshotsPage.h
@@ -40,6 +40,8 @@
#include "ui/pages/BasePage.h"
#include <Application.h>
+#include "settings/Setting.h"
+
class QFileSystemModel;
class QIdentityProxyModel;
class QItemSelection;
@@ -60,7 +62,8 @@ public:
explicit ScreenshotsPage(QString path, QWidget *parent = 0);
virtual ~ScreenshotsPage();
- virtual void openedImpl() override;
+ void openedImpl() override;
+ void closedImpl() override;
enum
{
@@ -111,4 +114,6 @@ private:
QString m_folder;
bool m_valid = false;
bool m_uploadActive = false;
+
+ std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
};
diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp
index a4f9f330..08389aa3 100644
--- a/launcher/ui/pages/instance/ServersPage.cpp
+++ b/launcher/ui/pages/instance/ServersPage.cpp
@@ -766,11 +766,21 @@ void ServersPage::updateState()
void ServersPage::openedImpl()
{
m_model->observe();
+
+ auto const setting_name = QString("WideBarVisibility_%1").arg(id());
+ if (!APPLICATION->settings()->contains(setting_name))
+ m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name);
+ else
+ m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name);
+
+ ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray());
}
void ServersPage::closedImpl()
{
m_model->unobserve();
+
+ m_wide_bar_setting->set(ui->toolBar->getVisibilityState());
}
void ServersPage::on_actionAdd_triggered()
diff --git a/launcher/ui/pages/instance/ServersPage.h b/launcher/ui/pages/instance/ServersPage.h
index 37399d49..476e7d70 100644
--- a/launcher/ui/pages/instance/ServersPage.h
+++ b/launcher/ui/pages/instance/ServersPage.h
@@ -42,6 +42,8 @@
#include "ui/pages/BasePage.h"
#include <Application.h>
+#include "settings/Setting.h"
+
namespace Ui
{
class ServersPage;
@@ -68,7 +70,7 @@ public:
}
virtual QIcon icon() const override
{
- return APPLICATION->getThemedIcon("unknown_server");
+ return APPLICATION->getThemedIcon("server");
}
virtual QString id() const override
{
@@ -112,5 +114,7 @@ private: // data
Ui::ServersPage *ui = nullptr;
ServersModel * m_model = nullptr;
InstancePtr m_inst = nullptr;
+
+ std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
};
diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp
index a021c633..c8a65f10 100644
--- a/launcher/ui/pages/instance/VersionPage.cpp
+++ b/launcher/ui/pages/instance/VersionPage.cpp
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* 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
@@ -125,6 +126,21 @@ void VersionPage::retranslate()
ui->retranslateUi(this);
}
+void VersionPage::openedImpl()
+{
+ auto const setting_name = QString("WideBarVisibility_%1").arg(id());
+ if (!APPLICATION->settings()->contains(setting_name))
+ m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name);
+ else
+ m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name);
+
+ ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray());
+}
+void VersionPage::closedImpl()
+{
+ m_wide_bar_setting->set(ui->toolBar->getVisibilityState());
+}
+
QMenu * VersionPage::createPopupMenu()
{
QMenu* filteredMenu = QMainWindow::createPopupMenu();
@@ -270,6 +286,7 @@ void VersionPage::updateButtons(int row)
ui->actionInstall_mods->setEnabled(controlsEnabled);
ui->actionReplace_Minecraft_jar->setEnabled(controlsEnabled);
ui->actionAdd_to_Minecraft_jar->setEnabled(controlsEnabled);
+ ui->actionAdd_Agents->setEnabled(controlsEnabled);
}
bool VersionPage::reloadPackProfile()
@@ -342,6 +359,18 @@ void VersionPage::on_actionReplace_Minecraft_jar_triggered()
updateButtons();
}
+
+void VersionPage::on_actionAdd_Agents_triggered()
+{
+ QStringList list = GuiUtil::BrowseForFiles("agent", tr("Select agents"), tr("Java agents (*.jar)"),
+ APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
+
+ if (!list.isEmpty())
+ m_profile->installAgents(list);
+
+ updateButtons();
+}
+
void VersionPage::on_actionMove_up_triggered()
{
try
diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h
index 979311fc..166f36bb 100644
--- a/launcher/ui/pages/instance/VersionPage.h
+++ b/launcher/ui/pages/instance/VersionPage.h
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* 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
@@ -69,6 +70,9 @@ public:
virtual bool shouldDisplay() const override;
void retranslate() override;
+ void openedImpl() override;
+ void closedImpl() override;
+
private slots:
void on_actionChange_version_triggered();
void on_actionInstall_Forge_triggered();
@@ -82,6 +86,7 @@ private slots:
void on_actionMove_down_triggered();
void on_actionAdd_to_Minecraft_jar_triggered();
void on_actionReplace_Minecraft_jar_triggered();
+ void on_actionAdd_Agents_triggered();
void on_actionRevert_triggered();
void on_actionEdit_triggered();
void on_actionInstall_mods_triggered();
@@ -114,6 +119,8 @@ private:
int currentIdx = 0;
bool controlsEnabled = false;
+ std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
+
public slots:
void versionCurrent(const QModelIndex &current, const QModelIndex &previous);
diff --git a/launcher/ui/pages/instance/VersionPage.ui b/launcher/ui/pages/instance/VersionPage.ui
index 14b7cd9f..74b9568a 100644
--- a/launcher/ui/pages/instance/VersionPage.ui
+++ b/launcher/ui/pages/instance/VersionPage.ui
@@ -109,6 +109,7 @@
<addaction name="separator"/>
<addaction name="actionAdd_to_Minecraft_jar"/>
<addaction name="actionReplace_Minecraft_jar"/>
+ <addaction name="actionAdd_Agents"/>
<addaction name="actionAdd_Empty"/>
<addaction name="separator"/>
<addaction name="actionMinecraftFolder"/>
@@ -226,6 +227,14 @@
<string>Replace Minecraft.jar</string>
</property>
</action>
+ <action name="actionAdd_Agents">
+ <property name="text">
+ <string>Add Agents</string>
+ </property>
+ <property name="toolTip">
+ <string>Add Java agents.</string>
+ </property>
+ </action>
<action name="actionAdd_Empty">
<property name="text">
<string>Add Empty</string>
diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp
index 7819d077..b8b48ef9 100644
--- a/launcher/ui/pages/instance/WorldListPage.cpp
+++ b/launcher/ui/pages/instance/WorldListPage.cpp
@@ -113,11 +113,21 @@ WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr<WorldList> worl
void WorldListPage::openedImpl()
{
m_worlds->startWatching();
+
+ auto const setting_name = QString("WideBarVisibility_%1").arg(id());
+ if (!APPLICATION->settings()->contains(setting_name))
+ m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name);
+ else
+ m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name);
+
+ ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray());
}
void WorldListPage::closedImpl()
{
m_worlds->stopWatching();
+
+ m_wide_bar_setting->set(ui->toolBar->getVisibilityState());
}
WorldListPage::~WorldListPage()
diff --git a/launcher/ui/pages/instance/WorldListPage.h b/launcher/ui/pages/instance/WorldListPage.h
index 1dc9e53e..925521be 100644
--- a/launcher/ui/pages/instance/WorldListPage.h
+++ b/launcher/ui/pages/instance/WorldListPage.h
@@ -42,6 +42,8 @@
#include <Application.h>
#include <LoggedProcess.h>
+#include "settings/Setting.h"
+
class WorldList;
namespace Ui
{
@@ -102,6 +104,8 @@ private:
unique_qobject_ptr<LoggedProcess> m_mceditProcess;
bool m_mceditStarting = false;
+ std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
+
private slots:
void on_actionCopy_Seed_triggered();
void on_actionMCEdit_triggered();
diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp
index a65b6585..f9ac4a78 100644
--- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp
+++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp
@@ -197,12 +197,18 @@ void FlamePage::suggestCurrent()
return;
}
- if (selectedVersion.isEmpty() || selectedVersion == "-1") {
+ if (m_selected_version_index == -1) {
dialog->setSuggestedPack();
return;
}
- dialog->setSuggestedPack(current.name, new InstanceImportTask(selectedVersion,this));
+ auto version = current.versions.at(m_selected_version_index);
+
+ QMap<QString, QString> extra_info;
+ extra_info.insert("pack_id", QString::number(current.addonId));
+ extra_info.insert("pack_version_id", QString::number(version.fileId));
+
+ dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info)));
QString editedLogoName;
editedLogoName = "curseforge_" + current.logoName.section(".", 0, 0);
listModel->getLogo(current.logoName, current.logoUrl,
@@ -211,11 +217,18 @@ void FlamePage::suggestCurrent()
void FlamePage::onVersionSelectionChanged(QString data)
{
- if (data.isNull() || data.isEmpty()) {
- selectedVersion = "";
+ bool is_blocked = false;
+ ui->versionSelectionBox->currentData().toInt(&is_blocked);
+
+ if (data.isNull() || data.isEmpty() || is_blocked) {
+ m_selected_version_index = -1;
return;
}
- selectedVersion = ui->versionSelectionBox->currentData().toString();
+
+ m_selected_version_index = ui->versionSelectionBox->currentIndex();
+
+ Q_ASSERT(current.versions.at(m_selected_version_index).downloadUrl == ui->versionSelectionBox->currentData().toString());
+
suggestCurrent();
}
diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.h b/launcher/ui/pages/modplatform/flame/FlamePage.h
index 8130e416..8bdca38e 100644
--- a/launcher/ui/pages/modplatform/flame/FlamePage.h
+++ b/launcher/ui/pages/modplatform/flame/FlamePage.h
@@ -99,5 +99,5 @@ private:
Flame::ListModel* listModel = nullptr;
Flame::IndexedPack current;
- QString selectedVersion;
+ int m_selected_version_index = -1;
};
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
index 4482774c..8ab2ad1d 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
@@ -300,7 +300,11 @@ void ModrinthPage::suggestCurrent()
for (auto& ver : current.versions) {
if (ver.id == selectedVersion) {
- dialog->setSuggestedPack(current.name, ver.version, new InstanceImportTask(ver.download_url, this));
+ QMap<QString, QString> extra_info;
+ extra_info.insert("pack_id", current.id);
+ extra_info.insert("pack_version_id", ver.id);
+
+ dialog->setSuggestedPack(current.name, ver.version, new InstanceImportTask(ver.download_url, this, std::move(extra_info)));
auto iconName = current.iconName;
m_model->getLogo(iconName, current.iconUrl.toString(),
[this, iconName](QString logo) { dialog->setSuggestedIconFromFile(logo, iconName); });
diff --git a/launcher/ui/themes/ITheme.cpp b/launcher/ui/themes/ITheme.cpp
index 7247b444..8bfc466d 100644
--- a/launcher/ui/themes/ITheme.cpp
+++ b/launcher/ui/themes/ITheme.cpp
@@ -6,19 +6,14 @@
void ITheme::apply(bool)
{
+ APPLICATION->setStyleSheet(QString());
QApplication::setStyle(QStyleFactory::create(qtTheme()));
- if(hasColorScheme())
- {
+ if (hasColorScheme()) {
QApplication::setPalette(colorScheme());
}
- if(hasStyleSheet())
- {
+ if (hasStyleSheet())
APPLICATION->setStyleSheet(appStyleSheet());
- }
- else
- {
- APPLICATION->setStyleSheet(QString());
- }
+
QDir::setSearchPaths("theme", searchPaths());
}
diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp
index c7c4dbbd..15994319 100644
--- a/launcher/ui/widgets/JavaSettingsWidget.cpp
+++ b/launcher/ui/widgets/JavaSettingsWidget.cpp
@@ -71,6 +71,7 @@ void JavaSettingsWidget::setupUi()
m_memoryGroupBox->setObjectName(QStringLiteral("memoryGroupBox"));
m_gridLayout_2 = new QGridLayout(m_memoryGroupBox);
m_gridLayout_2->setObjectName(QStringLiteral("gridLayout_2"));
+ m_gridLayout_2->setColumnStretch(0, 1);
m_labelMinMem = new QLabel(m_memoryGroupBox);
m_labelMinMem->setObjectName(QStringLiteral("labelMinMem"));
@@ -80,7 +81,7 @@ void JavaSettingsWidget::setupUi()
m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox"));
m_minMemSpinBox->setSuffix(QStringLiteral(" MiB"));
m_minMemSpinBox->setMinimum(128);
- m_minMemSpinBox->setMaximum(m_availableMemory);
+ m_minMemSpinBox->setMaximum(1048576);
m_minMemSpinBox->setSingleStep(128);
m_labelMinMem->setBuddy(m_minMemSpinBox);
m_gridLayout_2->addWidget(m_minMemSpinBox, 0, 1, 1, 1);
@@ -93,11 +94,15 @@ void JavaSettingsWidget::setupUi()
m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox"));
m_maxMemSpinBox->setSuffix(QStringLiteral(" MiB"));
m_maxMemSpinBox->setMinimum(128);
- m_maxMemSpinBox->setMaximum(m_availableMemory);
+ m_maxMemSpinBox->setMaximum(1048576);
m_maxMemSpinBox->setSingleStep(128);
m_labelMaxMem->setBuddy(m_maxMemSpinBox);
m_gridLayout_2->addWidget(m_maxMemSpinBox, 1, 1, 1, 1);
+ m_labelMaxMemIcon = new QLabel(m_memoryGroupBox);
+ m_labelMaxMemIcon->setObjectName(QStringLiteral("labelMaxMemIcon"));
+ m_gridLayout_2->addWidget(m_labelMaxMemIcon, 1, 2, 1, 1);
+
m_labelPermGen = new QLabel(m_memoryGroupBox);
m_labelPermGen->setObjectName(QStringLiteral("labelPermGen"));
m_labelPermGen->setText(QStringLiteral("PermGen:"));
@@ -108,7 +113,7 @@ void JavaSettingsWidget::setupUi()
m_permGenSpinBox->setObjectName(QStringLiteral("permGenSpinBox"));
m_permGenSpinBox->setSuffix(QStringLiteral(" MiB"));
m_permGenSpinBox->setMinimum(64);
- m_permGenSpinBox->setMaximum(m_availableMemory);
+ m_permGenSpinBox->setMaximum(1048576);
m_permGenSpinBox->setSingleStep(8);
m_gridLayout_2->addWidget(m_permGenSpinBox, 2, 1, 1, 1);
m_permGenSpinBox->setVisible(false);
@@ -130,6 +135,7 @@ void JavaSettingsWidget::initialize()
m_minMemSpinBox->setValue(observedMinMemory);
m_maxMemSpinBox->setValue(observedMaxMemory);
m_permGenSpinBox->setValue(observedPermGenMemory);
+ updateThresholds();
}
void JavaSettingsWidget::refresh()
@@ -210,9 +216,9 @@ int JavaSettingsWidget::permGenSize() const
void JavaSettingsWidget::memoryValueChanged(int)
{
bool actuallyChanged = false;
- int min = m_minMemSpinBox->value();
- int max = m_maxMemSpinBox->value();
- int permgen = m_permGenSpinBox->value();
+ unsigned int min = m_minMemSpinBox->value();
+ unsigned int max = m_maxMemSpinBox->value();
+ unsigned int permgen = m_permGenSpinBox->value();
QObject *obj = sender();
if (obj == m_minMemSpinBox && min != observedMinMemory)
{
@@ -242,6 +248,7 @@ void JavaSettingsWidget::memoryValueChanged(int)
if(actuallyChanged)
{
checkJavaPathOnEdit(m_javaPathTextBox->text());
+ updateThresholds();
}
}
@@ -435,3 +442,26 @@ void JavaSettingsWidget::retranslate()
m_permGenSpinBox->setToolTip(tr("The amount of memory available to store loaded Java classes."));
m_javaBrowseBtn->setText(tr("Browse"));
}
+
+void JavaSettingsWidget::updateThresholds()
+{
+ QString iconName;
+
+ if (observedMaxMemory >= m_availableMemory) {
+ iconName = "status-bad";
+ m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity."));
+ } else if (observedMaxMemory > (m_availableMemory * 0.9)) {
+ iconName = "status-yellow";
+ m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
+ } else {
+ iconName = "status-good";
+ m_labelMaxMemIcon->setToolTip("");
+ }
+
+ {
+ auto height = m_labelMaxMemIcon->fontInfo().pixelSize();
+ QIcon icon = APPLICATION->getThemedIcon(iconName);
+ QPixmap pix = icon.pixmap(height, height);
+ m_labelMaxMemIcon->setPixmap(pix);
+ }
+}
diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h
index 5344e2cd..e4b7c712 100644
--- a/launcher/ui/widgets/JavaSettingsWidget.h
+++ b/launcher/ui/widgets/JavaSettingsWidget.h
@@ -56,6 +56,8 @@ public:
int maxHeapSize() const;
QString javaPath() const;
+ void updateThresholds();
+
protected slots:
void memoryValueChanged(int);
@@ -85,6 +87,7 @@ private: /* data */
QSpinBox *m_maxMemSpinBox = nullptr;
QLabel *m_labelMinMem = nullptr;
QLabel *m_labelMaxMem = nullptr;
+ QLabel *m_labelMaxMemIcon = nullptr;
QSpinBox *m_minMemSpinBox = nullptr;
QLabel *m_labelPermGen = nullptr;
QSpinBox *m_permGenSpinBox = nullptr;
@@ -92,9 +95,9 @@ private: /* data */
QIcon yellowIcon;
QIcon badIcon;
- int observedMinMemory = 0;
- int observedMaxMemory = 0;
- int observedPermGenMemory = 0;
+ unsigned int observedMinMemory = 0;
+ unsigned int observedMaxMemory = 0;
+ unsigned int observedPermGenMemory = 0;
QString queuedCheck;
uint64_t m_availableMemory = 0ull;
shared_qobject_ptr<JavaChecker> m_checker;
diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp
index 8d606820..0a06a351 100644
--- a/launcher/ui/widgets/PageContainer.cpp
+++ b/launcher/ui/widgets/PageContainer.cpp
@@ -130,6 +130,11 @@ bool PageContainer::selectPage(QString pageId)
return false;
}
+BasePage* PageContainer::getPage(QString pageId)
+{
+ return m_model->findPageEntryById(pageId);
+}
+
void PageContainer::refreshContainer()
{
m_proxyModel->invalidate();
diff --git a/launcher/ui/widgets/PageContainer.h b/launcher/ui/widgets/PageContainer.h
index 80d87a9b..97e294dc 100644
--- a/launcher/ui/widgets/PageContainer.h
+++ b/launcher/ui/widgets/PageContainer.h
@@ -79,6 +79,7 @@ public:
}
virtual bool selectPage(QString pageId) override;
+ BasePage* getPage(QString pageId) override;
void refreshContainer() override;
virtual void setParentContainer(BasePageContainer * container)
diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp
index 79f1e0c9..428be563 100644
--- a/launcher/ui/widgets/WideBar.cpp
+++ b/launcher/ui/widgets/WideBar.cpp
@@ -1,19 +1,24 @@
#include "WideBar.h"
+
+#include <QContextMenuEvent>
+#include <QCryptographicHash>
#include <QToolButton>
-#include <QMenu>
-class ActionButton : public QToolButton
-{
+class ActionButton : public QToolButton {
Q_OBJECT
-public:
- ActionButton(QAction * action, QWidget * parent = 0) : QToolButton(parent), m_action(action) {
+ public:
+ ActionButton(QAction* action, QWidget* parent = nullptr) : QToolButton(parent), m_action(action)
+ {
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+
connect(action, &QAction::changed, this, &ActionButton::actionChanged);
connect(this, &ActionButton::clicked, action, &QAction::trigger);
+
actionChanged();
};
-private slots:
- void actionChanged() {
+ public slots:
+ void actionChanged()
+ {
setEnabled(m_action->isEnabled());
setChecked(m_action->isChecked());
setCheckable(m_action->isCheckable());
@@ -23,137 +28,242 @@ private slots:
setHidden(!m_action->isVisible());
setFocusPolicy(Qt::NoFocus);
}
-private:
- QAction * m_action;
-};
+ private:
+ QAction* m_action;
+};
WideBar::WideBar(const QString& title, QWidget* parent) : QToolBar(title, parent)
{
setFloatable(false);
setMovable(false);
+
+ setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
+ connect(this, &QToolBar::customContextMenuRequested, this, &WideBar::showVisibilityMenu);
}
WideBar::WideBar(QWidget* parent) : QToolBar(parent)
{
setFloatable(false);
setMovable(false);
-}
-
-struct WideBar::BarEntry {
- enum Type {
- None,
- Action,
- Separator,
- Spacer
- } type = None;
- QAction *qAction = nullptr;
- QAction *wideAction = nullptr;
-};
-
-WideBar::~WideBar()
-{
- for(auto *iter: m_entries) {
- delete iter;
- }
+ setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
+ connect(this, &QToolBar::customContextMenuRequested, this, &WideBar::showVisibilityMenu);
}
void WideBar::addAction(QAction* action)
{
- auto entry = new BarEntry();
- entry->qAction = addWidget(new ActionButton(action, this));
- entry->wideAction = action;
- entry->type = BarEntry::Action;
+ BarEntry entry;
+ entry.bar_action = addWidget(new ActionButton(action, this));
+ entry.menu_action = action;
+ entry.type = BarEntry::Type::Action;
+
m_entries.push_back(entry);
+
+ m_menu_state = MenuState::Dirty;
}
void WideBar::addSeparator()
{
- auto entry = new BarEntry();
- entry->qAction = QToolBar::addSeparator();
- entry->type = BarEntry::Separator;
+ BarEntry entry;
+ entry.bar_action = QToolBar::addSeparator();
+ entry.type = BarEntry::Type::Separator;
+
m_entries.push_back(entry);
}
-auto WideBar::getMatching(QAction* act) -> QList<BarEntry*>::iterator
+auto WideBar::getMatching(QAction* act) -> QList<BarEntry>::iterator
{
- auto iter = std::find_if(m_entries.begin(), m_entries.end(), [act](BarEntry * entry) {
- return entry->wideAction == act;
- });
-
+ auto iter = std::find_if(m_entries.begin(), m_entries.end(), [act](BarEntry const& entry) { return entry.menu_action == act; });
+
return iter;
}
-void WideBar::insertActionBefore(QAction* before, QAction* action){
+void WideBar::insertActionBefore(QAction* before, QAction* action)
+{
auto iter = getMatching(before);
- if(iter == m_entries.end())
+ if (iter == m_entries.end())
return;
- auto entry = new BarEntry();
- entry->qAction = insertWidget((*iter)->qAction, new ActionButton(action, this));
- entry->wideAction = action;
- entry->type = BarEntry::Action;
+ BarEntry entry;
+ entry.bar_action = insertWidget(iter->bar_action, new ActionButton(action, this));
+ entry.menu_action = action;
+ entry.type = BarEntry::Type::Action;
+
m_entries.insert(iter, entry);
+
+ m_menu_state = MenuState::Dirty;
}
-void WideBar::insertActionAfter(QAction* after, QAction* action){
+void WideBar::insertActionAfter(QAction* after, QAction* action)
+{
auto iter = getMatching(after);
- if(iter == m_entries.end())
+ if (iter == m_entries.end())
return;
- auto entry = new BarEntry();
- entry->qAction = insertWidget((*(iter+1))->qAction, new ActionButton(action, this));
- entry->wideAction = action;
- entry->type = BarEntry::Action;
+ BarEntry entry;
+ entry.bar_action = insertWidget((iter + 1)->bar_action, new ActionButton(action, this));
+ entry.menu_action = action;
+ entry.type = BarEntry::Type::Action;
+
m_entries.insert(iter + 1, entry);
+
+ m_menu_state = MenuState::Dirty;
}
void WideBar::insertSpacer(QAction* action)
{
auto iter = getMatching(action);
- if(iter == m_entries.end())
+ if (iter == m_entries.end())
return;
- QWidget* spacer = new QWidget();
+ auto* spacer = new QWidget();
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- auto entry = new BarEntry();
- entry->qAction = insertWidget((*iter)->qAction, spacer);
- entry->type = BarEntry::Spacer;
+ BarEntry entry;
+ entry.bar_action = insertWidget(iter->bar_action, spacer);
+ entry.type = BarEntry::Type::Spacer;
m_entries.insert(iter, entry);
}
void WideBar::insertSeparator(QAction* before)
{
auto iter = getMatching(before);
- if(iter == m_entries.end())
+ if (iter == m_entries.end())
return;
- auto entry = new BarEntry();
- entry->qAction = QToolBar::insertSeparator(before);
- entry->type = BarEntry::Separator;
+ BarEntry entry;
+ entry.bar_action = QToolBar::insertSeparator(before);
+ entry.type = BarEntry::Type::Separator;
+
m_entries.insert(iter, entry);
}
-QMenu * WideBar::createContextMenu(QWidget *parent, const QString & title)
+QMenu* WideBar::createContextMenu(QWidget* parent, const QString& title)
{
- QMenu *contextMenu = new QMenu(title, parent);
- for(auto & item: m_entries) {
- switch(item->type) {
+ auto* contextMenu = new QMenu(title, parent);
+ for (auto& item : m_entries) {
+ switch (item.type) {
default:
- case BarEntry::None:
+ case BarEntry::Type::None:
break;
- case BarEntry::Separator:
- case BarEntry::Spacer:
+ case BarEntry::Type::Separator:
+ case BarEntry::Type::Spacer:
contextMenu->addSeparator();
break;
- case BarEntry::Action:
- contextMenu->addAction(item->wideAction);
+ case BarEntry::Type::Action:
+ contextMenu->addAction(item.menu_action);
break;
}
}
return contextMenu;
}
+static void copyAction(QAction* from, QAction* to)
+{
+ Q_ASSERT(from);
+ Q_ASSERT(to);
+
+ to->setText(from->text());
+ to->setIcon(from->icon());
+ to->setToolTip(from->toolTip());
+}
+
+void WideBar::showVisibilityMenu(QPoint const& position)
+{
+ if (!m_bar_menu)
+ m_bar_menu = std::make_unique<QMenu>(this);
+
+ if (m_menu_state == MenuState::Dirty) {
+ for (auto* old_action : m_bar_menu->actions())
+ old_action->deleteLater();
+
+ m_bar_menu->clear();
+
+ for (auto& entry : m_entries) {
+ if (entry.type != BarEntry::Type::Action)
+ continue;
+
+ auto act = new QAction();
+ copyAction(entry.menu_action, act);
+
+ act->setCheckable(true);
+ act->setChecked(entry.bar_action->isVisible());
+
+ connect(act, &QAction::toggled, entry.bar_action, [this, &entry](bool toggled){
+ entry.bar_action->setVisible(toggled);
+
+ // NOTE: This is needed so that disabled actions get reflected on the button when it is made visible.
+ static_cast<ActionButton*>(widgetForAction(entry.bar_action))->actionChanged();
+ });
+
+ m_bar_menu->addAction(act);
+ }
+
+ m_menu_state = MenuState::Fresh;
+ }
+
+ m_bar_menu->popup(mapToGlobal(position));
+}
+
+[[nodiscard]] QByteArray WideBar::getVisibilityState() const
+{
+ QByteArray state;
+
+ for (auto const& entry : m_entries) {
+ if (entry.type != BarEntry::Type::Action)
+ continue;
+
+ state.append(entry.bar_action->isVisible() ? '1' : '0');
+ }
+
+ state.append(',');
+ state.append(getHash());
+
+ return state;
+}
+
+void WideBar::setVisibilityState(QByteArray&& state)
+{
+ auto split = state.split(',');
+
+ auto bits = split.first();
+ auto hash = split.last();
+
+ // If the actions changed, we better not try to load the old one to avoid unwanted hiding
+ if (!checkHash(hash))
+ return;
+
+ qsizetype i = 0;
+ for (auto& entry : m_entries) {
+ if (entry.type != BarEntry::Type::Action)
+ continue;
+ if (i == bits.size())
+ break;
+
+ entry.bar_action->setVisible(bits.at(i++) == '1');
+
+ // NOTE: This is needed so that disabled actions get reflected on the button when it is made visible.
+ static_cast<ActionButton*>(widgetForAction(entry.bar_action))->actionChanged();
+ }
+}
+
+QByteArray WideBar::getHash() const
+{
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ for (auto const& entry : m_entries) {
+ if (entry.type != BarEntry::Type::Action)
+ continue;
+ hash.addData(entry.menu_action->text().toLatin1());
+ }
+
+ return hash.result().toBase64();
+}
+
+bool WideBar::checkHash(QByteArray const& old_hash) const
+{
+ return old_hash == getHash();
+}
+
+
#include "WideBar.moc"
diff --git a/launcher/ui/widgets/WideBar.h b/launcher/ui/widgets/WideBar.h
index 8ff62ef2..a0a7896c 100644
--- a/launcher/ui/widgets/WideBar.h
+++ b/launcher/ui/widgets/WideBar.h
@@ -2,9 +2,10 @@
#include <QAction>
#include <QMap>
+#include <QMenu>
#include <QToolBar>
-class QMenu;
+#include <memory>
class WideBar : public QToolBar {
Q_OBJECT
@@ -12,7 +13,7 @@ class WideBar : public QToolBar {
public:
explicit WideBar(const QString& title, QWidget* parent = nullptr);
explicit WideBar(QWidget* parent = nullptr);
- virtual ~WideBar();
+ ~WideBar() override = default;
void addAction(QAction* action);
void addSeparator();
@@ -23,12 +24,31 @@ class WideBar : public QToolBar {
void insertActionAfter(QAction* after, QAction* action);
QMenu* createContextMenu(QWidget* parent = nullptr, const QString& title = QString());
+ void showVisibilityMenu(const QPoint&);
+
+ // Ideally we would use a QBitArray for this, but it doesn't support string conversion,
+ // so using it in settings is very messy.
+
+ [[nodiscard]] QByteArray getVisibilityState() const;
+ void setVisibilityState(QByteArray&&);
private:
- struct BarEntry;
+ struct BarEntry {
+ enum class Type { None, Action, Separator, Spacer } type = Type::None;
+ QAction* bar_action = nullptr;
+ QAction* menu_action = nullptr;
+ };
- auto getMatching(QAction* act) -> QList<BarEntry*>::iterator;
+ auto getMatching(QAction* act) -> QList<BarEntry>::iterator;
+
+ /** Used to distinguish between versions of the WideBar with different actions */
+ [[nodiscard]] QByteArray getHash() const;
+ [[nodiscard]] bool checkHash(QByteArray const&) const;
private:
- QList<BarEntry*> m_entries;
+ QList<BarEntry> m_entries;
+
+ // Menu to toggle visibility from buttons in the bar
+ std::unique_ptr<QMenu> m_bar_menu = nullptr;
+ enum class MenuState { Fresh, Dirty } m_menu_state = MenuState::Dirty;
};