Merge branch 'develop' into remove-updater
This commit is contained in:
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
// Only process one dropped file...
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -160,6 +160,8 @@ private slots:
|
||||
|
||||
void on_actionEditInstance_triggered();
|
||||
|
||||
void on_actionCreateInstanceShortcut_triggered();
|
||||
|
||||
void taskEnd();
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
private:
|
||||
Ui::BlockedModsDialog *ui;
|
||||
QList<BlockedMod> &m_mods;
|
||||
protected slots:
|
||||
void done(int r) override;
|
||||
|
||||
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);
|
||||
|
@ -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><html><head/><body><p>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.</p><p>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.</p></body></html></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>
|
||||
|
@ -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)
|
||||
|
@ -37,6 +37,7 @@ protected:
|
||||
|
||||
private:
|
||||
Ui::IconPickerDialog *ui;
|
||||
QPushButton *buttonRemove;
|
||||
|
||||
private
|
||||
slots:
|
||||
|
66
launcher/ui/dialogs/ImportResourcePackDialog.cpp
Normal file
66
launcher/ui/dialogs/ImportResourcePackDialog.cpp
Normal file
@ -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;
|
||||
}
|
27
launcher/ui/dialogs/ImportResourcePackDialog.h
Normal file
27
launcher/ui/dialogs/ImportResourcePackDialog.h
Normal file
@ -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);
|
||||
};
|
74
launcher/ui/dialogs/ImportResourcePackDialog.ui
Normal file
74
launcher/ui/dialogs/ImportResourcePackDialog.ui
Normal file
@ -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>
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -44,39 +44,7 @@
|
||||
<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>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1024</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelMinMem">
|
||||
<property name="text">
|
||||
<string>&Minimum memory allocation:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>minMemSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0,0,0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelMaxMem">
|
||||
<property name="text">
|
||||
@ -87,28 +55,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="minMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory Minecraft is started with.</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>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>256</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelPermGen">
|
||||
<property name="text">
|
||||
@ -119,7 +65,61 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelMinMem">
|
||||
<property name="text">
|
||||
<string>&Minimum memory allocation:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>minMemSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QSpinBox" name="minMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory Minecraft is started with.</string>
|
||||
</property>
|
||||
<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>256</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<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="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="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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,29 +127,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<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>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1024</number>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelMaxMem">
|
||||
<property name="text">
|
||||
<string>Maximum memory allocation:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<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 amount of memory Minecraft is started with.</string>
|
||||
@ -154,7 +153,7 @@
|
||||
<number>128</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65536</number>
|
||||
<number>1048576</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>128</number>
|
||||
@ -164,7 +163,29 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<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="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="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>
|
||||
|
433
launcher/ui/pages/instance/ManagedPackPage.cpp
Normal file
433
launcher/ui/pages/instance/ManagedPackPage.cpp
Normal file
@ -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"
|
152
launcher/ui/pages/instance/ManagedPackPage.h
Normal file
152
launcher/ui/pages/instance/ManagedPackPage.h
Normal file
@ -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;
|
||||
};
|
193
launcher/ui/pages/instance/ManagedPackPage.ui
Normal file
193
launcher/ui/pages/instance/ManagedPackPage.ui
Normal file
@ -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>
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 ¤t, const QModelIndex &previous);
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -99,5 +99,5 @@ private:
|
||||
Flame::ListModel* listModel = nullptr;
|
||||
Flame::IndexedPack current;
|
||||
|
||||
QString selectedVersion;
|
||||
int m_selected_version_index = -1;
|
||||
};
|
||||
|
@ -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); });
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
}
|
||||
|
||||
virtual bool selectPage(QString pageId) override;
|
||||
BasePage* getPage(QString pageId) override;
|
||||
|
||||
void refreshContainer() override;
|
||||
virtual void setParentContainer(BasePageContainer * container)
|
||||
|
@ -1,19 +1,24 @@
|
||||
#include "WideBar.h"
|
||||
#include <QToolButton>
|
||||
#include <QMenu>
|
||||
|
||||
class ActionButton : public QToolButton
|
||||
{
|
||||
#include <QContextMenuEvent>
|
||||
#include <QCryptographicHash>
|
||||
#include <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"
|
||||
|
@ -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;
|
||||
};
|
||||
|
Reference in New Issue
Block a user