diff --git a/CMakeLists.txt b/CMakeLists.txt index ce3d433fb..e3d60a102 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -338,6 +338,9 @@ if(NOT Launcher_FORCE_BUNDLED_LIBS) # Find cmark find_package(cmark QUIET) + + # Find qrcodegencpp-cmake + find_package(qrcodegencpp QUIET) endif() include(ECMQtDeclareLoggingCategory) @@ -528,19 +531,22 @@ if(NOT cmark_FOUND) else() message(STATUS "Using system cmark") endif() +if(NOT qrcodegencpp_FOUND) + set(QRCODE_SOURCES + libraries/qrcodegenerator/cpp/qrcodegen.cpp + libraries/qrcodegenerator/cpp/qrcodegen.hpp + ) + add_library(qrcodegenerator STATIC ${QRCODE_SOURCES}) + target_include_directories(qrcodegenerator PUBLIC "libraries/qrcodegenerator/cpp/" ) + generate_export_header(qrcodegenerator) +else() + add_library(qrcodegenerator ALIAS qrcodegencpp::qrcodegencpp) + message(STATUS "Using system qrcodegencpp-cmake") +endif() add_subdirectory(libraries/gamemode) add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API add_subdirectory(libraries/qdcss) # css parser -# qr code generator -set(QRCODE_SOURCES - libraries/qrcodegenerator/cpp/qrcodegen.cpp - libraries/qrcodegenerator/cpp/qrcodegen.hpp -) -add_library(qrcodegenerator STATIC ${QRCODE_SOURCES}) -target_include_directories(qrcodegenerator PUBLIC "libraries/qrcodegenerator/cpp/" ) -generate_export_header(qrcodegenerator) - ############################### Built Artifacts ############################### add_subdirectory(buildconfig) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index c22e2622b..80dc7beab 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -59,6 +59,7 @@ #include "ui/pages/BasePageProvider.h" #include "ui/pages/global/APIPage.h" #include "ui/pages/global/AccountListPage.h" +#include "ui/pages/global/AppearancePage.h" #include "ui/pages/global/ExternalToolsPage.h" #include "ui/pages/global/JavaPage.h" #include "ui/pages/global/LanguagePage.h" @@ -886,13 +887,14 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) { m_globalSettingsProvider = std::make_shared(tr("Settings")); m_globalSettingsProvider->addPage(); + m_globalSettingsProvider->addPage(); + m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); - m_globalSettingsProvider->addPage(); - m_globalSettingsProvider->addPage(); - m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); + m_globalSettingsProvider->addPage(); + m_globalSettingsProvider->addPage(); } PixmapCache::setInstance(new PixmapCache(this)); @@ -1650,9 +1652,9 @@ void Application::ShowGlobalSettings(class QWidget* parent, QString open_page) { SettingsObject::Lock lock(APPLICATION->settings()); PageDialog dlg(m_globalSettingsProvider.get(), open_page, parent); + connect(&dlg, &PageDialog::applied, this, &Application::globalSettingsApplied); dlg.exec(); } - emit globalSettingsClosed(); } MainWindow* Application::showMainWindow(bool minimized) diff --git a/launcher/Application.h b/launcher/Application.h index fefb32292..2daf6ef35 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -196,7 +196,7 @@ class Application : public QApplication { signals: void updateAllowedChanged(bool status); void globalSettingsAboutToOpen(); - void globalSettingsClosed(); + void globalSettingsApplied(); int currentCatChanged(int index); void oauthReplyRecieved(QVariantMap); diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 70e0f9dc1..096052a45 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -69,6 +69,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("lastTimePlayed", 0); m_settings->registerSetting("linkedInstances", "[]"); + m_settings->registerSetting("shortcuts", QString()); // Game time override auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false); @@ -398,6 +399,57 @@ bool BaseInstance::syncInstanceDirName(const QString& newRoot) const return oldRoot == newRoot || QFile::rename(oldRoot, newRoot); } +void BaseInstance::registerShortcut(const ShortcutData& data) +{ + auto currentShortcuts = shortcuts(); + currentShortcuts.append(data); + qDebug() << "Registering shortcut for instance" << id() << "with name" << data.name << "and path" << data.filePath; + setShortcuts(currentShortcuts); +} + +void BaseInstance::setShortcuts(const QList& shortcuts) +{ + // FIXME: if no change, do not set. setting involves saving a file. + QJsonArray array; + for (const auto& elem : shortcuts) { + array.append(QJsonObject{ { "name", elem.name }, { "filePath", elem.filePath }, { "target", static_cast(elem.target) } }); + } + + QJsonDocument document; + document.setArray(array); + m_settings->set("shortcuts", QString::fromUtf8(document.toJson(QJsonDocument::Compact))); +} + +QList BaseInstance::shortcuts() const +{ + auto data = m_settings->get("shortcuts").toString().toUtf8(); + QJsonParseError parseError; + auto document = QJsonDocument::fromJson(data, &parseError); + if (parseError.error != QJsonParseError::NoError || !document.isArray()) + return {}; + + QList results; + for (const auto& elem : document.array()) { + if (!elem.isObject()) + continue; + auto dict = elem.toObject(); + if (!dict.contains("name") || !dict.contains("filePath") || !dict.contains("target")) + continue; + int value = dict["target"].toInt(-1); + if (!dict["name"].isString() || !dict["filePath"].isString() || value < 0 || value >= 3) + continue; + + QString shortcutName = dict["name"].toString(); + QString filePath = dict["filePath"].toString(); + if (!QDir(filePath).exists()) { + qWarning() << "Shortcut" << shortcutName << "for instance" << name() << "have non-existent path" << filePath; + continue; + } + results.append({ shortcutName, filePath, static_cast(value) }); + } + return results; +} + QString BaseInstance::name() const { return m_settings->get("name").toString(); diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 1acf1afe0..3509c0155 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -38,7 +38,9 @@ #pragma once #include +#include #include +#include #include #include #include @@ -66,6 +68,16 @@ class BaseInstance; // pointer for lazy people using InstancePtr = std::shared_ptr; +/// Shortcut saving target representations +enum class ShortcutTarget { Desktop, Applications, Other }; + +/// Shortcut data representation +struct ShortcutData { + QString name; + QString filePath; + ShortcutTarget target = ShortcutTarget::Other; +}; + /*! * \brief Base class for instances. * This class implements many functions that are common between instances and @@ -129,6 +141,11 @@ class BaseInstance : public QObject, public std::enable_shared_from_this shortcuts() const; + void setShortcuts(const QList& shortcuts); + /// Value used for instance window titles QString windowTitle() const; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 928c15325..a7ccb809d 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -864,7 +864,6 @@ SET(LAUNCHER_SOURCES ui/setupwizard/LanguageWizardPage.h ui/setupwizard/PasteWizardPage.cpp ui/setupwizard/PasteWizardPage.h - ui/setupwizard/ThemeWizardPage.cpp ui/setupwizard/ThemeWizardPage.h ui/setupwizard/AutoJavaWizardPage.cpp ui/setupwizard/AutoJavaWizardPage.h @@ -960,6 +959,7 @@ SET(LAUNCHER_SOURCES ui/pages/global/MinecraftPage.h ui/pages/global/LauncherPage.cpp ui/pages/global/LauncherPage.h + ui/pages/global/AppearancePage.h ui/pages/global/ProxyPage.cpp ui/pages/global/ProxyPage.h ui/pages/global/APIPage.cpp @@ -1157,8 +1157,8 @@ SET(LAUNCHER_SOURCES ui/widgets/ProgressWidget.cpp ui/widgets/WideBar.h ui/widgets/WideBar.cpp - ui/widgets/ThemeCustomizationWidget.h - ui/widgets/ThemeCustomizationWidget.cpp + ui/widgets/AppearanceWidget.h + ui/widgets/AppearanceWidget.cpp ui/widgets/MinecraftSettingsWidget.h ui/widgets/MinecraftSettingsWidget.cpp ui/widgets/JavaSettingsWidget.h @@ -1200,7 +1200,6 @@ qt_wrap_ui(LAUNCHER_UI ui/setupwizard/PasteWizardPage.ui ui/setupwizard/AutoJavaWizardPage.ui ui/setupwizard/LoginWizardPage.ui - ui/setupwizard/ThemeWizardPage.ui ui/pages/global/AccountListPage.ui ui/pages/global/JavaPage.ui ui/pages/global/LauncherPage.ui @@ -1234,7 +1233,7 @@ qt_wrap_ui(LAUNCHER_UI ui/widgets/InfoFrame.ui ui/widgets/ModFilterWidget.ui ui/widgets/SubTaskProgressBar.ui - ui/widgets/ThemeCustomizationWidget.ui + ui/widgets/AppearanceWidget.ui ui/widgets/MinecraftSettingsWidget.ui ui/widgets/JavaSettingsWidget.ui ui/dialogs/CopyInstanceDialog.ui diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 9d45f4af3..5136e7954 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -898,26 +898,26 @@ QString getApplicationsDir() } // Cross-platform Shortcut creation -bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) +QString createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) { if (destination.isEmpty()) { destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name)); } if (!ensureFilePathExists(destination)) { qWarning() << "Destination path can't be created!"; - return false; + return QString(); } #if defined(Q_OS_MACOS) QDir application = destination + ".app/"; if (application.exists()) { qWarning() << "Application already exists!"; - return false; + return QString(); } if (!application.mkpath(".")) { qWarning() << "Couldn't create application"; - return false; + return QString(); } QDir content = application.path() + "/Contents/"; @@ -927,7 +927,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri if (!(content.mkpath(".") && resources.mkpath(".") && binaryDir.mkpath("."))) { qWarning() << "Couldn't create directories within application"; - return false; + return QString(); } info.open(QIODevice::WriteOnly | QIODevice::Text); @@ -976,7 +976,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri "\n" ""; - return true; + return application.path(); #elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) if (!destination.endsWith(".desktop")) // in case of isFlatpak destination is already populated destination += ".desktop"; @@ -1002,32 +1002,32 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther); - return true; + return destination; #elif defined(Q_OS_WIN) QFileInfo targetInfo(target); if (!targetInfo.exists()) { qWarning() << "Target file does not exist!"; - return false; + return QString(); } target = targetInfo.absoluteFilePath(); if (target.length() >= MAX_PATH) { qWarning() << "Target file path is too long!"; - return false; + return QString(); } if (!icon.isEmpty() && icon.length() >= MAX_PATH) { qWarning() << "Icon path is too long!"; - return false; + return QString(); } destination += ".lnk"; if (destination.length() >= MAX_PATH) { qWarning() << "Destination path is too long!"; - return false; + return QString(); } QString argStr; @@ -1046,7 +1046,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri if (argStr.length() >= MAX_PATH) { qWarning() << "Arguments string is too long!"; - return false; + return QString(); } HRESULT hres; @@ -1055,7 +1055,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri hres = CoInitialize(nullptr); if (FAILED(hres)) { qWarning() << "Failed to initialize COM!"; - return false; + return QString(); } WCHAR wsz[MAX_PATH]; @@ -1109,10 +1109,12 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri // go away COM, nobody likes you CoUninitialize(); - return SUCCEEDED(hres); + if (SUCCEEDED(hres)) + return destination; + return QString(); #else qWarning("Desktop Shortcuts not supported on your platform!"); - return false; + return QString(); #endif } diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index b1108eded..0e573a09e 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -362,8 +362,9 @@ bool overrideFolder(QString overwritten_path, QString override_path); /** * Creates a shortcut to the specified target file at the specified destination path. + * Returns null QString if creation failed; otherwise returns the path to the created shortcut. */ -bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon); +QString createShortcut(QString destination, QString target, QStringList args, QString name, QString icon); enum class FilesystemType { FAT, diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 89e7dc04d..de94db7c3 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -60,6 +60,7 @@ #include "NullInstance.h" #include "WatchLock.h" #include "minecraft/MinecraftInstance.h" +#include "minecraft/ShortcutUtils.h" #include "settings/INISettingsObject.h" #ifdef Q_OS_WIN32 @@ -333,7 +334,7 @@ bool InstanceList::trashInstance(const InstanceId& id) { auto inst = getInstanceById(id); if (!inst) { - qDebug() << "Cannot trash instance" << id << ". No such instance is present (deleted externally?)."; + qWarning() << "Cannot trash instance" << id << ". No such instance is present (deleted externally?)."; return false; } @@ -348,26 +349,43 @@ bool InstanceList::trashInstance(const InstanceId& id) } if (!FS::trash(inst->instanceRoot(), &trashedLoc)) { - qDebug() << "Trash of instance" << id << "has not been completely successfully..."; + qWarning() << "Trash of instance" << id << "has not been completely successful..."; return false; } qDebug() << "Instance" << id << "has been trashed by the launcher."; m_trashHistory.push({ id, inst->instanceRoot(), trashedLoc, cachedGroupId }); + // Also trash all of its shortcuts; we remove the shortcuts if trash fails since it is invalid anyway + for (const auto& [name, filePath, target] : inst->shortcuts()) { + if (!FS::trash(filePath, &trashedLoc)) { + qWarning() << "Trash of shortcut" << name << "at path" << filePath << "for instance" << id + << "has not been successful, trying to delete it instead..."; + if (!FS::deletePath(filePath)) { + qWarning() << "Deletion of shortcut" << name << "at path" << filePath << "for instance" << id + << "has not been successful, given up..."; + } else { + qDebug() << "Shortcut" << name << "at path" << filePath << "for instance" << id << "has been deleted by the launcher."; + } + continue; + } + qDebug() << "Shortcut" << name << "at path" << filePath << "for instance" << id << "has been trashed by the launcher."; + m_trashHistory.top().shortcuts.append({ { name, filePath, target }, trashedLoc }); + } + return true; } -bool InstanceList::trashedSomething() +bool InstanceList::trashedSomething() const { return !m_trashHistory.empty(); } -void InstanceList::undoTrashInstance() +bool InstanceList::undoTrashInstance() { if (m_trashHistory.empty()) { qWarning() << "Nothing to recover from trash."; - return; + return true; } auto top = m_trashHistory.pop(); @@ -377,21 +395,41 @@ void InstanceList::undoTrashInstance() top.path += "1"; } + if (!QFile(top.trashPath).rename(top.path)) { + qWarning() << "Moving" << top.trashPath << "back to" << top.path << "failed!"; + return false; + } qDebug() << "Moving" << top.trashPath << "back to" << top.path; - QFile(top.trashPath).rename(top.path); + + bool ok = true; + for (const auto& [data, trashPath] : top.shortcuts) { + if (QDir(data.filePath).exists()) { + // Don't try to append 1 here as the shortcut may have suffixes like .app, just warn and skip it + qWarning() << "Shortcut" << trashPath << "original directory" << data.filePath << "already exists!"; + ok = false; + continue; + } + if (!QFile(trashPath).rename(data.filePath)) { + qWarning() << "Moving shortcut from" << trashPath << "back to" << data.filePath << "failed!"; + ok = false; + continue; + } + qDebug() << "Moving shortcut from" << trashPath << "back to" << data.filePath; + } m_instanceGroupIndex[top.id] = top.groupName; increaseGroupCount(top.groupName); saveGroupList(); emit instancesChanged(); + return ok; } void InstanceList::deleteInstance(const InstanceId& id) { auto inst = getInstanceById(id); if (!inst) { - qDebug() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?)."; + qWarning() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?)."; return; } @@ -404,11 +442,19 @@ void InstanceList::deleteInstance(const InstanceId& id) qDebug() << "Will delete instance" << id; if (!FS::deletePath(inst->instanceRoot())) { - qWarning() << "Deletion of instance" << id << "has not been completely successful ..."; + qWarning() << "Deletion of instance" << id << "has not been completely successful..."; return; } qDebug() << "Instance" << id << "has been deleted by the launcher."; + + for (const auto& [name, filePath, target] : inst->shortcuts()) { + if (!FS::deletePath(filePath)) { + qWarning() << "Deletion of shortcut" << name << "at path" << filePath << "for instance" << id << "has not been successful..."; + continue; + } + qDebug() << "Shortcut" << name << "at path" << filePath << "for instance" << id << "has been deleted by the launcher."; + } } static QMap getIdMapping(const QList& list) @@ -638,7 +684,12 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id) } else { inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot)); } - qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot(); + qDebug() << "Loaded instance" << inst->name() << "from" << inst->instanceRoot(); + + auto shortcut = inst->shortcuts(); + if (!shortcut.isEmpty()) + qDebug() << "Loaded" << shortcut.size() << "shortcut(s) for instance" << inst->name(); + return inst; } diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h index c85fe55c7..fc4fa9a39 100644 --- a/launcher/InstanceList.h +++ b/launcher/InstanceList.h @@ -56,11 +56,17 @@ enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateErro enum class GroupsState { NotLoaded, Steady, Dirty }; +struct TrashShortcutItem { + ShortcutData data; + QString trashPath; +}; + struct TrashHistoryItem { QString id; QString path; QString trashPath; QString groupName; + QList shortcuts; }; class InstanceList : public QAbstractListModel { @@ -111,8 +117,8 @@ class InstanceList : public QAbstractListModel { void deleteGroup(const GroupId& name); void renameGroup(const GroupId& src, const GroupId& dst); bool trashInstance(const InstanceId& id); - bool trashedSomething(); - void undoTrashInstance(); + bool trashedSomething() const; + bool undoTrashInstance(); void deleteInstance(const InstanceId& id); // Wrap an instance creation task in some more task machinery and make it ready to be used diff --git a/launcher/JavaCommon.cpp b/launcher/JavaCommon.cpp index b71000054..7bb674dde 100644 --- a/launcher/JavaCommon.cpp +++ b/launcher/JavaCommon.cpp @@ -95,7 +95,7 @@ void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& re { QString text; text += QObject::tr( - "The specified Java binary didn't work.
You should use the auto-detect feature, " + "The specified Java binary didn't work.
You should press 'Detect', " "or set the path to the Java executable.
"); CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); } diff --git a/launcher/Json.cpp b/launcher/Json.cpp index f397f89c5..8623eb2a8 100644 --- a/launcher/Json.cpp +++ b/launcher/Json.cpp @@ -279,4 +279,29 @@ QJsonValue requireIsType(const QJsonValue& value, const QString& wha return value; } +QStringList toStringList(const QString& jsonString) +{ + QJsonParseError parseError; + QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8(), &parseError); + + if (parseError.error != QJsonParseError::NoError || !doc.isArray()) + return {}; + try { + return ensureIsArrayOf(doc.array(), ""); + } catch (Json::JsonException& e) { + return {}; + } +} + +QString fromStringList(const QStringList& list) +{ + QJsonArray array; + for (const QString& str : list) { + array.append(str); + } + + QJsonDocument doc(toJsonArray(list)); + return QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); +} + } // namespace Json diff --git a/launcher/Json.h b/launcher/Json.h index c13be6470..509a41fd5 100644 --- a/launcher/Json.h +++ b/launcher/Json.h @@ -99,7 +99,7 @@ template QJsonArray toJsonArray(const QList& container) { QJsonArray array; - for (const T item : container) { + for (const T& item : container) { array.append(toJson(item)); } return array; @@ -278,5 +278,9 @@ JSON_HELPERFUNCTIONS(Variant, QVariant) #undef JSON_HELPERFUNCTIONS +// helper functions for settings +QStringList toStringList(const QString& jsonString); +QString fromStringList(const QStringList& list); + } // namespace Json using JSONValidationError = Json::JsonException; diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 77e2294a6..fb15cb120 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -256,6 +256,10 @@ void MinecraftInstance::loadSpecificSettings() connect(dataPacksEnabled.get(), &Setting::SettingChanged, this, [this] { m_data_pack_list.reset(); }); connect(dataPacksPath.get(), &Setting::SettingChanged, this, [this] { m_data_pack_list.reset(); }); + // Join server on launch, this does not have a global override + m_settings->registerSetting("OverrideModDownloadLoaders", false); + m_settings->registerSetting("ModDownloadLoaders", "[]"); + qDebug() << "Instance-type specific settings were loaded!"; setSpecificSettingsLoaded(true); diff --git a/launcher/minecraft/ShortcutUtils.cpp b/launcher/minecraft/ShortcutUtils.cpp index 43954aa6a..0336a9512 100644 --- a/launcher/minecraft/ShortcutUtils.cpp +++ b/launcher/minecraft/ShortcutUtils.cpp @@ -48,10 +48,10 @@ namespace ShortcutUtils { -void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) +bool createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) { if (!shortcut.instance) - return; + return false; QString appPath = QApplication::applicationFilePath(); auto icon = APPLICATION->icons()->icon(shortcut.iconKey.isEmpty() ? shortcut.instance->iconKey() : shortcut.iconKey); @@ -64,7 +64,7 @@ void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) if (appPath.startsWith("/private/var/")) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts.")); - return; + return false; } iconPath = FS::PathCombine(shortcut.instance->instanceRoot(), "Icon.icns"); @@ -72,7 +72,7 @@ void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for application.")); - return; + return false; } QIcon iconObj = icon->icon(); @@ -82,7 +82,7 @@ void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) if (!success) { iconFile.remove(); QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for application.")); - return; + return false; } #elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) if (appPath.startsWith("/tmp/.mount_")) { @@ -102,7 +102,7 @@ void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut.")); - return; + return false; } bool success = icon->icon().pixmap(64, 64).save(&iconFile, "PNG"); iconFile.close(); @@ -110,7 +110,7 @@ void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) if (!success) { iconFile.remove(); QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut.")); - return; + return false; } if (DesktopServices::isFlatpak()) { @@ -128,7 +128,7 @@ void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut.")); - return; + return false; } bool success = icon->icon().pixmap(64, 64).save(&iconFile, "ICO"); iconFile.close(); @@ -139,51 +139,58 @@ void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) if (!success) { iconFile.remove(); QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut.")); - return; + return false; } #else QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Not supported on your platform!")); - return; + return false; #endif args.append({ "--launch", shortcut.instance->id() }); args.append(shortcut.extraArgs); - if (!FS::createShortcut(filePath, appPath, args, shortcut.name, iconPath)) { + QString shortcutPath = FS::createShortcut(filePath, appPath, args, shortcut.name, iconPath); + if (shortcutPath.isEmpty()) { #if not defined(Q_OS_MACOS) iconFile.remove(); #endif QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create %1 shortcut!").arg(shortcut.targetString)); + return false; } + + shortcut.instance->registerShortcut({ shortcut.name, shortcutPath, shortcut.target }); + return true; } -void createInstanceShortcutOnDesktop(const Shortcut& shortcut) +bool createInstanceShortcutOnDesktop(const Shortcut& shortcut) { if (!shortcut.instance) - return; + return false; QString desktopDir = FS::getDesktopDir(); if (desktopDir.isEmpty()) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Couldn't find desktop?!")); - return; + return false; } QString shortcutFilePath = FS::PathCombine(desktopDir, FS::RemoveInvalidFilenameChars(shortcut.name)); - createInstanceShortcut(shortcut, shortcutFilePath); + if (!createInstanceShortcut(shortcut, shortcutFilePath)) + return false; QMessageBox::information(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Created a shortcut to this %1 on your desktop!").arg(shortcut.targetString)); + return true; } -void createInstanceShortcutInApplications(const Shortcut& shortcut) +bool createInstanceShortcutInApplications(const Shortcut& shortcut) { if (!shortcut.instance) - return; + return false; QString applicationsDir = FS::getApplicationsDir(); if (applicationsDir.isEmpty()) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Couldn't find applications folder?!")); - return; + return false; } #if defined(Q_OS_MACOS) || defined(Q_OS_WIN) @@ -193,20 +200,22 @@ void createInstanceShortcutInApplications(const Shortcut& shortcut) if (!applicationsDirQ.mkpath(".")) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create instances folder in applications folder!")); - return; + return false; } #endif QString shortcutFilePath = FS::PathCombine(applicationsDir, FS::RemoveInvalidFilenameChars(shortcut.name)); - createInstanceShortcut(shortcut, shortcutFilePath); + if (!createInstanceShortcut(shortcut, shortcutFilePath)) + return false; QMessageBox::information(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Created a shortcut to this %1 in your applications folder!").arg(shortcut.targetString)); + return true; } -void createInstanceShortcutInOther(const Shortcut& shortcut) +bool createInstanceShortcutInOther(const Shortcut& shortcut) { if (!shortcut.instance) - return; + return false; QString defaultedDir = FS::getDesktopDir(); #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) @@ -225,13 +234,15 @@ void createInstanceShortcutInOther(const Shortcut& shortcut) shortcutFilePath = fileDialog.getSaveFileName(shortcut.parent, QObject::tr("Create Shortcut"), shortcutFilePath, QObject::tr("Desktop Entries") + " (*" + extension + ")"); if (shortcutFilePath.isEmpty()) - return; // file dialog canceled by user + return false; // file dialog canceled by user if (shortcutFilePath.endsWith(extension)) shortcutFilePath = shortcutFilePath.mid(0, shortcutFilePath.length() - extension.length()); - createInstanceShortcut(shortcut, shortcutFilePath); + if (!createInstanceShortcut(shortcut, shortcutFilePath)) + return false; QMessageBox::information(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Created a shortcut to this %1!").arg(shortcut.targetString)); + return true; } } // namespace ShortcutUtils diff --git a/launcher/minecraft/ShortcutUtils.h b/launcher/minecraft/ShortcutUtils.h index e3d2e283a..b995c36bd 100644 --- a/launcher/minecraft/ShortcutUtils.h +++ b/launcher/minecraft/ShortcutUtils.h @@ -38,6 +38,7 @@ #pragma once #include "Application.h" +#include #include namespace ShortcutUtils { @@ -49,18 +50,19 @@ struct Shortcut { QWidget* parent = nullptr; QStringList extraArgs = {}; QString iconKey = ""; + ShortcutTarget target; }; /// Create an instance shortcut on the specified file path -void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath); +bool createInstanceShortcut(const Shortcut& shortcut, const QString& filePath); /// Create an instance shortcut on the desktop -void createInstanceShortcutOnDesktop(const Shortcut& shortcut); +bool createInstanceShortcutOnDesktop(const Shortcut& shortcut); /// Create an instance shortcut in the Applications directory -void createInstanceShortcutInApplications(const Shortcut& shortcut); +bool createInstanceShortcutInApplications(const Shortcut& shortcut); /// Create an instance shortcut in other directories -void createInstanceShortcutInOther(const Shortcut& shortcut); +bool createInstanceShortcutInOther(const Shortcut& shortcut); } // namespace ShortcutUtils diff --git a/launcher/resources/breeze_dark/breeze_dark.qrc b/launcher/resources/breeze_dark/breeze_dark.qrc index 8be5d117b..585f2c60a 100644 --- a/launcher/resources/breeze_dark/breeze_dark.qrc +++ b/launcher/resources/breeze_dark/breeze_dark.qrc @@ -43,5 +43,6 @@ scalable/rename.svg scalable/launch.svg scalable/server.svg + scalable/appearance.svg diff --git a/launcher/resources/breeze_dark/scalable/appearance.svg b/launcher/resources/breeze_dark/scalable/appearance.svg new file mode 100644 index 000000000..93e6ffa76 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/appearance.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/breeze_light.qrc b/launcher/resources/breeze_light/breeze_light.qrc index 291bcb508..2b0adba10 100644 --- a/launcher/resources/breeze_light/breeze_light.qrc +++ b/launcher/resources/breeze_light/breeze_light.qrc @@ -43,5 +43,6 @@ scalable/rename.svg scalable/launch.svg scalable/server.svg + scalable/appearance.svg diff --git a/launcher/resources/breeze_light/scalable/appearance.svg b/launcher/resources/breeze_light/scalable/appearance.svg new file mode 100644 index 000000000..6e6d64a79 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/appearance.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/flat/flat.qrc b/launcher/resources/flat/flat.qrc index 9f88645c2..2cc9f46f5 100644 --- a/launcher/resources/flat/flat.qrc +++ b/launcher/resources/flat/flat.qrc @@ -49,5 +49,6 @@ scalable/rename.svg scalable/server.svg scalable/launch.svg + scalable/appearance.svg diff --git a/launcher/resources/flat/scalable/appearance.svg b/launcher/resources/flat/scalable/appearance.svg new file mode 100644 index 000000000..11dcb3f33 --- /dev/null +++ b/launcher/resources/flat/scalable/appearance.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/flat_white/flat_white.qrc b/launcher/resources/flat_white/flat_white.qrc index 56aad62fa..d873e40d6 100644 --- a/launcher/resources/flat_white/flat_white.qrc +++ b/launcher/resources/flat_white/flat_white.qrc @@ -49,5 +49,6 @@ scalable/tag.svg scalable/launch.svg scalable/server.svg + scalable/appearance.svg diff --git a/launcher/resources/flat_white/scalable/appearance.svg b/launcher/resources/flat_white/scalable/appearance.svg new file mode 100644 index 000000000..b20d91f12 --- /dev/null +++ b/launcher/resources/flat_white/scalable/appearance.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 853dda525..116dd73cd 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -247,7 +247,7 @@ scalable/matrix.svg - + scalable/discord.svg @@ -279,7 +279,7 @@ scalable/instances/fox.svg scalable/instances/bee.svg - + 32x32/instances/chicken_legacy.png 128x128/instances/chicken_legacy.png @@ -347,6 +347,7 @@ scalable/export.svg scalable/launch.svg scalable/server.svg + scalable/appearance.svg scalable/instances/quiltmc.svg scalable/instances/fabricmc.svg diff --git a/launcher/resources/multimc/scalable/appearance.svg b/launcher/resources/multimc/scalable/appearance.svg new file mode 100644 index 000000000..429670c36 --- /dev/null +++ b/launcher/resources/multimc/scalable/appearance.svg @@ -0,0 +1,2440 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OK + + + + + + + + + + + + 22% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OK + + + + + + + + + + + + 22% + + + + + diff --git a/launcher/resources/pe_blue/pe_blue.qrc b/launcher/resources/pe_blue/pe_blue.qrc index df3a71ab2..1e6b5d3cc 100644 --- a/launcher/resources/pe_blue/pe_blue.qrc +++ b/launcher/resources/pe_blue/pe_blue.qrc @@ -41,5 +41,6 @@ scalable/launch.svg scalable/shortcut.svg scalable/server.svg + scalable/appearance.svg diff --git a/launcher/resources/pe_blue/scalable/appearance.svg b/launcher/resources/pe_blue/scalable/appearance.svg new file mode 100644 index 000000000..9323ec02d --- /dev/null +++ b/launcher/resources/pe_blue/scalable/appearance.svg @@ -0,0 +1,65 @@ + + + + diff --git a/launcher/resources/pe_colored/pe_colored.qrc b/launcher/resources/pe_colored/pe_colored.qrc index 1a08f7e18..71b38024a 100644 --- a/launcher/resources/pe_colored/pe_colored.qrc +++ b/launcher/resources/pe_colored/pe_colored.qrc @@ -41,5 +41,6 @@ scalable/launch.svg scalable/shortcut.svg scalable/server.svg + scalable/appearance.svg diff --git a/launcher/resources/pe_colored/scalable/appearance.svg b/launcher/resources/pe_colored/scalable/appearance.svg new file mode 100644 index 000000000..88c1eaf26 --- /dev/null +++ b/launcher/resources/pe_colored/scalable/appearance.svg @@ -0,0 +1,71 @@ + + + + diff --git a/launcher/resources/pe_dark/pe_dark.qrc b/launcher/resources/pe_dark/pe_dark.qrc index 9d3d3d46c..9ccfece1e 100644 --- a/launcher/resources/pe_dark/pe_dark.qrc +++ b/launcher/resources/pe_dark/pe_dark.qrc @@ -41,5 +41,6 @@ scalable/launch.svg scalable/shortcut.svg scalable/server.svg + scalable/appearance.svg diff --git a/launcher/resources/pe_dark/scalable/appearance.svg b/launcher/resources/pe_dark/scalable/appearance.svg new file mode 100644 index 000000000..24b7283e4 --- /dev/null +++ b/launcher/resources/pe_dark/scalable/appearance.svg @@ -0,0 +1,65 @@ + + + + diff --git a/launcher/resources/pe_light/pe_light.qrc b/launcher/resources/pe_light/pe_light.qrc index 2775a0872..a6d49b803 100644 --- a/launcher/resources/pe_light/pe_light.qrc +++ b/launcher/resources/pe_light/pe_light.qrc @@ -41,5 +41,6 @@ scalable/launch.svg scalable/shortcut.svg scalable/server.svg + scalable/appearance.svg diff --git a/launcher/resources/pe_light/scalable/appearance.svg b/launcher/resources/pe_light/scalable/appearance.svg new file mode 100644 index 000000000..61b2f3422 --- /dev/null +++ b/launcher/resources/pe_light/scalable/appearance.svg @@ -0,0 +1,66 @@ + + + + diff --git a/launcher/tools/JVisualVM.cpp b/launcher/tools/JVisualVM.cpp index 4da4e1e54..0cae8e37b 100644 --- a/launcher/tools/JVisualVM.cpp +++ b/launcher/tools/JVisualVM.cpp @@ -24,7 +24,7 @@ JVisualVM::JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject* void JVisualVM::profilerStarted() { - emit readyToLaunch(tr("JVisualVM started")); + emit readyToLaunch(tr("VisualVM started")); } void JVisualVM::profilerFinished([[maybe_unused]] int exit, QProcess::ExitStatus status) @@ -82,7 +82,7 @@ bool JVisualVMFactory::check(const QString& path, QString* error) } QFileInfo finfo(path); if (!finfo.isExecutable() || !finfo.fileName().contains("visualvm")) { - *error = QObject::tr("Invalid path to JVisualVM"); + *error = QObject::tr("Invalid path to VisualVM"); return false; } return true; diff --git a/launcher/tools/JVisualVM.h b/launcher/tools/JVisualVM.h index 2828119a1..c152aecdb 100644 --- a/launcher/tools/JVisualVM.h +++ b/launcher/tools/JVisualVM.h @@ -4,7 +4,7 @@ class JVisualVMFactory : public BaseProfilerFactory { public: - QString name() const override { return "JVisualVM"; } + QString name() const override { return "VisualVM"; } void registerSettings(SettingsObjectPtr settings) override; BaseExternalTool* createTool(InstancePtr instance, QObject* parent = 0) override; bool check(QString* error) override; diff --git a/launcher/ui/InstanceWindow.cpp b/launcher/ui/InstanceWindow.cpp index bf83a56c9..2f156e125 100644 --- a/launcher/ui/InstanceWindow.cpp +++ b/launcher/ui/InstanceWindow.cpp @@ -111,7 +111,7 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget* parent) : QMainWin m_container->addButtons(horizontalLayout); connect(m_instance.get(), &BaseInstance::profilerChanged, this, &InstanceWindow::updateButtons); - connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceWindow::updateButtons); + connect(APPLICATION, &Application::globalSettingsApplied, this, &InstanceWindow::updateButtons); } // restore window state diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 63fcc3c9b..e0f37968f 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -379,7 +379,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi connect(APPLICATION->instances().get(), &InstanceList::instanceSelectRequest, this, &MainWindow::instanceSelectRequest); // When the global settings page closes, we want to know about it and update our state - connect(APPLICATION, &Application::globalSettingsClosed, this, &MainWindow::globalSettingsClosed); + connect(APPLICATION, &Application::globalSettingsApplied, this, &MainWindow::globalSettingsClosed); m_statusLeft = new QLabel(tr("No instance selected"), this); m_statusCenter = new QLabel(tr("Total playtime: 0s"), this); @@ -1207,7 +1207,10 @@ void MainWindow::renameGroup(QString group) void MainWindow::undoTrashInstance() { - APPLICATION->instances()->undoTrashInstance(); + if (!APPLICATION->instances()->undoTrashInstance()) + QMessageBox::warning( + this, tr("Failed to undo trashing instance"), + tr("Some instances and shortcuts could not be restored.\nPlease check your trashbin to manually restore them.")); ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething()); } @@ -1406,11 +1409,15 @@ void MainWindow::on_actionDeleteInstance_triggered() } auto id = m_selectedInstance->id(); + QString shortcutStr; + auto shortcuts = m_selectedInstance->shortcuts(); + if (!shortcuts.isEmpty()) + shortcutStr = tr(" and its %n registered shortcut(s)", "", shortcuts.size()); auto response = CustomMessageBox::selectable(this, tr("Confirm Deletion"), - tr("You are about to delete \"%1\".\n" + tr("You are about to delete \"%1\"%2.\n" "This may be permanent and will completely delete the instance.\n\n" "Are you sure?") - .arg(m_selectedInstance->name()), + .arg(m_selectedInstance->name(), shortcutStr), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); diff --git a/launcher/ui/dialogs/CreateShortcutDialog.cpp b/launcher/ui/dialogs/CreateShortcutDialog.cpp index 278573a22..5cfe33c7f 100644 --- a/launcher/ui/dialogs/CreateShortcutDialog.cpp +++ b/launcher/ui/dialogs/CreateShortcutDialog.cpp @@ -83,12 +83,12 @@ CreateShortcutDialog::CreateShortcutDialog(InstancePtr instance, QWidget* parent QString applicationDir = FS::getApplicationsDir(); if (!desktopDir.isEmpty()) - ui->saveTargetSelectionBox->addItem(tr("Desktop"), QVariant::fromValue(SaveTarget::Desktop)); + ui->saveTargetSelectionBox->addItem(tr("Desktop"), QVariant::fromValue(ShortcutTarget::Desktop)); if (!applicationDir.isEmpty()) - ui->saveTargetSelectionBox->addItem(tr("Applications"), QVariant::fromValue(SaveTarget::Applications)); + ui->saveTargetSelectionBox->addItem(tr("Applications"), QVariant::fromValue(ShortcutTarget::Applications)); } - ui->saveTargetSelectionBox->addItem(tr("Other..."), QVariant::fromValue(SaveTarget::Other)); + ui->saveTargetSelectionBox->addItem(tr("Other..."), QVariant::fromValue(ShortcutTarget::Other)); // Populate worlds if (m_QuickJoinSupported) { @@ -206,17 +206,17 @@ void CreateShortcutDialog::createShortcut() } } - auto target = ui->saveTargetSelectionBox->currentData().value(); + auto target = ui->saveTargetSelectionBox->currentData().value(); auto name = ui->instNameTextBox->text(); if (name.isEmpty()) name = ui->instNameTextBox->placeholderText(); if (ui->overrideAccountCheckbox->isChecked()) extraArgs.append({ "--profile", ui->accountSelectionBox->currentData().toString() }); - ShortcutUtils::Shortcut args{ m_instance.get(), name, targetString, this, extraArgs, InstIconKey }; - if (target == SaveTarget::Desktop) + ShortcutUtils::Shortcut args{ m_instance.get(), name, targetString, this, extraArgs, InstIconKey, target }; + if (target == ShortcutTarget::Desktop) ShortcutUtils::createInstanceShortcutOnDesktop(args); - else if (target == SaveTarget::Applications) + else if (target == ShortcutTarget::Applications) ShortcutUtils::createInstanceShortcutInApplications(args); else ShortcutUtils::createInstanceShortcutInOther(args); diff --git a/launcher/ui/dialogs/CreateShortcutDialog.h b/launcher/ui/dialogs/CreateShortcutDialog.h index cfedbf017..29e68a787 100644 --- a/launcher/ui/dialogs/CreateShortcutDialog.h +++ b/launcher/ui/dialogs/CreateShortcutDialog.h @@ -54,9 +54,6 @@ class CreateShortcutDialog : public QDialog { InstancePtr m_instance; bool m_QuickJoinSupported = false; - // Index representations - enum class SaveTarget { Desktop, Applications, Other }; - // Functions void stateChanged(); }; diff --git a/launcher/ui/dialogs/CreateShortcutDialog.ui b/launcher/ui/dialogs/CreateShortcutDialog.ui index 9e2bdd747..24d4dc2dc 100644 --- a/launcher/ui/dialogs/CreateShortcutDialog.ui +++ b/launcher/ui/dialogs/CreateShortcutDialog.ui @@ -194,6 +194,20 @@ + + + + Note: If a shortcut is moved after creation, it won't be deleted when deleting the instance. + + + + + + + You'll need to delete them manually if that is the case. + + + diff --git a/launcher/ui/pagedialog/PageDialog.cpp b/launcher/ui/pagedialog/PageDialog.cpp index d211cb4d3..8ce53448a 100644 --- a/launcher/ui/pagedialog/PageDialog.cpp +++ b/launcher/ui/pagedialog/PageDialog.cpp @@ -21,42 +21,63 @@ #include #include "Application.h" -#include "settings/SettingsObject.h" -#include "ui/widgets/IconLabel.h" #include "ui/widgets/PageContainer.h" PageDialog::PageDialog(BasePageProvider* pageProvider, QString defaultId, QWidget* parent) : QDialog(parent) { setWindowTitle(pageProvider->dialogTitle()); - m_container = new PageContainer(pageProvider, defaultId, this); + m_container = new PageContainer(pageProvider, std::move(defaultId), this); + + auto* mainLayout = new QVBoxLayout(this); + + auto* focusStealer = new QPushButton(this); + mainLayout->addWidget(focusStealer); + focusStealer->setDefault(true); + focusStealer->hide(); - QVBoxLayout* mainLayout = new QVBoxLayout; mainLayout->addWidget(m_container); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0, 0, 0, 0); + setLayout(mainLayout); - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close); - buttons->button(QDialogButtonBox::Close)->setDefault(true); - buttons->button(QDialogButtonBox::Close)->setText(tr("Close")); + auto* buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttons->button(QDialogButtonBox::Ok)->setText(tr("&OK")); + buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel")); buttons->button(QDialogButtonBox::Help)->setText(tr("Help")); buttons->setContentsMargins(6, 0, 6, 0); m_container->addButtons(buttons); - connect(buttons->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(close())); - connect(buttons->button(QDialogButtonBox::Help), SIGNAL(clicked()), m_container, SLOT(help())); + connect(buttons->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &PageDialog::accept); + connect(buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &PageDialog::reject); + connect(buttons->button(QDialogButtonBox::Help), &QPushButton::clicked, m_container, &PageContainer::help); restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("PagedGeometry").toByteArray())); } +void PageDialog::accept() +{ + if (handleClose()) + QDialog::accept(); +} + void PageDialog::closeEvent(QCloseEvent* event) { - qDebug() << "Paged dialog close requested"; - if (m_container->prepareToClose()) { - qDebug() << "Paged dialog close approved"; - APPLICATION->settings()->set("PagedGeometry", saveGeometry().toBase64()); - qDebug() << "Paged dialog geometry saved"; + if (handleClose()) QDialog::closeEvent(event); - } +} + +bool PageDialog::handleClose() +{ + qDebug() << "Paged dialog close requested"; + if (!m_container->prepareToClose()) + return false; + + qDebug() << "Paged dialog close approved"; + APPLICATION->settings()->set("PagedGeometry", saveGeometry().toBase64()); + qDebug() << "Paged dialog geometry saved"; + + emit applied(); + return true; } diff --git a/launcher/ui/pagedialog/PageDialog.h b/launcher/ui/pagedialog/PageDialog.h index aa50bc5e1..9a8a3ccaa 100644 --- a/launcher/ui/pagedialog/PageDialog.h +++ b/launcher/ui/pagedialog/PageDialog.h @@ -25,8 +25,13 @@ class PageDialog : public QDialog { explicit PageDialog(BasePageProvider* pageProvider, QString defaultId = QString(), QWidget* parent = 0); virtual ~PageDialog() {} - private slots: - virtual void closeEvent(QCloseEvent* event); + signals: + void applied(); + + private: + void accept() override; + void closeEvent(QCloseEvent* event) override; + bool handleClose(); private: PageContainer* m_container; diff --git a/launcher/ui/pages/global/APIPage.h b/launcher/ui/pages/global/APIPage.h index d4ed92900..9252a9ab3 100644 --- a/launcher/ui/pages/global/APIPage.h +++ b/launcher/ui/pages/global/APIPage.h @@ -53,7 +53,7 @@ class APIPage : public QWidget, public BasePage { explicit APIPage(QWidget* parent = 0); ~APIPage(); - QString displayName() const override { return tr("APIs"); } + QString displayName() const override { return tr("Services"); } QIcon icon() const override { return APPLICATION->getThemedIcon("worlds"); } QString id() const override { return "apis"; } QString helpPage() const override { return "APIs"; } diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index 05c256bb2..c6a4593fc 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -10,7 +10,7 @@ 620 - + 0 @@ -24,15 +24,20 @@ 0 - - - 0 + + + true - - - Services - - + + + + 0 + -216 + 816 + 832 + + + @@ -50,7 +55,14 @@ - + + + + 0 + 0 + + + @@ -65,7 +77,7 @@ - + Use Default true @@ -91,30 +103,10 @@ Meta&data Server - - - - You can set this to a third-party metadata server to use patched libraries or other hacks. - - - Qt::RichText - - - true - - - - - - - - - - - Enter a custom URL for meta here. + You can set this to a third-party metadata server to use patched libraries or other hacks. Qt::RichText @@ -127,35 +119,80 @@ + + + + Use Default + + + - - - Qt::Vertical - - + + - 20 - 40 + 0 + 0 - + + User Agent + + + + + + Use Default + + + + + + + Enter a custom User Agent here. The special string $LAUNCHER_VER will be replaced with the version of the launcher. + + + true + + + + + - - - - - API Keys - - - &Microsoft Authentication + &API Keys + + + + &Microsoft Authentation + + + Qt::RichText + + + true + + + true + + + msaClientID + + + + + + + Use Default + + + @@ -170,99 +207,83 @@ - - - (Default) + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + + + Mod&rinth + + + Qt::RichText + + + true + + + true + + + modrinthToken - - - Enter a custom client ID for Microsoft Authentication here. - - - Qt::RichText - - - true - - - true - - - - - - - - - - true - - - &Modrinth API - - - - - - <html><head/><body><p>Note: you only need to set this to access private data. Read the <a href="https://docs.modrinth.com/api/#authentication">documentation</a> for more information.</p></body></html> - - - true - - - - - - - Enter a custom API token for Modrinth here. - - - Qt::RichText - - - true - - - true - - - - true - (None) + Use None - - - - - - - true - - - &CurseForge Core API - - - - + + - Note: you probably don't need to set this if CurseForge already works. + <html><head/><body><p>Note: you only need to set this to access private data. Read the <a href="https://docs.modrinth.com/api/#authentication">documentation</a> for more information.</p></body></html> + + + true + + + true - - + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + - Enter a custom API Key for CurseForge here. + &CurseForge Qt::RichText @@ -273,45 +294,71 @@ true + + flameKey + - + true - (Default) + Use Default - - - - - - - Technic Client ID - - - + - <html><head/><body><p>Note: you only need to set this to access private data.</p></body></html> + Note: you probably don't need to set this if CurseForge already works. + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + + + &Technic + + + technicClientID - (None) + Use Default - + - Enter a custom GUID client ID for Technic here. + <html><head/><body><p>Note: you only need to set this to access private data.</p></body></html> + + + true @@ -319,61 +366,16 @@ - + Qt::Vertical - - 20 - 40 - - - - - - - - - Miscellaneous - - - - - 0 0 - - User Agent - - - - - - - - - Enter a custom User Agent here. The special string $LAUNCHER_VER will be replaced with the version of the launcher. - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - diff --git a/launcher/ui/pages/global/AppearancePage.h b/launcher/ui/pages/global/AppearancePage.h new file mode 100644 index 000000000..29b2d34bf --- /dev/null +++ b/launcher/ui/pages/global/AppearancePage.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include "Application.h" +#include "java/JavaChecker.h" +#include "translations/TranslationsModel.h" +#include "ui/pages/BasePage.h" +#include "ui/widgets/AppearanceWidget.h" + +class QTextCharFormat; +class SettingsObject; + +class AppearancePage : public AppearanceWidget, public BasePage { + Q_OBJECT + + public: + explicit AppearancePage(QWidget* parent = nullptr) : AppearanceWidget(false, parent) { layout()->setContentsMargins(0, 0, 6, 0); } + + QString displayName() const override { return tr("Appearance"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("appearance"); } + QString id() const override { return "appearance-settings"; } + QString helpPage() const override { return "Launcher-settings"; } + + bool apply() override + { + applySettings(); + return true; + } + + void retranslate() override { retranslateUi(); } +}; diff --git a/launcher/ui/pages/global/ExternalToolsPage.cpp b/launcher/ui/pages/global/ExternalToolsPage.cpp index 33e9c5388..4110bd5ad 100644 --- a/launcher/ui/pages/global/ExternalToolsPage.cpp +++ b/launcher/ui/pages/global/ExternalToolsPage.cpp @@ -50,7 +50,6 @@ ExternalToolsPage::ExternalToolsPage(QWidget* parent) : QWidget(parent), ui(new Ui::ExternalToolsPage) { ui->setupUi(this); - ui->tabWidget->tabBar()->hide(); ui->jsonEditorTextBox->setClearButtonEnabled(true); @@ -128,13 +127,13 @@ void ExternalToolsPage::on_jvisualvmPathBtn_clicked() QString raw_dir = ui->jvisualvmPathEdit->text(); QString error; do { - raw_dir = QFileDialog::getOpenFileName(this, tr("JVisualVM Executable"), raw_dir); + raw_dir = QFileDialog::getOpenFileName(this, tr("VisualVM Executable"), raw_dir); if (raw_dir.isEmpty()) { break; } QString cooked_dir = FS::NormalizePath(raw_dir); if (!APPLICATION->profilers()["jvisualvm"]->check(cooked_dir, &error)) { - QMessageBox::critical(this, tr("Error"), tr("Error while checking JVisualVM install:\n%1").arg(error)); + QMessageBox::critical(this, tr("Error"), tr("Error while checking VisualVM install:\n%1").arg(error)); continue; } else { ui->jvisualvmPathEdit->setText(cooked_dir); @@ -146,9 +145,9 @@ void ExternalToolsPage::on_jvisualvmCheckBtn_clicked() { QString error; if (!APPLICATION->profilers()["jvisualvm"]->check(ui->jvisualvmPathEdit->text(), &error)) { - QMessageBox::critical(this, tr("Error"), tr("Error while checking JVisualVM install:\n%1").arg(error)); + QMessageBox::critical(this, tr("Error"), tr("Error while checking VisualVM install:\n%1").arg(error)); } else { - QMessageBox::information(this, tr("OK"), tr("JVisualVM setup seems to be OK")); + QMessageBox::information(this, tr("OK"), tr("VisualVM setup seems to be OK")); } } @@ -187,7 +186,7 @@ void ExternalToolsPage::on_mceditCheckBtn_clicked() void ExternalToolsPage::on_jsonEditorBrowseBtn_clicked() { - QString raw_file = QFileDialog::getOpenFileName(this, tr("JSON Editor"), + QString raw_file = QFileDialog::getOpenFileName(this, tr("Text Editor"), ui->jsonEditorTextBox->text().isEmpty() #if defined(Q_OS_LINUX) ? QString("/usr/bin") diff --git a/launcher/ui/pages/global/ExternalToolsPage.h b/launcher/ui/pages/global/ExternalToolsPage.h index 7248f0c99..377488ccf 100644 --- a/launcher/ui/pages/global/ExternalToolsPage.h +++ b/launcher/ui/pages/global/ExternalToolsPage.h @@ -51,7 +51,7 @@ class ExternalToolsPage : public QWidget, public BasePage { explicit ExternalToolsPage(QWidget* parent = 0); ~ExternalToolsPage(); - QString displayName() const override { return tr("External Tools"); } + QString displayName() const override { return tr("Tools"); } QIcon icon() const override { auto icon = APPLICATION->getThemedIcon("externaltools"); diff --git a/launcher/ui/pages/global/ExternalToolsPage.ui b/launcher/ui/pages/global/ExternalToolsPage.ui index 47c77842a..b094e3693 100644 --- a/launcher/ui/pages/global/ExternalToolsPage.ui +++ b/launcher/ui/pages/global/ExternalToolsPage.ui @@ -7,7 +7,7 @@ 0 0 673 - 751 + 823 @@ -24,28 +24,43 @@ 0 - - - 0 + + + true - - - Tab 1 - - + + + + 0 + 0 + 669 + 819 + + + - + - J&Profiler + &Editors - + - + + + &Text Editor + + + jsonEditorTextBox + + + + + - + - + Browse @@ -54,65 +69,38 @@ - + - Check + Used to edit component JSON files. - + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + - <html><head/><body><p><a href="https://www.ej-technologies.com/products/jprofiler/overview.html">https://www.ej-technologies.com/products/jprofiler/overview.html</a></p></body></html> + &MCEdit + + + mceditPathEdit - - - - - - - J&VisualVM - - - - - - - - - - - Browse - - - - - - - - - Check - - - - - - - <html><head/><body><p><a href="https://visualvm.github.io/">https://visualvm.github.io/</a></p></body></html> - - - - - - - - - - &MCEdit - - @@ -129,6 +117,12 @@ + + + 0 + 0 + + Check @@ -137,7 +131,7 @@ - <html><head/><body><p><a href="https://www.mcedit.net/">https://www.mcedit.net/</a></p></body></html> + <html><head/><body><p><a href="https://www.mcedit.net/">MCEdit Website</a> - Used as world editor in the instance Worlds menu.</p></body></html> @@ -145,28 +139,138 @@ - + - External Editors (leave empty for system default) + &Profilers - - - - - + + - &Text Editor: + Profilers are accessible through the Launch dropdown menu. jsonEditorTextBox - - + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + - Browse + J&Profiler + + + jprofilerPathEdit + + + + + + + + + + + + Browse + + + + + + + + + + 0 + 0 + + + + Check + + + + + + + <html><head/><body><p><a href="https://www.ej-technologies.com/products/jprofiler/overview.html">JProfiler Website</a></p></body></html> + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + + + &VisualVM + + + jvisualvmPathEdit + + + + + + + + + + + + Browse + + + + + + + + + + 0 + 0 + + + + Check + + + + + + + <html><head/><body><p><a href="https://visualvm.github.io/">VisualVM Website</a></p></body></html> @@ -180,8 +284,8 @@ - 20 - 216 + 0 + 0 diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index a4b2ac203..a40e38868 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -50,10 +50,22 @@ 0 0 535 - 610 + 606 + + 0 + + + 0 + + + 0 + + + 0 + @@ -65,78 +77,56 @@ - Management + Installations - - - Downloaded Java Versions - - - - - - - 0 - 0 - - - - - - - - - - Download - - - - - - - Remove - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Refresh - - - - - - - + + + + + Download + + + + + + + Remove + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Refresh + + + + - - - Qt::Vertical + + + + 0 + 0 + - - - 20 - 40 - - - + diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index d89a65a85..132a2c320 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -81,24 +81,14 @@ LauncherPage::LauncherPage(QWidget* parent) : QWidget(parent), ui(new Ui::Launch ui->sortingModeGroup->setId(ui->sortByNameBtn, Sort_Name); ui->sortingModeGroup->setId(ui->sortLastLaunchedBtn, Sort_LastLaunch); - defaultFormat = new QTextCharFormat(ui->fontPreview->currentCharFormat()); - - m_languageModel = APPLICATION->translations(); loadSettings(); ui->updateSettingsBox->setHidden(!APPLICATION->updater()); - - connect(ui->fontSizeBox, QOverload::of(&QSpinBox::valueChanged), this, &LauncherPage::refreshFontPreview); - connect(ui->consoleFont, &QFontComboBox::currentFontChanged, this, &LauncherPage::refreshFontPreview); - connect(ui->themeCustomizationWidget, &ThemeCustomizationWidget::currentWidgetThemeChanged, this, &LauncherPage::refreshFontPreview); - - connect(ui->themeCustomizationWidget, &ThemeCustomizationWidget::currentCatChanged, APPLICATION, &Application::currentCatChanged); } LauncherPage::~LauncherPage() { delete ui; - delete defaultFormat; } bool LauncherPage::apply() @@ -203,9 +193,9 @@ void LauncherPage::on_skinsDirBrowseBtn_clicked() } } -void LauncherPage::on_metadataDisableBtn_clicked() +void LauncherPage::on_metadataEnableBtn_clicked() { - ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); + ui->metadataWarningLabel->setHidden(ui->metadataEnableBtn->isChecked()); } void LauncherPage::applySettings() @@ -226,9 +216,6 @@ void LauncherPage::applySettings() s->set("RequestTimeout", ui->timeoutSecondsSpinBox->value()); // Console settings - QString consoleFontFamily = ui->consoleFont->currentFont().family(); - s->set("ConsoleFont", consoleFontFamily); - s->set("ConsoleFontSize", ui->fontSizeBox->value()); s->set("ConsoleMaxLines", ui->lineLimitSpinBox->value()); s->set("ConsoleOverflowStop", ui->checkStopLogging->checkState() != Qt::Unchecked); @@ -269,13 +256,10 @@ void LauncherPage::applySettings() break; } - // Cat - s->set("CatOpacity", ui->catOpacitySpinBox->value()); - // Mods - s->set("ModMetadataDisabled", ui->metadataDisableBtn->isChecked()); - s->set("ModDependenciesDisabled", ui->dependenciesDisableBtn->isChecked()); - s->set("SkipModpackUpdatePrompt", ui->skipModpackUpdatePromptBtn->isChecked()); + s->set("ModMetadataDisabled", !ui->metadataEnableBtn->isChecked()); + s->set("ModDependenciesDisabled", !ui->dependenciesEnableBtn->isChecked()); + s->set("SkipModpackUpdatePrompt", !ui->modpackUpdatePromptBtn->isChecked()); } void LauncherPage::loadSettings() { @@ -286,11 +270,6 @@ void LauncherPage::loadSettings() ui->updateIntervalSpinBox->setValue(APPLICATION->updater()->getUpdateCheckInterval() / 3600); } - // Toolbar/menu bar settings (not applicable if native menu bar is present) - ui->toolsBox->setEnabled(!QMenuBar().isNativeMenuBar()); -#ifdef Q_OS_MACOS - ui->toolsBox->setVisible(!QMenuBar().isNativeMenuBar()); -#endif ui->preferMenuBarCheckBox->setChecked(s->get("MenuBarInsteadOfToolBar").toBool()); ui->numberOfConcurrentTasksSpinBox->setValue(s->get("NumberOfConcurrentTasks").toInt()); @@ -299,17 +278,6 @@ void LauncherPage::loadSettings() ui->timeoutSecondsSpinBox->setValue(s->get("RequestTimeout").toInt()); // Console settings - QString fontFamily = APPLICATION->settings()->get("ConsoleFont").toString(); - QFont consoleFont(fontFamily); - ui->consoleFont->setCurrentFont(consoleFont); - - bool conversionOk = true; - int fontSize = APPLICATION->settings()->get("ConsoleFontSize").toInt(&conversionOk); - if (!conversionOk) { - fontSize = 11; - } - ui->fontSizeBox->setValue(fontSize); - refreshFontPreview(); ui->lineLimitSpinBox->setValue(s->get("ConsoleMaxLines").toInt()); ui->checkStopLogging->setChecked(s->get("ConsoleOverflowStop").toBool()); @@ -342,59 +310,11 @@ void LauncherPage::loadSettings() } ui->renamingBehaviorComboBox->setCurrentIndex(renamingModeEnum); - // Cat - ui->catOpacitySpinBox->setValue(s->get("CatOpacity").toInt()); - // Mods - ui->metadataDisableBtn->setChecked(s->get("ModMetadataDisabled").toBool()); - ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); - ui->dependenciesDisableBtn->setChecked(s->get("ModDependenciesDisabled").toBool()); - ui->skipModpackUpdatePromptBtn->setChecked(s->get("SkipModpackUpdatePrompt").toBool()); -} - -void LauncherPage::refreshFontPreview() -{ - const LogColors& colors = APPLICATION->themeManager()->getLogColors(); - - int fontSize = ui->fontSizeBox->value(); - QString fontFamily = ui->consoleFont->currentFont().family(); - ui->fontPreview->clear(); - defaultFormat->setFont(QFont(fontFamily, fontSize)); - - auto print = [this, colors](const QString& message, MessageLevel::Enum level) { - QTextCharFormat format(*defaultFormat); - - QColor bg = colors.background.value(level); - QColor fg = colors.foreground.value(level); - - if (bg.isValid()) - format.setBackground(bg); - - if (fg.isValid()) - format.setForeground(fg); - - // append a paragraph/line - auto workCursor = ui->fontPreview->textCursor(); - workCursor.movePosition(QTextCursor::End); - workCursor.insertText(message, format); - workCursor.insertBlock(); - }; - - print(QString("%1 version: %2 (%3)\n") - .arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM), - MessageLevel::Launcher); - - QDate today = QDate::currentDate(); - - if (today.month() == 10 && today.day() == 31) - print(tr("[Test/ERROR] OOoooOOOoooo! A spooky error!"), MessageLevel::Error); - else - print(tr("[Test/ERROR] A spooky error!"), MessageLevel::Error); - - print(tr("[Test/INFO] A harmless message..."), MessageLevel::Info); - print(tr("[Test/WARN] A not so spooky warning."), MessageLevel::Warning); - print(tr("[Test/DEBUG] A secret debugging message..."), MessageLevel::Debug); - print(tr("[Test/FATAL] A terrifying fatal error!"), MessageLevel::Fatal); + ui->metadataEnableBtn->setChecked(!s->get("ModMetadataDisabled").toBool()); + ui->metadataWarningLabel->setHidden(ui->metadataEnableBtn->isChecked()); + ui->dependenciesEnableBtn->setChecked(!s->get("ModDependenciesDisabled").toBool()); + ui->modpackUpdatePromptBtn->setChecked(!s->get("SkipModpackUpdatePrompt").toBool()); } void LauncherPage::retranslate() diff --git a/launcher/ui/pages/global/LauncherPage.h b/launcher/ui/pages/global/LauncherPage.h index 02f371b04..d76c84b63 100644 --- a/launcher/ui/pages/global/LauncherPage.h +++ b/launcher/ui/pages/global/LauncherPage.h @@ -57,8 +57,8 @@ class LauncherPage : public QWidget, public BasePage { explicit LauncherPage(QWidget* parent = 0); ~LauncherPage(); - QString displayName() const override { return tr("Launcher"); } - QIcon icon() const override { return APPLICATION->getThemedIcon("launcher"); } + QString displayName() const override { return tr("General"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("settings"); } QString id() const override { return "launcher-settings"; } QString helpPage() const override { return "Launcher-settings"; } bool apply() override; @@ -75,23 +75,8 @@ class LauncherPage : public QWidget, public BasePage { void on_downloadsDirBrowseBtn_clicked(); void on_javaDirBrowseBtn_clicked(); void on_skinsDirBrowseBtn_clicked(); - void on_metadataDisableBtn_clicked(); - - /*! - * Updates the font preview - */ - void refreshFontPreview(); + void on_metadataEnableBtn_clicked(); private: Ui::LauncherPage* ui; - - /*! - * Stores the currently selected update channel. - */ - QString m_currentUpdateChannel; - - // default format for the font preview... - QTextCharFormat* defaultFormat; - - std::shared_ptr m_languageModel; }; diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 7942f4f4f..55478e6a0 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -6,8 +6,8 @@ 0 0 - 511 - 726 + 767 + 796 @@ -16,7 +16,7 @@ 0 - + 0 @@ -30,426 +30,37 @@ 0 - - - + + + Qt::ScrollBarAsNeeded - - QTabWidget::Rounded + + true - - 0 - - - - Features - - + + + + 0 + -356 + 742 + 1148 + + + - - - Qt::ScrollBarAsNeeded - - - true - - - - - 0 - 0 - 473 - 770 - - - - - - - Update Settings - - - - - - Check for updates automatically - - - - - - - - - Update interval - - - - - - - Set it to 0 to only check on launch - - - h - - - 0 - - - 99999999 - - - - - - - - - - - - Folders - - - - - - I&nstances: - - - instDirTextBox - - - - - - - - - - Browse - - - - - - - - Rename instance folders - - - - - - - - Never - - - - - Ask - - - - - Always - - - - - - - - - &Mods: - - - modsDirTextBox - - - - - - - - - - Browse - - - - - - - - &Icons: - - - iconsDirTextBox - - - - - - - - - - Browse - - - - - - - - &Java: - - - javaDirTextBox - - - - - - - - - - Browse - - - - - - - - &Skins: - - - skinsDirTextBox - - - - - - - - - - Browse - - - - - - - - &Downloads: - - - downloadsDirTextBox - - - - - - - - - - Browse - - - - - - - - - - When enabled, in addition to the downloads folder, its sub folders will also be searched when looking for resources (e.g. when looking for blocked mods on CurseForge). - - - Check downloads folder recursively - - - - - - - When enabled, it will move blocked resources instead of copying them. - - - Move blocked resources - - - - - - - - - - - - Mods - - - - - - Disable using metadata provided by mod providers (like Modrinth or CurseForge) for mods. - - - Disable using metadata for mods - - - - - - - <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: Disabling mod metadata may also disable some QoL features, such as mod updating!</span></p></body></html> - - - true - - - - - - - Disable the automatic detection, installation, and updating of mod dependencies. - - - Disable automatic mod dependency management - - - - - - - When creating a new modpack instance, do not suggest updating existing instances instead. - - - Skip modpack update prompt - - - - - - - - - - Miscellaneous - - - - - - 1 - - - - - - - Number of concurrent tasks - - - - - - - 1 - - - - - - - Number of concurrent downloads - - - - - - - Number of manual retries - - - - - - - 0 - - - - - - - Seconds to wait until the requests are terminated - - - Timeout for HTTP requests - - - - - - - s - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - - - - User Interface - - - - + true - Instance view sorting mode + User Interface - + - + - &By last launched + Instance Sorting - - sortingModeGroup - @@ -462,81 +73,80 @@ - - - - - - - Theme - - - - - - - - - - - Cat - - - - - - Set the cat's opacity. 0% is fully transparent and 100% is fully opaque. - + - Opacity + &By last launched + + sortingModeGroup + - - - - - - - % - - - 100 - - - 0 - - - - - + + - Qt::Horizontal + Qt::Vertical + + + QSizePolicy::Fixed - 40 - 20 + 0 + 6 + + + + + + + + Instance Renaming + + + + + + + + 0 + 0 + + + + + Ask what to do with the folder + + + + + Always rename the folder + + + + + Never rename the folder—only the displayed name + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 - - - - - - - - 0 - 0 - - - - Tools - - @@ -551,42 +161,300 @@ - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - Console - - - - + - &History limit + Updater - + - + + + + + How Often? + + + + + + + + 0 + 0 + + + + Set to 0 to only check on launch + + + On Launch + + + hours + + + Every + + + 0 + + + 168 + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + - &Stop logging when log overflows + Check for updates automatically + + + + + + + Folders + + + + + + + + + Browse + + + + + + + &Java: + + + javaDirTextBox + + + + + + + Browse + + + + + + + &Skins: + + + skinsDirTextBox + + + + + + + &Mods: + + + modsDirTextBox + + + + + + + Browse + + + + + + + &Downloads: + + + downloadsDirTextBox + + + + + + + + + + + + I&nstances: + + + instDirTextBox + + + + + + + Browse + + + + + + + Browse + + + + + + + + + + Browse + + + + + + + + + + + + + &Icons: + + + iconsDirTextBox + + + + + + + + + + Mods and Modpacks + + + + + + When enabled, in addition to the downloads folder, its sub folders will also be searched when looking for resources (e.g. when looking for blocked mods on CurseForge). + + + Check &subfolders for blocked mods + + + + + + + When enabled, it will move blocked resources instead of copying them. + + + Move blocked mods instead of copying them + + + + + + + Store version information provided by mod providers (like Modrinth or CurseForge) for mods. + + + Keep track of mod metadata + + + + + + + <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: Disabling mod metadata may also disable some QoL features, such as mod updating!</span></p></body></html> + + + true + + + + + + + Automatically detect, install, and update mod dependencies. + + + Install dependencies automatically + + + + + + + When creating a new modpack instance, suggest updating an existing instance instead. + + + Suggest to update an existing instance during modpack installation + + + + + + + + + + Console + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 0 + 0 + + + + Log History &Limit: + + + lineLimitSpinBox + + + + - + 0 0 @@ -608,83 +476,170 @@ + + + + &Stop logging when log overflows + + + - - - - 0 - 0 - - + - Console &font + Tasks - - - + + + - + 0 0 - - Qt::ScrollBarAsNeeded + + + 60 + 0 + - - false - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + 0 - - + + - + 0 0 + + + 60 + 0 + + + + 1 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + s + + + + + + + Retry Limit: + + + + + + + Concurrent Download Limit: + + + + + + + Seconds to wait until the requests are terminated + + + HTTP Timeout: + - + + + + 0 + 0 + + + + + 60 + 0 + + - 5 - - - 16 - - - 11 + 1 + + + + Concurrent Task Limit: + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + - - - ThemeCustomizationWidget - QWidget -
ui/widgets/ThemeCustomizationWidget.h
- 1 -
-
- tabWidget scrollArea + sortByNameBtn + sortLastLaunchedBtn + renamingBehaviorComboBox + preferMenuBarCheckBox autoUpdateCheckBox updateIntervalSpinBox instDirTextBox @@ -700,26 +655,20 @@ downloadsDirTextBox downloadsDirBrowseBtn downloadsDirWatchRecursiveCheckBox - metadataDisableBtn - dependenciesDisableBtn - skipModpackUpdatePromptBtn + downloadsDirMoveCheckBox + metadataEnableBtn + dependenciesEnableBtn + modpackUpdatePromptBtn + lineLimitSpinBox + checkStopLogging numberOfConcurrentTasksSpinBox numberOfConcurrentDownloadsSpinBox numberOfManualRetriesSpinBox timeoutSecondsSpinBox - sortLastLaunchedBtn - sortByNameBtn - catOpacitySpinBox - preferMenuBarCheckBox - lineLimitSpinBox - checkStopLogging - consoleFont - fontSizeBox - fontPreview - \ No newline at end of file + diff --git a/launcher/ui/pages/global/ProxyPage.cpp b/launcher/ui/pages/global/ProxyPage.cpp index 9caffcb37..979f07c6a 100644 --- a/launcher/ui/pages/global/ProxyPage.cpp +++ b/launcher/ui/pages/global/ProxyPage.cpp @@ -46,7 +46,6 @@ ProxyPage::ProxyPage(QWidget* parent) : QWidget(parent), ui(new Ui::ProxyPage) { ui->setupUi(this); - ui->tabWidget->tabBar()->hide(); loadSettings(); updateCheckboxStuff(); diff --git a/launcher/ui/pages/global/ProxyPage.ui b/launcher/ui/pages/global/ProxyPage.ui index 91ba46b3d..dec8d0a26 100644 --- a/launcher/ui/pages/global/ProxyPage.ui +++ b/launcher/ui/pages/global/ProxyPage.ui @@ -23,184 +23,205 @@ 0 - - 0 - 0 - - - - - - - - - - This only applies to the launcher. Minecraft does not accept proxy settings. - - - Qt::AlignCenter - - - true - - - - - - - Type - - - - - - Uses your system's default proxy settings. - - - &Default - - - proxyGroup - - - - - - - &None - - - proxyGroup - - - - - - - &SOCKS5 - - - proxyGroup - - - - - - - &HTTP - - - proxyGroup - - - - - - - - - - &Address and Port - - - - - - 127.0.0.1 - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - QAbstractSpinBox::PlusMinus - - - 65535 - - - 8080 - - - - - - - - - - Authentication - - - - - - - - - &Username: - - - proxyUserEdit - - - - - - - &Password: - - - proxyPassEdit - - - - - - - QLineEdit::Password - - - - - - - Note: Proxy username and password are stored in plain text inside the launcher's configuration file! - - - true - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - + + + This only applies to the launcher. Minecraft does not accept proxy settings. + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true + + + + + Type + + + + + + Uses your system's default proxy settings. + + + Use S&ystem Settings + + + proxyGroup + + + + + + + &None + + + proxyGroup + + + + + + + &SOCKS5 + + + proxyGroup + + + + + + + &HTTP + + + proxyGroup + + + + + + + + + + &Address and Port + + + + + + + 0 + 0 + + + + + 300 + 0 + + + + 127.0.0.1 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + QAbstractSpinBox::PlusMinus + + + 65535 + + + 8080 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Authentication + + + + + + &Username: + + + proxyUserEdit + + + + + + + + + + &Password: + + + proxyPassEdit + + + + + + + QLineEdit::Password + + + + + + + Note: Proxy username and password are stored in plain text inside the launcher's configuration file! + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + +
+ + proxyDefaultBtn + proxyNoneBtn + proxySOCKS5Btn + proxyHTTPBtn + proxyAddrEdit + proxyPortEdit + proxyUserEdit + proxyPassEdit + diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index de173937b..2c507e84b 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -49,7 +49,7 @@ class InstanceSettingsPage : public MinecraftSettingsWidget, public BasePage { : MinecraftSettingsWidget(std::move(instance), parent) { connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::saveSettings); - connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings); + connect(APPLICATION, &Application::globalSettingsApplied, this, &InstanceSettingsPage::loadSettings); } ~InstanceSettingsPage() override {} QString displayName() const override { return tr("Settings"); } diff --git a/launcher/ui/setupwizard/ThemeWizardPage.cpp b/launcher/ui/setupwizard/ThemeWizardPage.cpp index fe11ed9ae..c97037f9f 100644 --- a/launcher/ui/setupwizard/ThemeWizardPage.cpp +++ b/launcher/ui/setupwizard/ThemeWizardPage.cpp @@ -34,37 +34,6 @@ ThemeWizardPage::ThemeWizardPage(QWidget* parent) : BaseWizardPage(parent), ui(n updateIcons(); updateCat(); } - -ThemeWizardPage::~ThemeWizardPage() -{ - delete ui; -} - -void ThemeWizardPage::updateIcons() -{ - qDebug() << "Setting Icons"; - ui->previewIconButton0->setIcon(APPLICATION->getThemedIcon("new")); - ui->previewIconButton1->setIcon(APPLICATION->getThemedIcon("centralmods")); - ui->previewIconButton2->setIcon(APPLICATION->getThemedIcon("viewfolder")); - ui->previewIconButton3->setIcon(APPLICATION->getThemedIcon("launch")); - ui->previewIconButton4->setIcon(APPLICATION->getThemedIcon("copy")); - ui->previewIconButton5->setIcon(APPLICATION->getThemedIcon("export")); - ui->previewIconButton6->setIcon(APPLICATION->getThemedIcon("delete")); - ui->previewIconButton7->setIcon(APPLICATION->getThemedIcon("about")); - ui->previewIconButton8->setIcon(APPLICATION->getThemedIcon("settings")); - ui->previewIconButton9->setIcon(APPLICATION->getThemedIcon("cat")); - update(); - repaint(); - parentWidget()->update(); -} - -void ThemeWizardPage::updateCat() -{ - qDebug() << "Setting Cat"; - ui->catImagePreviewButton->setIcon(QIcon(QString(R"(%1)").arg(APPLICATION->themeManager()->getCatPack()))); -} - -void ThemeWizardPage::retranslate() { ui->retranslateUi(this); } diff --git a/launcher/ui/setupwizard/ThemeWizardPage.h b/launcher/ui/setupwizard/ThemeWizardPage.h index f3d40b6d8..8ea438398 100644 --- a/launcher/ui/setupwizard/ThemeWizardPage.h +++ b/launcher/ui/setupwizard/ThemeWizardPage.h @@ -17,27 +17,30 @@ */ #pragma once +#include +#include #include #include "BaseWizardPage.h" -namespace Ui { -class ThemeWizardPage; -} - class ThemeWizardPage : public BaseWizardPage { Q_OBJECT public: - explicit ThemeWizardPage(QWidget* parent = nullptr); - ~ThemeWizardPage(); + ThemeWizardPage(QWidget* parent = nullptr) : BaseWizardPage(parent) + { + auto layout = new QVBoxLayout(this); + layout->addWidget(&widget); + layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + + setTitle(tr("Appearance")); + setSubTitle(tr("Select theme and icons to use")); + } bool validatePage() override { return true; }; - void retranslate() override; - - private slots: - void updateIcons(); - void updateCat(); + void retranslate() override { widget.retranslateUi(); } private: - Ui::ThemeWizardPage* ui; + AppearanceWidget widget{true}; }; diff --git a/launcher/ui/setupwizard/ThemeWizardPage.ui b/launcher/ui/setupwizard/ThemeWizardPage.ui deleted file mode 100644 index 01394ea40..000000000 --- a/launcher/ui/setupwizard/ThemeWizardPage.ui +++ /dev/null @@ -1,371 +0,0 @@ - - - ThemeWizardPage - - - - 0 - 0 - 510 - 552 - - - - WizardPage - - - - - - Select the Theme you wish to use - - - - - - - - 0 - 100 - - - - - - - - Hint: The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. - - - true - - - - - - - Qt::Horizontal - - - - - - - Preview: - - - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - 0 - 0 - - - - - 30 - 30 - - - - - .. - - - false - - - true - - - - - - - - - - 0 - 256 - - - - The cat appears in the background and does not serve a purpose, it is purely visual. - - - - - - - 256 - 256 - - - - true - - - - - - - Qt::Vertical - - - - 20 - 193 - - - - - - - - - ThemeCustomizationWidget - QWidget -
ui/widgets/ThemeCustomizationWidget.h
-
-
- - -
diff --git a/launcher/ui/themes/CatPack.cpp b/launcher/ui/themes/CatPack.cpp index 85eb85a18..416b86139 100644 --- a/launcher/ui/themes/CatPack.cpp +++ b/launcher/ui/themes/CatPack.cpp @@ -43,7 +43,7 @@ #include "FileSystem.h" #include "Json.h" -QString BasicCatPack::path() +QString BasicCatPack::path() const { const auto now = QDate::currentDate(); const auto birthday = QDate(now.year(), 11, 1); @@ -100,12 +100,12 @@ QDate ensureDay(int year, int month, int day) return QDate(year, month, day); } -QString JsonCatPack::path() +QString JsonCatPack::path() const { return path(QDate::currentDate()); } -QString JsonCatPack::path(QDate now) +QString JsonCatPack::path(QDate now) const { for (auto var : m_variants) { QDate startDate = ensureDay(now.year(), var.startTime.month, var.startTime.day); diff --git a/launcher/ui/themes/CatPack.h b/launcher/ui/themes/CatPack.h index 5a13d0cef..e0e34f86e 100644 --- a/launcher/ui/themes/CatPack.h +++ b/launcher/ui/themes/CatPack.h @@ -43,18 +43,18 @@ class CatPack { public: virtual ~CatPack() {} - virtual QString id() = 0; - virtual QString name() = 0; - virtual QString path() = 0; + virtual QString id() const = 0; + virtual QString name() const = 0; + virtual QString path() const = 0; }; class BasicCatPack : public CatPack { public: BasicCatPack(QString id, QString name) : m_id(id), m_name(name) {} BasicCatPack(QString id) : BasicCatPack(id, id) {} - virtual QString id() override { return m_id; } - virtual QString name() override { return m_name; } - virtual QString path() override; + virtual QString id() const override { return m_id; } + virtual QString name() const override { return m_name; } + virtual QString path() const override; protected: QString m_id; @@ -65,7 +65,7 @@ class FileCatPack : public BasicCatPack { public: FileCatPack(QString id, QFileInfo& fileInfo) : BasicCatPack(id), m_path(fileInfo.absoluteFilePath()) {} FileCatPack(QFileInfo& fileInfo) : FileCatPack(fileInfo.baseName(), fileInfo) {} - virtual QString path() { return m_path; } + virtual QString path() const { return m_path; } private: QString m_path; @@ -83,8 +83,8 @@ class JsonCatPack : public BasicCatPack { PartialDate endTime; }; JsonCatPack(QFileInfo& manifestInfo); - virtual QString path() override; - QString path(QDate now); + virtual QString path() const override; + QString path(QDate now) const; private: QString m_default_path; diff --git a/launcher/ui/themes/HintOverrideProxyStyle.cpp b/launcher/ui/themes/HintOverrideProxyStyle.cpp index f5b8232a8..6d95cc7dd 100644 --- a/launcher/ui/themes/HintOverrideProxyStyle.cpp +++ b/launcher/ui/themes/HintOverrideProxyStyle.cpp @@ -20,7 +20,7 @@ HintOverrideProxyStyle::HintOverrideProxyStyle(QStyle* style) : QProxyStyle(style) { - setObjectName(style->objectName()); + setObjectName(baseStyle()->objectName()); } int HintOverrideProxyStyle::styleHint(QStyle::StyleHint hint, @@ -31,5 +31,11 @@ int HintOverrideProxyStyle::styleHint(QStyle::StyleHint hint, if (hint == QStyle::SH_ItemView_ActivateItemOnSingleClick) return 0; + if (hint == QStyle::SH_Slider_AbsoluteSetButtons) + return Qt::LeftButton | Qt::MiddleButton; + + if (hint == QStyle::SH_Slider_PageSetButtons) + return Qt::RightButton; + return QProxyStyle::styleHint(hint, option, widget, returnData); } diff --git a/launcher/ui/themes/ITheme.h b/launcher/ui/themes/ITheme.h index 7dc5fc64a..a3dd14d09 100644 --- a/launcher/ui/themes/ITheme.h +++ b/launcher/ui/themes/ITheme.h @@ -47,6 +47,7 @@ struct LogColors { }; // TODO: rename to Theme; this is not an interface as it contains method implementations +// TODO: make methods const class ITheme { public: virtual ~ITheme() {} diff --git a/launcher/ui/themes/IconTheme.cpp b/launcher/ui/themes/IconTheme.cpp index 4bd889854..6415c5148 100644 --- a/launcher/ui/themes/IconTheme.cpp +++ b/launcher/ui/themes/IconTheme.cpp @@ -21,8 +21,6 @@ #include #include -IconTheme::IconTheme(const QString& id, const QString& path) : m_id(id), m_path(path) {} - bool IconTheme::load() { const QString path = m_path + "/index.theme"; @@ -36,18 +34,3 @@ bool IconTheme::load() settings.endGroup(); return !m_name.isNull(); } - -QString IconTheme::id() -{ - return m_id; -} - -QString IconTheme::path() -{ - return m_path; -} - -QString IconTheme::name() -{ - return m_name; -} diff --git a/launcher/ui/themes/IconTheme.h b/launcher/ui/themes/IconTheme.h index 4e466c6ae..f49e39289 100644 --- a/launcher/ui/themes/IconTheme.h +++ b/launcher/ui/themes/IconTheme.h @@ -22,13 +22,13 @@ class IconTheme { public: - IconTheme(const QString& id, const QString& path); + IconTheme(const QString& id, const QString& path) : m_id(id), m_path(path) {} IconTheme() = default; bool load(); - QString id(); - QString path(); - QString name(); + QString id() const { return m_id; } + QString path() const { return m_path; } + QString name() const { return m_name; } private: QString m_id; diff --git a/launcher/ui/widgets/AppearanceWidget.cpp b/launcher/ui/widgets/AppearanceWidget.cpp new file mode 100644 index 000000000..731b72727 --- /dev/null +++ b/launcher/ui/widgets/AppearanceWidget.cpp @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2025 TheKodeToad + * Copyright (C) 2022 Tayou + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AppearanceWidget.h" +#include "ui_AppearanceWidget.h" + +#include +#include +#include "BuildConfig.h" +#include "ui/themes/ITheme.h" +#include "ui/themes/ThemeManager.h" + +AppearanceWidget::AppearanceWidget(bool themesOnly, QWidget* parent) + : QWidget(parent), m_ui(new Ui::AppearanceWidget), m_themesOnly(themesOnly) +{ + m_ui->setupUi(this); + + m_ui->catPreview->setGraphicsEffect(new QGraphicsOpacityEffect(this)); + + m_defaultFormat = QTextCharFormat(m_ui->consolePreview->currentCharFormat()); + + if (themesOnly) { + m_ui->catPackLabel->hide(); + m_ui->catPackComboBox->hide(); + m_ui->catPackFolder->hide(); + m_ui->settingsBox->hide(); + m_ui->consolePreview->hide(); + m_ui->catPreview->hide(); + loadThemeSettings(); + } else { + loadSettings(); + loadThemeSettings(); + + updateConsolePreview(); + updateCatPreview(); + } + + connect(m_ui->fontSizeBox, QOverload::of(&QSpinBox::valueChanged), this, &AppearanceWidget::updateConsolePreview); + connect(m_ui->consoleFont, &QFontComboBox::currentFontChanged, this, &AppearanceWidget::updateConsolePreview); + + connect(m_ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &AppearanceWidget::applyIconTheme); + connect(m_ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &AppearanceWidget::applyWidgetTheme); + connect(m_ui->catPackComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &AppearanceWidget::applyCatTheme); + connect(m_ui->catOpacitySlider, &QAbstractSlider::valueChanged, this, &AppearanceWidget::updateCatPreview); + + connect(m_ui->iconsFolder, &QPushButton::clicked, this, + [] { DesktopServices::openPath(APPLICATION->themeManager()->getIconThemesFolder().path()); }); + connect(m_ui->widgetStyleFolder, &QPushButton::clicked, this, + [] { DesktopServices::openPath(APPLICATION->themeManager()->getApplicationThemesFolder().path()); }); + connect(m_ui->catPackFolder, &QPushButton::clicked, this, + [] { DesktopServices::openPath(APPLICATION->themeManager()->getCatPacksFolder().path()); }); + connect(m_ui->reloadThemesButton, &QPushButton::pressed, this, &AppearanceWidget::loadThemeSettings); +} + +AppearanceWidget::~AppearanceWidget() +{ + delete m_ui; +} + +void AppearanceWidget::applySettings() +{ + SettingsObjectPtr settings = APPLICATION->settings(); + QString consoleFontFamily = m_ui->consoleFont->currentFont().family(); + settings->set("ConsoleFont", consoleFontFamily); + settings->set("ConsoleFontSize", m_ui->fontSizeBox->value()); + settings->set("CatOpacity", m_ui->catOpacitySlider->value()); +} + +void AppearanceWidget::loadSettings() +{ + QString fontFamily = APPLICATION->settings()->get("ConsoleFont").toString(); + QFont consoleFont(fontFamily); + m_ui->consoleFont->setCurrentFont(consoleFont); + + bool conversionOk = true; + int fontSize = APPLICATION->settings()->get("ConsoleFontSize").toInt(&conversionOk); + if (!conversionOk) { + fontSize = 11; + } + m_ui->fontSizeBox->setValue(fontSize); + + m_ui->catOpacitySlider->setValue(APPLICATION->settings()->get("CatOpacity").toInt()); +} + +void AppearanceWidget::retranslateUi() +{ + m_ui->retranslateUi(this); +} + +void AppearanceWidget::applyIconTheme(int index) +{ + auto settings = APPLICATION->settings(); + auto originalIconTheme = settings->get("IconTheme").toString(); + auto newIconTheme = m_ui->iconsComboBox->itemData(index).toString(); + if (originalIconTheme != newIconTheme) { + settings->set("IconTheme", newIconTheme); + APPLICATION->themeManager()->applyCurrentlySelectedTheme(); + } +} + +void AppearanceWidget::applyWidgetTheme(int index) +{ + auto settings = APPLICATION->settings(); + auto originalAppTheme = settings->get("ApplicationTheme").toString(); + auto newAppTheme = m_ui->widgetStyleComboBox->itemData(index).toString(); + if (originalAppTheme != newAppTheme) { + settings->set("ApplicationTheme", newAppTheme); + APPLICATION->themeManager()->applyCurrentlySelectedTheme(); + } + + updateConsolePreview(); +} + +void AppearanceWidget::applyCatTheme(int index) +{ + auto settings = APPLICATION->settings(); + auto originalCat = settings->get("BackgroundCat").toString(); + auto newCat = m_ui->catPackComboBox->itemData(index).toString(); + if (originalCat != newCat) { + settings->set("BackgroundCat", newCat); + } + + APPLICATION->currentCatChanged(index); + updateCatPreview(); +} + +void AppearanceWidget::loadThemeSettings() +{ + APPLICATION->themeManager()->refresh(); + + m_ui->iconsComboBox->blockSignals(true); + m_ui->widgetStyleComboBox->blockSignals(true); + m_ui->catPackComboBox->blockSignals(true); + + m_ui->iconsComboBox->clear(); + m_ui->widgetStyleComboBox->clear(); + m_ui->catPackComboBox->clear(); + + const SettingsObjectPtr settings = APPLICATION->settings(); + + const QString currentIconTheme = settings->get("IconTheme").toString(); + const auto iconThemes = APPLICATION->themeManager()->getValidIconThemes(); + + for (int i = 0; i < iconThemes.count(); ++i) { + const IconTheme* theme = iconThemes[i]; + + QIcon iconForComboBox = QIcon(theme->path() + "/scalable/settings"); + m_ui->iconsComboBox->addItem(iconForComboBox, theme->name(), theme->id()); + + if (currentIconTheme == theme->id()) + m_ui->iconsComboBox->setCurrentIndex(i); + } + + const QString currentTheme = settings->get("ApplicationTheme").toString(); + auto themes = APPLICATION->themeManager()->getValidApplicationThemes(); + for (int i = 0; i < themes.count(); ++i) { + ITheme* theme = themes[i]; + + m_ui->widgetStyleComboBox->addItem(theme->name(), theme->id()); + + if (!theme->tooltip().isEmpty()) + m_ui->widgetStyleComboBox->setItemData(i, theme->tooltip(), Qt::ToolTipRole); + + if (currentTheme == theme->id()) + m_ui->widgetStyleComboBox->setCurrentIndex(i); + } + + if (!m_themesOnly) { + const QString currentCat = settings->get("BackgroundCat").toString(); + const auto cats = APPLICATION->themeManager()->getValidCatPacks(); + for (int i = 0; i < cats.count(); ++i) { + const CatPack* cat = cats[i]; + + QIcon catIcon = QIcon(QString("%1").arg(cat->path())); + m_ui->catPackComboBox->addItem(catIcon, cat->name(), cat->id()); + + if (currentCat == cat->id()) + m_ui->catPackComboBox->setCurrentIndex(i); + } + } + + m_ui->iconsComboBox->blockSignals(false); + m_ui->widgetStyleComboBox->blockSignals(false); + m_ui->catPackComboBox->blockSignals(false); +} + +void AppearanceWidget::updateConsolePreview() +{ + const LogColors& colors = APPLICATION->themeManager()->getLogColors(); + + int fontSize = m_ui->fontSizeBox->value(); + QString fontFamily = m_ui->consoleFont->currentFont().family(); + m_ui->consolePreview->clear(); + m_defaultFormat.setFont(QFont(fontFamily, fontSize)); + + auto print = [this, colors](const QString& message, MessageLevel::Enum level) { + QTextCharFormat format(m_defaultFormat); + + QColor bg = colors.background.value(level); + QColor fg = colors.foreground.value(level); + + if (bg.isValid()) + format.setBackground(bg); + + if (fg.isValid()) + format.setForeground(fg); + + // append a paragraph/line + auto workCursor = m_ui->consolePreview->textCursor(); + workCursor.movePosition(QTextCursor::End); + workCursor.insertText(message, format); + workCursor.insertBlock(); + }; + + print(QString("%1 version: %2\n") + .arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()), + MessageLevel::Launcher); + + QDate today = QDate::currentDate(); + + if (today.month() == 10 && today.day() == 31) + print(tr("[ERROR] OOoooOOOoooo! A spooky error!"), MessageLevel::Error); + else + print(tr("[ERROR] A spooky error!"), MessageLevel::Error); + + print(tr("[INFO] A harmless message..."), MessageLevel::Info); + print(tr("[WARN] A not so spooky warning."), MessageLevel::Warning); + print(tr("[DEBUG] A secret debugging message..."), MessageLevel::Debug); + print(tr("[FATAL] A terrifying fatal error!"), MessageLevel::Fatal); +} + +void AppearanceWidget::updateCatPreview() +{ + QIcon catPackIcon(APPLICATION->themeManager()->getCatPack()); + m_ui->catPreview->setIcon(catPackIcon); + + auto effect = dynamic_cast(m_ui->catPreview->graphicsEffect()); + if (effect) + effect->setOpacity(m_ui->catOpacitySlider->value() / 100.0); +} diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.h b/launcher/ui/widgets/AppearanceWidget.h similarity index 58% rename from launcher/ui/widgets/ThemeCustomizationWidget.h rename to launcher/ui/widgets/AppearanceWidget.h index 6977b8495..1fc89af3a 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.h +++ b/launcher/ui/widgets/AppearanceWidget.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher + * Copyright (C) 2025 TheKodeToad * Copyright (C) 2022 Tayou * * This program is free software: you can redistribute it and/or modify @@ -15,42 +16,47 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #pragma once -#include -#include "translations/TranslationsModel.h" +#include +#include -enum ThemeFields { NONE = 0b0000, ICONS = 0b0001, WIDGETS = 0b0010, CAT = 0b0100 }; +#include +#include +#include +#include "java/JavaChecker.h" +#include "ui/pages/BasePage.h" + +class QTextCharFormat; +class SettingsObject; namespace Ui { -class ThemeCustomizationWidget; +class AppearanceWidget; } -class ThemeCustomizationWidget : public QWidget { +class AppearanceWidget : public QWidget { Q_OBJECT public: - explicit ThemeCustomizationWidget(QWidget* parent = nullptr); - ~ThemeCustomizationWidget() override; - - void showFeatures(ThemeFields features); + explicit AppearanceWidget(bool simple, QWidget* parent = 0); + virtual ~AppearanceWidget(); + public: void applySettings(); - void loadSettings(); - void retranslate(); + void retranslateUi(); - private slots: + private: void applyIconTheme(int index); void applyWidgetTheme(int index); void applyCatTheme(int index); - void refresh(); + void loadThemeSettings(); - signals: - int currentIconThemeChanged(int index); - int currentWidgetThemeChanged(int index); - int currentCatChanged(int index); + void updateConsolePreview(); + void updateCatPreview(); - private: - Ui::ThemeCustomizationWidget* ui; + Ui::AppearanceWidget* m_ui; + QTextCharFormat m_defaultFormat; + bool m_themesOnly; }; diff --git a/launcher/ui/widgets/AppearanceWidget.ui b/launcher/ui/widgets/AppearanceWidget.ui new file mode 100644 index 000000000..c672279f0 --- /dev/null +++ b/launcher/ui/widgets/AppearanceWidget.ui @@ -0,0 +1,594 @@ + + + AppearanceWidget + + + + 0 + 0 + 600 + 700 + + + + + 300 + 0 + + + + + + + + + + false + + + + + + + + View cat packs folder. + + + Open Folder + + + + + + + View widget themes folder. + + + Open Folder + + + + + + + View icon themes folder. + + + Open Folder + + + + + + + &Cat Pack: + + + catPackComboBox + + + + + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + + 0 + 0 + + + + Reload All + + + + + + + Theme: + + + widgetStyleComboBox + + + + + + + &Icons: + + + iconsComboBox + + + + + + + + + + + + + + + + + + + + Console Font: + + + + + + + + + + + 0 + 0 + + + + 5 + + + 16 + + + 11 + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + + + Cat Opacity + + + + + + + + 300 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 300 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false + + + Opaque + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + false + + + Transparent + + + + + + + + 0 + 0 + + + + 100 + + + Qt::Horizontal + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + + + + + + Preview + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + + + + + 128 + 256 + + + + true + + + + + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::NoFocus + + + + + + + .. + + + true + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + + + + Qt::ScrollBarAsNeeded + + + false + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + + + widgetStyleComboBox + widgetStyleFolder + iconsComboBox + iconsFolder + catPackComboBox + catPackFolder + reloadThemesButton + consoleFont + fontSizeBox + catOpacitySlider + consolePreview + + + + diff --git a/launcher/ui/widgets/CustomCommands.cpp b/launcher/ui/widgets/CustomCommands.cpp index 9b98d7409..45bd0cbea 100644 --- a/launcher/ui/widgets/CustomCommands.cpp +++ b/launcher/ui/widgets/CustomCommands.cpp @@ -44,13 +44,14 @@ CustomCommands::~CustomCommands() CustomCommands::CustomCommands(QWidget* parent) : QWidget(parent), ui(new Ui::CustomCommands) { ui->setupUi(this); + connect(ui->overrideCheckBox, &QCheckBox::toggled, ui->customCommandsWidget, &QWidget::setEnabled); } void CustomCommands::initialize(bool checkable, bool checked, const QString& prelaunch, const QString& wrapper, const QString& postexit) { - ui->customCommandsGroupBox->setCheckable(checkable); + ui->overrideCheckBox->setVisible(checkable); if (checkable) { - ui->customCommandsGroupBox->setChecked(checked); + ui->overrideCheckBox->setChecked(checked); } ui->preLaunchCmdTextBox->setText(prelaunch); ui->wrapperCmdTextBox->setText(wrapper); @@ -64,9 +65,9 @@ void CustomCommands::retranslate() bool CustomCommands::checked() const { - if (!ui->customCommandsGroupBox->isCheckable()) + if (!ui->overrideCheckBox->isVisible()) return true; - return ui->customCommandsGroupBox->isChecked(); + return ui->overrideCheckBox->isChecked(); } QString CustomCommands::prelaunchCommand() const diff --git a/launcher/ui/widgets/CustomCommands.ui b/launcher/ui/widgets/CustomCommands.ui index b485c293e..c1b4558a8 100644 --- a/launcher/ui/widgets/CustomCommands.ui +++ b/launcher/ui/widgets/CustomCommands.ui @@ -24,58 +24,100 @@ 0 - + + + Override &Global Settings + + + true + + + + + true - - &Custom Commands - - - true - - - false - + + 0 + + + 0 + + + 0 + - &Pre-launch command: + &Pre-launch Command preLaunchCmdTextBox - - + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + + + + + + + P&ost-exit Command + + + labelPostExitCmd + + + + + + + - &Wrapper command: + &Wrapper Command wrapperCmdTextBox - - - - - - P&ost-exit command: + + + Qt::Vertical - - postExitCmdTextBox + + QSizePolicy::Fixed - - - - + + + 0 + 6 + + + diff --git a/launcher/ui/widgets/EnvironmentVariables.cpp b/launcher/ui/widgets/EnvironmentVariables.cpp index 633fc6122..653f0d23d 100644 --- a/launcher/ui/widgets/EnvironmentVariables.cpp +++ b/launcher/ui/widgets/EnvironmentVariables.cpp @@ -50,6 +50,8 @@ EnvironmentVariables::EnvironmentVariables(QWidget* parent) : QWidget(parent), u }); connect(ui->clear, &QPushButton::clicked, this, [this] { ui->list->clear(); }); + + connect(ui->overrideCheckBox, &QCheckBox::toggled, ui->settingsWidget, &QWidget::setEnabled); } EnvironmentVariables::~EnvironmentVariables() @@ -60,8 +62,8 @@ EnvironmentVariables::~EnvironmentVariables() void EnvironmentVariables::initialize(bool instance, bool override, const QMap& value) { // update widgets to settings - ui->groupBox->setCheckable(instance); - ui->groupBox->setChecked(override); + ui->overrideCheckBox->setVisible(instance); + ui->overrideCheckBox->setChecked(override); // populate ui->list->clear(); @@ -94,9 +96,9 @@ void EnvironmentVariables::retranslate() bool EnvironmentVariables::override() const { - if (!ui->groupBox->isCheckable()) + if (!ui->overrideCheckBox->isVisible()) return false; - return ui->groupBox->isChecked(); + return ui->overrideCheckBox->isChecked(); } QMap EnvironmentVariables::value() const diff --git a/launcher/ui/widgets/EnvironmentVariables.ui b/launcher/ui/widgets/EnvironmentVariables.ui index ded5b2ded..cc52b5d10 100644 --- a/launcher/ui/widgets/EnvironmentVariables.ui +++ b/launcher/ui/widgets/EnvironmentVariables.ui @@ -14,27 +14,72 @@ Form - - 0 - - - 0 - - - 0 - - - 0 - - - - &Environment Variables + + + Override &Global Settings - + true - + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + &Add + + + + + + + &Remove + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Clear + + + + + @@ -67,44 +112,6 @@ - - - - - - &Add - - - - - - - &Remove - - - - - - - &Clear - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index a255168e9..f5435d16f 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -289,26 +289,18 @@ void JavaSettingsWidget::updateThresholds() unsigned int maxMem = m_ui->maxMemSpinBox->value(); unsigned int minMem = m_ui->minMemSpinBox->value(); - QString iconName; + const QString warningColour(QStringLiteral("%1")); if (maxMem >= sysMiB) { - iconName = "status-bad"; - m_ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity.")); + m_ui->labelMaxMemNotice->setText(QString("%1").arg(tr("Your maximum memory allocation exceeds your system memory capacity."))); + m_ui->labelMaxMemNotice->show(); } else if (maxMem > (sysMiB * 0.9)) { - iconName = "status-yellow"; - m_ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); + m_ui->labelMaxMemNotice->setText(warningColour.arg(tr("Your maximum memory allocation is close to your system memory capacity."))); + m_ui->labelMaxMemNotice->show(); } else if (maxMem < minMem) { - iconName = "status-yellow"; - m_ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value")); + m_ui->labelMaxMemNotice->setText(warningColour.arg(tr("Your maximum memory allocation is below the minimum memory allocation."))); + m_ui->labelMaxMemNotice->show(); } else { - iconName = "status-good"; - m_ui->labelMaxMemIcon->setToolTip(""); - } - - { - auto height = m_ui->labelMaxMemIcon->fontInfo().pixelSize(); - QIcon icon = APPLICATION->getThemedIcon(iconName); - QPixmap pix = icon.pixmap(height, height); - m_ui->labelMaxMemIcon->setPixmap(pix); + m_ui->labelMaxMemNotice->hide(); } } diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index 21a71fb8b..85266bc2b 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -65,4 +65,4 @@ class JavaSettingsWidget : public QWidget { InstancePtr m_instance; Ui::JavaSettingsWidget* m_ui; unique_qobject_ptr m_checker; -}; +}; \ No newline at end of file diff --git a/launcher/ui/widgets/JavaSettingsWidget.ui b/launcher/ui/widgets/JavaSettingsWidget.ui index 15ce88f0c..fb974570f 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.ui +++ b/launcher/ui/widgets/JavaSettingsWidget.ui @@ -7,13 +7,13 @@ 0 0 500 - 600 + 1000 Form - + @@ -29,53 +29,92 @@ false - + Auto-&detect Java version - - + + - + + + &Detect + + - Browse + &Browse + + + + Qt::Horizontal + + + + 40 + 20 + + + + - + - - - - Download Java - - - - - - - Auto-detect... - - - - Test + Test S&ettings + + + + Open Java &Downloader + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + - + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + Automatically downloads and selects the Java build recommended by Mojang. @@ -86,6 +125,29 @@ + + + If enabled, the launcher will not check if an instance is compatible with the selected Java version. + + + Skip Java compatibility checks + + + + + + + + + + Java &Executable + + + javaPathTextBox + + + + If enabled, the launcher won't prompt you to choose a Java version if one is not found on startup. @@ -96,14 +158,20 @@ - - - If enabled, the launcher will not check if an instance is compatible with the selected Java version. + + + Qt::Vertical - - Skip Java compatibility checks + + QSizePolicy::Fixed - + + + 0 + 6 + + + @@ -122,23 +190,29 @@ false - - - + + + - PermGen (Java 7 and earlier): + Memory Notice - - + + - Minimum memory allocation: + (-XX:PermSize) + + + 0 + 0 + + The amount of memory available to store loaded Java classes. @@ -149,7 +223,7 @@ 4 - 999999999 + 1048576 8 @@ -159,28 +233,14 @@ - - - - Maximum memory allocation: - - - - - - - - - - Qt::AlignCenter - - - maxMemSpinBox - - - + + + 0 + 0 + + The maximum amount of memory Minecraft is allowed to use. @@ -201,8 +261,21 @@ + + + + (-Xmx) + + + + + + 0 + 0 + + The amount of memory Minecraft is started with. @@ -223,6 +296,56 @@ + + + + &PermGen Size: + + + permGenSpinBox + + + + + + + (-Xms) + + + + + + + Ma&ximum Memory Usage: + + + maxMemSpinBox + + + + + + + M&inimum Memory Usage: + + + minMemSpinBox + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + @@ -251,14 +374,14 @@ javaPathTextBox - javaBrowseBtn - javaDownloadBtn javaDetectBtn - javaTestBtn + javaBrowseBtn skipCompatibilityCheckBox skipWizardCheckBox autodetectJavaCheckBox autodownloadJavaCheckBox + javaTestBtn + javaDownloadBtn minMemSpinBox maxMemSpinBox permGenSpinBox diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.cpp b/launcher/ui/widgets/MinecraftSettingsWidget.cpp index df7ed083b..5eceb0b76 100644 --- a/launcher/ui/widgets/MinecraftSettingsWidget.cpp +++ b/launcher/ui/widgets/MinecraftSettingsWidget.cpp @@ -41,6 +41,8 @@ #include #include "Application.h" #include "BuildConfig.h" +#include "Json.h" +#include "minecraft/PackProfile.h" #include "minecraft/WorldList.h" #include "minecraft/auth/AccountList.h" #include "settings/Setting.h" @@ -51,16 +53,13 @@ MinecraftSettingsWidget::MinecraftSettingsWidget(MinecraftInstancePtr instance, m_ui->setupUi(this); if (m_instance == nullptr) { - for (int i = m_ui->settingsTabs->count() - 1; i >= 0; --i) { - const QString name = m_ui->settingsTabs->widget(i)->objectName(); - - if (name == "javaPage" || name == "launchPage") - m_ui->settingsTabs->removeTab(i); - } + m_ui->settingsTabs->removeTab(1); m_ui->openGlobalSettingsButton->setVisible(false); - + m_ui->instanceAccountGroupBox->hide(); + m_ui->serverJoinGroupBox->hide(); m_ui->globalDataPacksGroupBox->hide(); + m_ui->loaderGroup->hide(); } else { m_javaSettings = new JavaSettingsWidget(m_instance, this); m_ui->javaScrollArea->setWidget(m_javaSettings); @@ -74,7 +73,6 @@ MinecraftSettingsWidget::MinecraftSettingsWidget(MinecraftInstancePtr instance, tr("Warning: The maximized option is " "not fully supported on this Minecraft version.")); - m_ui->miscellaneousSettingsBox->setCheckable(true); m_ui->consoleSettingsBox->setCheckable(true); m_ui->windowSizeGroupBox->setCheckable(true); m_ui->nativeWorkaroundsGroupBox->setCheckable(true); @@ -108,6 +106,17 @@ MinecraftSettingsWidget::MinecraftSettingsWidget(MinecraftInstancePtr instance, }); connect(m_ui->dataPacksPathEdit, &QLineEdit::editingFinished, this, &MinecraftSettingsWidget::editedDataPacksPath); connect(m_ui->dataPacksPathBrowse, &QPushButton::clicked, this, &MinecraftSettingsWidget::selectDataPacksFolder); + + connect(m_ui->loaderGroup, &QGroupBox::toggled, this, [this](bool value) { + m_instance->settings()->set("OverrideModDownloadLoaders", value); + if (!value) + m_instance->settings()->reset("ModDownloadLoaders"); + }); + connect(m_ui->neoForge, &QCheckBox::stateChanged, this, &MinecraftSettingsWidget::selectedLoadersChanged); + connect(m_ui->forge, &QCheckBox::stateChanged, this, &MinecraftSettingsWidget::selectedLoadersChanged); + connect(m_ui->fabric, &QCheckBox::stateChanged, this, &MinecraftSettingsWidget::selectedLoadersChanged); + connect(m_ui->quilt, &QCheckBox::stateChanged, this, &MinecraftSettingsWidget::selectedLoadersChanged); + connect(m_ui->liteLoader, &QCheckBox::stateChanged, this, &MinecraftSettingsWidget::selectedLoadersChanged); } m_ui->maximizedWarning->hide(); @@ -150,11 +159,13 @@ void MinecraftSettingsWidget::loadSettings() settings = APPLICATION->settings(); // Game Window - m_ui->windowSizeGroupBox->setChecked(m_instance == nullptr || settings->get("OverrideWindow").toBool()); - m_ui->windowSizeGroupBox->setChecked(settings->get("OverrideWindow").toBool()); + m_ui->windowSizeGroupBox->setChecked(m_instance == nullptr || settings->get("OverrideWindow").toBool() || + settings->get("OverrideMiscellaneous").toBool()); m_ui->maximizedCheckBox->setChecked(settings->get("LaunchMaximized").toBool()); m_ui->windowWidthSpinBox->setValue(settings->get("MinecraftWinWidth").toInt()); m_ui->windowHeightSpinBox->setValue(settings->get("MinecraftWinHeight").toInt()); + m_ui->closeAfterLaunchCheck->setChecked(settings->get("CloseAfterLaunch").toBool()); + m_ui->quitAfterGameStopCheck->setChecked(settings->get("QuitAfterGameStop").toBool()); // Game Time m_ui->gameTimeGroupBox->setChecked(m_instance == nullptr || settings->get("OverrideGameTime").toBool()); @@ -169,11 +180,6 @@ void MinecraftSettingsWidget::loadSettings() m_ui->autoCloseConsoleCheck->setChecked(settings->get("AutoCloseConsole").toBool()); m_ui->showConsoleErrorCheck->setChecked(settings->get("ShowConsoleOnError").toBool()); - // Miscellaneous - m_ui->miscellaneousSettingsBox->setChecked(settings->get("OverrideMiscellaneous").toBool()); - m_ui->closeAfterLaunchCheck->setChecked(settings->get("CloseAfterLaunch").toBool()); - m_ui->quitAfterGameStopCheck->setChecked(settings->get("QuitAfterGameStop").toBool()); - if (m_javaSettings != nullptr) m_javaSettings->loadSettings(); @@ -238,6 +244,35 @@ void MinecraftSettingsWidget::loadSettings() m_ui->instanceAccountGroupBox->setChecked(settings->get("UseAccountForInstance").toBool()); updateAccountsMenu(*settings); + + m_ui->loaderGroup->blockSignals(true); + m_ui->neoForge->blockSignals(true); + m_ui->forge->blockSignals(true); + m_ui->fabric->blockSignals(true); + m_ui->quilt->blockSignals(true); + m_ui->liteLoader->blockSignals(true); + auto instLoaders = m_instance->getPackProfile()->getSupportedModLoaders().value(); + m_ui->loaderGroup->setChecked(settings->get("OverrideModDownloadLoaders").toBool()); + auto loaders = Json::toStringList(settings->get("ModDownloadLoaders").toString()); + if (loaders.isEmpty()) { + m_ui->neoForge->setChecked(instLoaders & ModPlatform::NeoForge); + m_ui->forge->setChecked(instLoaders & ModPlatform::Forge); + m_ui->fabric->setChecked(instLoaders & ModPlatform::Fabric); + m_ui->quilt->setChecked(instLoaders & ModPlatform::Quilt); + m_ui->liteLoader->setChecked(instLoaders & ModPlatform::LiteLoader); + } else { + m_ui->neoForge->setChecked(loaders.contains(getModLoaderAsString(ModPlatform::NeoForge))); + m_ui->forge->setChecked(loaders.contains(getModLoaderAsString(ModPlatform::Forge))); + m_ui->fabric->setChecked(loaders.contains(getModLoaderAsString(ModPlatform::Fabric))); + m_ui->quilt->setChecked(loaders.contains(getModLoaderAsString(ModPlatform::Quilt))); + m_ui->liteLoader->setChecked(loaders.contains(getModLoaderAsString(ModPlatform::LiteLoader))); + } + m_ui->loaderGroup->blockSignals(false); + m_ui->neoForge->blockSignals(false); + m_ui->forge->blockSignals(false); + m_ui->fabric->blockSignals(false); + m_ui->quilt->blockSignals(false); + m_ui->liteLoader->blockSignals(false); } m_ui->legacySettingsGroupBox->setChecked(settings->get("OverrideLegacySettings").toBool()); @@ -263,20 +298,6 @@ void MinecraftSettingsWidget::saveSettings() { SettingsObject::Lock lock(settings); - // Miscellaneous - bool miscellaneous = m_instance == nullptr || m_ui->miscellaneousSettingsBox->isChecked(); - - if (m_instance != nullptr) - settings->set("OverrideMiscellaneous", miscellaneous); - - if (miscellaneous) { - settings->set("CloseAfterLaunch", m_ui->closeAfterLaunchCheck->isChecked()); - settings->set("QuitAfterGameStop", m_ui->quitAfterGameStopCheck->isChecked()); - } else { - settings->reset("CloseAfterLaunch"); - settings->reset("QuitAfterGameStop"); - } - // Console bool console = m_instance == nullptr || m_ui->consoleSettingsBox->isChecked(); @@ -293,20 +314,26 @@ void MinecraftSettingsWidget::saveSettings() settings->reset("ShowConsoleOnError"); } - // Window Size + // Game Window bool window = m_instance == nullptr || m_ui->windowSizeGroupBox->isChecked(); - if (m_instance != nullptr) + if (m_instance != nullptr) { settings->set("OverrideWindow", window); + settings->set("OverrideMiscellaneous", window); + } if (window) { settings->set("LaunchMaximized", m_ui->maximizedCheckBox->isChecked()); settings->set("MinecraftWinWidth", m_ui->windowWidthSpinBox->value()); settings->set("MinecraftWinHeight", m_ui->windowHeightSpinBox->value()); + settings->set("CloseAfterLaunch", m_ui->closeAfterLaunchCheck->isChecked()); + settings->set("QuitAfterGameStop", m_ui->quitAfterGameStopCheck->isChecked()); } else { settings->reset("LaunchMaximized"); settings->reset("MinecraftWinWidth"); settings->reset("MinecraftWinHeight"); + settings->reset("CloseAfterLaunch"); + settings->reset("QuitAfterGameStop"); } // Custom Commands @@ -504,3 +531,19 @@ void MinecraftSettingsWidget::selectDataPacksFolder() m_ui->dataPacksPathEdit->setText(path); m_instance->settings()->set("GlobalDataPacksPath", path); } + +void MinecraftSettingsWidget::selectedLoadersChanged() +{ + QStringList loaders; + if (m_ui->neoForge->isChecked()) + loaders << getModLoaderAsString(ModPlatform::NeoForge); + if (m_ui->forge->isChecked()) + loaders << getModLoaderAsString(ModPlatform::Forge); + if (m_ui->fabric->isChecked()) + loaders << getModLoaderAsString(ModPlatform::Fabric); + if (m_ui->quilt->isChecked()) + loaders << getModLoaderAsString(ModPlatform::Quilt); + if (m_ui->liteLoader->isChecked()) + loaders << getModLoaderAsString(ModPlatform::LiteLoader); + m_instance->settings()->set("ModDownloadLoaders", Json::fromStringList(loaders)); +} diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.h b/launcher/ui/widgets/MinecraftSettingsWidget.h index 002cd2d56..1481d0fae 100644 --- a/launcher/ui/widgets/MinecraftSettingsWidget.h +++ b/launcher/ui/widgets/MinecraftSettingsWidget.h @@ -56,6 +56,8 @@ class MinecraftSettingsWidget : public QWidget { void openGlobalSettings(); void updateAccountsMenu(const SettingsObject& settings); bool isQuickPlaySupported(); + private slots: + void selectedLoadersChanged(); void editedDataPacksPath(); void selectDataPacksFolder(); diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.ui b/launcher/ui/widgets/MinecraftSettingsWidget.ui index 61d90255f..4a35addc0 100644 --- a/launcher/ui/widgets/MinecraftSettingsWidget.ui +++ b/launcher/ui/widgets/MinecraftSettingsWidget.ui @@ -7,7 +7,7 @@ 0 0 648 - 400 + 600 @@ -51,9 +51,6 @@ 0 - - Qt::ScrollBarAlwaysOff - true @@ -61,9 +58,9 @@ 0 - -166 - 610 - 768 + 0 + 603 + 1042 @@ -81,15 +78,8 @@ false - - - - - Start Minecraft maximized - - - - + + The base game only supports resolution. In order to simulate the maximized behaviour the current implementation approximates the maximum display size. @@ -99,52 +89,165 @@ + + + + When the game window closes, quit the launcher + + + + + + + Start Minecraft maximized + + + + + + + When the game window opens, hide the launcher + + + + + + + + 0 + 0 + + + + + + + 1 + + + 65536 + + + 1 + + + 854 + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 6 + + + + + + + + + 0 + 0 + + + + + + + 1 + + + 65536 + + + 480 + + + + + + + &Window Size: + + + windowWidthSpinBox + + + + + + + × + + + + + + + pixels + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + true + + + &Console Window + + + false + + + false + + - - - - - Window height: - - - - - - - Window width: - - - - - - - 1 - - - 65536 - - - 1 - - - 854 - - - - - - - 1 - - - 65536 - - - 480 - - - - + + + When the game is launched, show the console window + + + + + + + When the game crashes, show the console window + + + + + + + When the game quits, hide the console window + + @@ -162,7 +265,7 @@ - + Allows installing data packs across all worlds if an applicable mod is installed. It is most likely you will need to change the path - please refer to the mod's website. @@ -186,14 +289,14 @@ It is most likely you will need to change the path - please refer to the mod's w - + Folder Path - + @@ -260,67 +363,156 @@ It is most likely you will need to change the path - please refer to the mod's w - - - true - + - &Console + Override &Default Account - false + true false - + - + - Show console while the game is running + Account: - - - Automatically close console when the game quits + + + + 0 + 0 + - - - Show console when the game crashes + + + Qt::Horizontal - + + + 0 + 0 + + + - + - &Miscellaneous + Enable Auto-&join - false + true false - - - + + + + + + 0 + 0 + + + + + + - Close the launcher after game window opens + Singleplayer world: + + + + + + + Server address: + + + + + + + + 200 + 16777215 + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + Override Mod Download &Loaders + + + true + + + false + + + + + + NeoForge - + - Quit the launcher after game window closes + Forge + + + + + + + Fabric + + + + + + + Quilt + + + + + + + LiteLoader @@ -362,7 +554,7 @@ It is most likely you will need to change the path - please refer to the mod's w 0 0 624 - 291 + 487 @@ -377,9 +569,6 @@ It is most likely you will need to change the path - please refer to the mod's w - - Qt::ScrollBarAlwaysOff - true @@ -388,8 +577,8 @@ It is most likely you will need to change the path - please refer to the mod's w 0 0 - 610 - 439 + 624 + 487 @@ -432,49 +621,68 @@ It is most likely you will need to change the path - please refer to the mod's w false - + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + - - - Use system installation of OpenAL - - - - - &GLFW library path + &GLFW library path: lineEditGLFWPath - - - - Use system installation of GLFW + + + + Qt::Vertical - - - - - - false + + QSizePolicy::Fixed - + + + 0 + 6 + + + - + - &OpenAL library path + &OpenAL library path: lineEditOpenALPath - + + + + false + + + + + + + Use system installation of GLFW + + + + + + + Use system installation of OpenAL + + + + false @@ -561,115 +769,6 @@ It is most likely you will need to change the path - please refer to the mod's w - - - Launch - - - - - - true - - - - - 0 - 0 - 624 - 291 - - - - - - - Override default &account - - - true - - - false - - - - - - - - - 0 - 0 - - - - Account: - - - - - - - - - - - - - - - Set a &target to join on launch - - - true - - - false - - - - - - Server address: - - - - - - - - - - Singleplayer world - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Custom Commands @@ -685,6 +784,18 @@ It is most likely you will need to change the path - please refer to the mod's w Environment Variables + + 0 + + + 0 + + + 0 + + + 0 + @@ -711,31 +822,19 @@ It is most likely you will need to change the path - please refer to the mod's w openGlobalSettingsButton settingsTabs + scrollArea maximizedCheckBox - windowWidthSpinBox windowHeightSpinBox + windowWidthSpinBox + closeAfterLaunchCheck + quitAfterGameStopCheck + showConsoleCheck + showConsoleErrorCheck + autoCloseConsoleCheck showGameTime recordGameTime showGlobalGameTime showGameTimeWithoutDays - showConsoleCheck - autoCloseConsoleCheck - showConsoleErrorCheck - closeAfterLaunchCheck - quitAfterGameStopCheck - javaScrollArea - scrollArea_2 - onlineFixes - useNativeGLFWCheck - lineEditGLFWPath - useNativeOpenALCheck - lineEditOpenALPath - perfomanceGroupBox - enableFeralGamemodeCheck - enableMangoHud - useDiscreteGpuCheck - useZink - scrollArea_3 instanceAccountGroupBox instanceAccountSelector serverJoinGroupBox @@ -743,6 +842,17 @@ It is most likely you will need to change the path - please refer to the mod's w serverJoinAddress worldJoinButton worldsCb + javaScrollArea + scrollArea_2 + onlineFixes + useNativeGLFWCheck + lineEditGLFWPath + useNativeOpenALCheck + lineEditOpenALPath + enableFeralGamemodeCheck + enableMangoHud + useDiscreteGpuCheck + useZink diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index da41b990a..031ff0f94 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -40,6 +40,7 @@ #include #include #include "BaseVersionList.h" +#include "Json.h" #include "Version.h" #include "meta/Index.h" #include "modplatform/ModIndex.h" @@ -218,7 +219,14 @@ void ModFilterWidget::prepareBasicFilter() if (m_instance) { m_filter->hideInstalled = false; m_filter->side = ""; // or "both" - auto loaders = m_instance->getPackProfile()->getSupportedModLoaders().value(); + ModPlatform::ModLoaderTypes loaders; + if (m_instance->settings()->get("OverrideModDownloadLoaders").toBool()) { + for (auto loader : Json::toStringList(m_instance->settings()->get("ModDownloadLoaders").toString())) { + loaders |= ModPlatform::getModLoaderFromString(loader); + } + } else { + loaders = m_instance->getPackProfile()->getSupportedModLoaders().value(); + } ui->neoForge->setChecked(loaders & ModPlatform::NeoForge); ui->forge->setChecked(loaders & ModPlatform::Forge); ui->fabric->setChecked(loaders & ModPlatform::Fabric); diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp deleted file mode 100644 index 097678b8d..000000000 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2024 Tayou - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include "ThemeCustomizationWidget.h" -#include "ui_ThemeCustomizationWidget.h" - -#include "Application.h" -#include "DesktopServices.h" -#include "ui/themes/ITheme.h" -#include "ui/themes/ThemeManager.h" - -ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget* parent) : QWidget(parent), ui(new Ui::ThemeCustomizationWidget) -{ - ui->setupUi(this); - loadSettings(); - ThemeCustomizationWidget::refresh(); - - connect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); - connect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, - &ThemeCustomizationWidget::applyWidgetTheme); - connect(ui->backgroundCatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyCatTheme); - - connect(ui->iconsFolder, &QPushButton::clicked, this, - [] { DesktopServices::openPath(APPLICATION->themeManager()->getIconThemesFolder().path()); }); - connect(ui->widgetStyleFolder, &QPushButton::clicked, this, - [] { DesktopServices::openPath(APPLICATION->themeManager()->getApplicationThemesFolder().path()); }); - connect(ui->catPackFolder, &QPushButton::clicked, this, - [] { DesktopServices::openPath(APPLICATION->themeManager()->getCatPacksFolder().path()); }); - - connect(ui->refreshButton, &QPushButton::clicked, this, &ThemeCustomizationWidget::refresh); -} - -ThemeCustomizationWidget::~ThemeCustomizationWidget() -{ - delete ui; -} - -/// -/// The layout was not quite right, so currently this just disables the UI elements, which should be hidden instead -/// TODO FIXME -/// -/// Original Method One: -/// ui->iconsComboBox->setVisible(features& ThemeFields::ICONS); -/// ui->iconsLabel->setVisible(features& ThemeFields::ICONS); -/// ui->widgetStyleComboBox->setVisible(features& ThemeFields::WIDGETS); -/// ui->widgetThemeLabel->setVisible(features& ThemeFields::WIDGETS); -/// ui->backgroundCatComboBox->setVisible(features& ThemeFields::CAT); -/// ui->backgroundCatLabel->setVisible(features& ThemeFields::CAT); -/// -/// original Method Two: -/// if (!(features & ThemeFields::ICONS)) { -/// ui->formLayout->setRowVisible(0, false); -/// } -/// if (!(features & ThemeFields::WIDGETS)) { -/// ui->formLayout->setRowVisible(1, false); -/// } -/// if (!(features & ThemeFields::CAT)) { -/// ui->formLayout->setRowVisible(2, false); -/// } -/// -/// -void ThemeCustomizationWidget::showFeatures(ThemeFields features) -{ - ui->iconsComboBox->setEnabled(features & ThemeFields::ICONS); - ui->iconsLabel->setEnabled(features & ThemeFields::ICONS); - ui->widgetStyleComboBox->setEnabled(features & ThemeFields::WIDGETS); - ui->widgetStyleLabel->setEnabled(features & ThemeFields::WIDGETS); - ui->backgroundCatComboBox->setEnabled(features & ThemeFields::CAT); - ui->backgroundCatLabel->setEnabled(features & ThemeFields::CAT); -} - -void ThemeCustomizationWidget::applyIconTheme(int index) -{ - auto settings = APPLICATION->settings(); - auto originalIconTheme = settings->get("IconTheme").toString(); - auto newIconTheme = ui->iconsComboBox->itemData(index).toString(); - if (originalIconTheme != newIconTheme) { - settings->set("IconTheme", newIconTheme); - APPLICATION->themeManager()->applyCurrentlySelectedTheme(); - } - - emit currentIconThemeChanged(index); -} - -void ThemeCustomizationWidget::applyWidgetTheme(int index) -{ - auto settings = APPLICATION->settings(); - auto originalAppTheme = settings->get("ApplicationTheme").toString(); - auto newAppTheme = ui->widgetStyleComboBox->itemData(index).toString(); - if (originalAppTheme != newAppTheme) { - settings->set("ApplicationTheme", newAppTheme); - APPLICATION->themeManager()->applyCurrentlySelectedTheme(); - } - - emit currentWidgetThemeChanged(index); -} - -void ThemeCustomizationWidget::applyCatTheme(int index) -{ - auto settings = APPLICATION->settings(); - auto originalCat = settings->get("BackgroundCat").toString(); - auto newCat = ui->backgroundCatComboBox->itemData(index).toString(); - if (originalCat != newCat) { - settings->set("BackgroundCat", newCat); - } - - emit currentCatChanged(index); -} - -void ThemeCustomizationWidget::applySettings() -{ - applyIconTheme(ui->iconsComboBox->currentIndex()); - applyWidgetTheme(ui->widgetStyleComboBox->currentIndex()); - applyCatTheme(ui->backgroundCatComboBox->currentIndex()); -} -void ThemeCustomizationWidget::loadSettings() -{ - auto settings = APPLICATION->settings(); - - { - auto currentIconTheme = settings->get("IconTheme").toString(); - auto iconThemes = APPLICATION->themeManager()->getValidIconThemes(); - int idx = 0; - for (auto iconTheme : iconThemes) { - QIcon iconForComboBox = QIcon(iconTheme->path() + "/scalable/settings"); - ui->iconsComboBox->addItem(iconForComboBox, iconTheme->name(), iconTheme->id()); - if (currentIconTheme == iconTheme->id()) { - ui->iconsComboBox->setCurrentIndex(idx); - } - idx++; - } - } - - { - auto currentTheme = settings->get("ApplicationTheme").toString(); - auto themes = APPLICATION->themeManager()->getValidApplicationThemes(); - int idx = 0; - for (auto& theme : themes) { - ui->widgetStyleComboBox->addItem(theme->name(), theme->id()); - if (theme->tooltip() != "") { - int index = ui->widgetStyleComboBox->count() - 1; - ui->widgetStyleComboBox->setItemData(index, theme->tooltip(), Qt::ToolTipRole); - } - if (currentTheme == theme->id()) { - ui->widgetStyleComboBox->setCurrentIndex(idx); - } - idx++; - } - } - - auto cat = settings->get("BackgroundCat").toString(); - for (auto& catFromList : APPLICATION->themeManager()->getValidCatPacks()) { - QIcon catIcon = QIcon(QString("%1").arg(catFromList->path())); - ui->backgroundCatComboBox->addItem(catIcon, catFromList->name(), catFromList->id()); - if (cat == catFromList->id()) { - ui->backgroundCatComboBox->setCurrentIndex(ui->backgroundCatComboBox->count() - 1); - } - } -} - -void ThemeCustomizationWidget::retranslate() -{ - ui->retranslateUi(this); -} - -void ThemeCustomizationWidget::refresh() -{ - applySettings(); - disconnect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); - disconnect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, - &ThemeCustomizationWidget::applyWidgetTheme); - disconnect(ui->backgroundCatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, - &ThemeCustomizationWidget::applyCatTheme); - APPLICATION->themeManager()->refresh(); - ui->iconsComboBox->clear(); - ui->widgetStyleComboBox->clear(); - ui->backgroundCatComboBox->clear(); - loadSettings(); - connect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); - connect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, - &ThemeCustomizationWidget::applyWidgetTheme); - connect(ui->backgroundCatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyCatTheme); -}; \ No newline at end of file diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.ui b/launcher/ui/widgets/ThemeCustomizationWidget.ui deleted file mode 100644 index 1faa45c4f..000000000 --- a/launcher/ui/widgets/ThemeCustomizationWidget.ui +++ /dev/null @@ -1,212 +0,0 @@ - - - ThemeCustomizationWidget - - - - 0 - 0 - 400 - 191 - - - - Form - - - - QLayout::SetMinimumSize - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - &Icons - - - iconsComboBox - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - - - - - View icon themes folder. - - - - - - - - - true - - - - - - - - - &Widgets - - - widgetStyleComboBox - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - - - - - View widget themes folder. - - - - - - - - - true - - - - - - - - - The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. - - - C&at - - - backgroundCatComboBox - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. - - - - - - - View cat packs folder. - - - - - - - - - true - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Refresh all - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - -