diff options
Diffstat (limited to 'launcher')
-rw-r--r-- | launcher/Application.cpp | 51 | ||||
-rw-r--r-- | launcher/Application.h | 13 | ||||
-rw-r--r-- | launcher/CMakeLists.txt | 2 | ||||
-rw-r--r-- | launcher/DesktopServices.cpp | 2 | ||||
-rw-r--r-- | launcher/resources/multimc/index.theme | 2 | ||||
-rw-r--r-- | launcher/resources/pe_blue/index.theme | 2 | ||||
-rw-r--r-- | launcher/resources/pe_colored/index.theme | 2 | ||||
-rw-r--r-- | launcher/resources/pe_dark/index.theme | 2 | ||||
-rw-r--r-- | launcher/resources/pe_light/index.theme | 2 | ||||
-rw-r--r-- | launcher/ui/MainWindow.cpp | 32 | ||||
-rw-r--r-- | launcher/ui/MainWindow.h | 9 | ||||
-rw-r--r-- | launcher/ui/MainWindow.ui | 57 | ||||
-rw-r--r-- | launcher/ui/instanceview/InstanceView.cpp | 4 | ||||
-rw-r--r-- | launcher/ui/setupwizard/ThemeWizardPage.cpp | 2 | ||||
-rw-r--r-- | launcher/ui/themes/IconTheme.cpp | 53 | ||||
-rw-r--r-- | launcher/ui/themes/IconTheme.h | 37 | ||||
-rw-r--r-- | launcher/ui/themes/ThemeManager.cpp | 192 | ||||
-rw-r--r-- | launcher/ui/themes/ThemeManager.h | 23 | ||||
-rw-r--r-- | launcher/ui/widgets/ThemeCustomizationWidget.cpp | 55 | ||||
-rw-r--r-- | launcher/ui/widgets/ThemeCustomizationWidget.h | 16 | ||||
-rw-r--r-- | launcher/ui/widgets/ThemeCustomizationWidget.ui | 98 |
21 files changed, 461 insertions, 195 deletions
diff --git a/launcher/Application.cpp b/launcher/Application.cpp index aeea90f1..acac2da2 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -526,7 +526,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this)); // Theming - m_settings->registerSetting("IconTheme", QString("pe_colored")); + m_settings->registerSetting("IconTheme", QString()); m_settings->registerSetting("ApplicationTheme", QString()); m_settings->registerSetting("BackgroundCat", QString("kitteh")); @@ -801,7 +801,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } // Themes - m_themeManager = std::make_unique<ThemeManager>(m_mainWindow); + m_themeManager = std::make_unique<ThemeManager>(); // initialize and load all instances { @@ -893,8 +893,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } }); - applyCurrentlySelectedTheme(true); - updateCapabilities(); if(createSetupWizard()) @@ -902,6 +900,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) return; } + m_themeManager->applyCurrentlySelectedTheme(true); performMainStartupAction(); } @@ -930,11 +929,21 @@ bool Application::createSetupWizard() }(); bool languageRequired = settings()->get("Language").toString().isEmpty(); bool pasteInterventionRequired = settings()->get("PastebinURL") != ""; - bool themeInterventionRequired = settings()->get("ApplicationTheme") == ""; + bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString()); + bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString()); + bool themeInterventionRequired = !validWidgets || !validIcons; bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired; if(wizardRequired) { + // set default theme after going into theme wizard + if (!validIcons) + settings()->set("IconTheme", QString("pe_colored")); + if (!validWidgets) + settings()->set("ApplicationTheme", QString("system")); + + m_themeManager->applyCurrentlySelectedTheme(true); + m_setupWizard = new SetupWizard(nullptr); if (languageRequired) { @@ -953,9 +962,9 @@ bool Application::createSetupWizard() if (themeInterventionRequired) { - settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard)); } + connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished); m_setupWizard->show(); return true; @@ -1158,26 +1167,6 @@ std::shared_ptr<JavaInstallList> Application::javalist() return m_javalist; } -QList<ITheme*> Application::getValidApplicationThemes() -{ - return m_themeManager->getValidApplicationThemes(); -} - -void Application::applyCurrentlySelectedTheme(bool initial) -{ - m_themeManager->applyCurrentlySelectedTheme(initial); -} - -void Application::setApplicationTheme(const QString& name) -{ - m_themeManager->setApplicationTheme(name); -} - -void Application::setIconTheme(const QString& name) -{ - m_themeManager->setIconTheme(name); -} - QIcon Application::getThemedIcon(const QString& name) { if(name == "logo") { @@ -1186,16 +1175,6 @@ QIcon Application::getThemedIcon(const QString& name) return QIcon::fromTheme(name); } -QList<CatPack*> Application::getValidCatPacks() -{ - return m_themeManager->getValidCatPacks(); -} - -QString Application::getCatPack(QString catName) -{ - return m_themeManager->getCatPack(catName); -} - bool Application::openJsonEditor(const QString& filename) { const QString file = QDir::current().absoluteFilePath(filename); diff --git a/launcher/Application.h b/launcher/Application.h index c0a980b2..1c221cec 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -71,6 +71,7 @@ class TranslationsModel; class ITheme; class MCEditTool; class ThemeManager; +class IconTheme; namespace Meta { class Index; @@ -119,17 +120,7 @@ public: QIcon getThemedIcon(const QString& name); - void setIconTheme(const QString& name); - - void applyCurrentlySelectedTheme(bool initial = false); - - QList<ITheme*> getValidApplicationThemes(); - - void setApplicationTheme(const QString& name); - - QList<CatPack*> getValidCatPacks(); - - QString getCatPack(QString catName = ""); + ThemeManager* themeManager() { return m_themeManager.get(); } shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; } diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 2d06dbf4..8f5b0e77 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -759,6 +759,8 @@ SET(LAUNCHER_SOURCES ui/themes/ITheme.h ui/themes/SystemTheme.cpp ui/themes/SystemTheme.h + ui/themes/IconTheme.cpp + ui/themes/IconTheme.h ui/themes/ThemeManager.cpp ui/themes/ThemeManager.h ui/themes/CatPack.cpp diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index 2984a1b4..4939161f 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -109,7 +109,7 @@ bool openDirectory(const QString &path, bool ensureExists) qDebug() << "Opening directory" << path; QDir parentPath; QDir dir(path); - if (!dir.exists()) + if (ensureExists && !dir.exists()) { parentPath.mkpath(dir.absolutePath()); } diff --git a/launcher/resources/multimc/index.theme b/launcher/resources/multimc/index.theme index 070e23f1..4da8072d 100644 --- a/launcher/resources/multimc/index.theme +++ b/launcher/resources/multimc/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=multimc +Name=Legacy Comment=Default Icons Inherits=default Directories=8x8,16x16,22x22,24x24,32x32,32x32/instances,48x48,50x50/instances,64x64,128x128/instances,256x256,scalable,scalable/instances diff --git a/launcher/resources/pe_blue/index.theme b/launcher/resources/pe_blue/index.theme index c9e0d93a..6d842b5d 100644 --- a/launcher/resources/pe_blue/index.theme +++ b/launcher/resources/pe_blue/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=pe_blue +Name=Simple (Blue) Comment=Icons by pexner (blue) Inherits=multimc Directories=scalable diff --git a/launcher/resources/pe_colored/index.theme b/launcher/resources/pe_colored/index.theme index b757bbd7..bca5494f 100644 --- a/launcher/resources/pe_colored/index.theme +++ b/launcher/resources/pe_colored/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=pe_colored +Name=Simple (Colored) Comment=Icons by pexner (colored) Inherits=multimc Directories=scalable diff --git a/launcher/resources/pe_dark/index.theme b/launcher/resources/pe_dark/index.theme index b7d1ad01..4cfbf09c 100644 --- a/launcher/resources/pe_dark/index.theme +++ b/launcher/resources/pe_dark/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=pe_dark +Name=Simple (Dark) Comment=Icons by pexner (dark) Inherits=multimc Directories=scalable diff --git a/launcher/resources/pe_light/index.theme b/launcher/resources/pe_light/index.theme index c106acc8..87b76d13 100644 --- a/launcher/resources/pe_light/index.theme +++ b/launcher/resources/pe_light/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=pe_light +Name=Simple (Light) Comment=Icons by pexner (light) Inherits=multimc Directories=scalable diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index da572fc3..e4a699da 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -627,7 +627,7 @@ void MainWindow::updateThemeMenu() themeMenu = new QMenu(this); } - auto themes = APPLICATION->getValidApplicationThemes(); + auto themes = APPLICATION->themeManager()->getValidApplicationThemes(); QActionGroup* themesGroup = new QActionGroup(this); @@ -641,7 +641,7 @@ void MainWindow::updateThemeMenu() themeAction->setActionGroup(themesGroup); connect(themeAction, &QAction::triggered, [theme]() { - APPLICATION->setApplicationTheme(theme->id()); + APPLICATION->themeManager()->setApplicationTheme(theme->id()); APPLICATION->settings()->set("ApplicationTheme", theme->id()); }); } @@ -1134,26 +1134,40 @@ void MainWindow::undoTrashInstance() ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething()); } +void MainWindow::on_actionViewLauncherRootFolder_triggered() +{ + DesktopServices::openDirectory("."); +} + void MainWindow::on_actionViewInstanceFolder_triggered() { QString str = APPLICATION->settings()->get("InstanceDir").toString(); DesktopServices::openDirectory(str); } -void MainWindow::on_actionViewLauncherRootFolder_triggered() +void MainWindow::on_actionViewCentralModsFolder_triggered() { - const QString dataPath = QDir::currentPath(); - DesktopServices::openDirectory(dataPath); + DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true); } -void MainWindow::refreshInstances() +void MainWindow::on_actionViewIconThemeFolder_triggered() { - APPLICATION->instances()->loadList(); + DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); } -void MainWindow::on_actionViewCentralModsFolder_triggered() +void MainWindow::on_actionViewWidgetThemeFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true); + DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); +} + +void MainWindow::on_actionViewCatPackFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); +} + +void MainWindow::refreshInstances() +{ + APPLICATION->instances()->loadList(); } void MainWindow::checkForUpdates() diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 27c2756f..ffc7154a 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -111,16 +111,19 @@ private slots: void on_actionChangeInstIcon_triggered(); + void on_actionViewLauncherRootFolder_triggered(); + void on_actionViewInstanceFolder_triggered(); + void on_actionViewCentralModsFolder_triggered(); - void on_actionViewLauncherRootFolder_triggered(); + void on_actionViewIconThemeFolder_triggered(); + void on_actionViewWidgetThemeFolder_triggered(); + void on_actionViewCatPackFolder_triggered(); void on_actionViewSelectedInstFolder_triggered(); void refreshInstances(); - void on_actionViewCentralModsFolder_triggered(); - void checkForUpdates(); void on_actionSettings_triggered(); diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index e4421d40..6ef32099 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -186,9 +186,14 @@ <property name="toolTipsVisible"> <bool>true</bool> </property> - <addaction name="actionViewInstanceFolder"/> <addaction name="actionViewLauncherRootFolder"/> + <addaction name="separator"/> + <addaction name="actionViewInstanceFolder"/> <addaction name="actionViewCentralModsFolder"/> + <addaction name="separator"/> + <addaction name="actionViewIconThemeFolder"/> + <addaction name="actionViewWidgetThemeFolder"/> + <addaction name="actionViewCatPackFolder"/> </widget> <widget class="QMenu" name="accountsMenu"> <property name="title"> @@ -465,7 +470,8 @@ </action> <action name="actionExportInstanceZip"> <property name="icon"> - <iconset theme="launcher"/> + <iconset theme="launcher"> + <normaloff>.</normaloff>.</iconset> </property> <property name="text"> <string>Prism Launcher (zip)</string> @@ -473,7 +479,8 @@ </action> <action name="actionExportInstanceMrPack"> <property name="icon"> - <iconset theme="modrinth"/> + <iconset theme="modrinth"> + <normaloff>.</normaloff>.</iconset> </property> <property name="text"> <string>Modrinth (mrpack)</string> @@ -481,15 +488,17 @@ </action> <action name="actionExportInstanceFlamePack"> <property name="icon"> - <iconset theme="flame"/> + <iconset theme="flame"> + <normaloff>.</normaloff>.</iconset> </property> <property name="text"> - <string>CurseForge (zip)</string> + <string>CurseForge (zip)</string> </property> </action> <action name="actionExportInstanceToModList"> <property name="icon"> - <iconset theme="new"/> + <iconset theme="new"> + <normaloff>.</normaloff>.</iconset> </property> <property name="text"> <string>Mod List</string> @@ -552,7 +561,7 @@ <normaloff>.</normaloff>.</iconset> </property> <property name="text"> - <string>&View Instance Folder</string> + <string>View &Instance Folder</string> </property> <property name="toolTip"> <string>Open the instance folder in a file browser.</string> @@ -564,7 +573,7 @@ <normaloff>.</normaloff>.</iconset> </property> <property name="text"> - <string>&View Launcher Root Folder</string> + <string>View Launcher &Root Folder</string> </property> <property name="toolTip"> <string>Open the launcher's root folder in a file browser.</string> @@ -719,6 +728,38 @@ <string>Open the %1 wiki</string> </property> </action> + <action name="actionViewWidgetThemeFolder"> + <property name="icon"> + <iconset theme="viewfolder"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="text"> + <string>View &Widget Themes Folder</string> + </property> + <property name="toolTip"> + <string>View Widget Theme Folder</string> + </property> + </action> + <action name="actionViewIconThemeFolder"> + <property name="icon"> + <iconset theme="viewfolder"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="text"> + <string>View I&con Theme Folder</string> + </property> + <property name="toolTip"> + <string>View Icon Theme Folder</string> + </property> + </action> + <action name="actionViewCatPackFolder"> + <property name="icon"> + <iconset theme="viewfolder"/> + </property> + <property name="text"> + <string>View Cat Packs Folder</string> + </property> + </action> </widget> <customwidgets> <customwidget> diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 05f0004d..155550e1 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -52,7 +52,7 @@ #include <Application.h> #include <InstanceList.h> - +#include "ui/themes/ThemeManager.h" template <typename T> bool listsIntersect(const QList<T> &l1, const QList<T> t2) { @@ -503,7 +503,7 @@ void InstanceView::setPaintCat(bool visible) { m_catVisible = visible; if (visible) - m_catPixmap.load(APPLICATION->getCatPack()); + m_catPixmap.load(APPLICATION->themeManager()->getCatPack()); else m_catPixmap = QPixmap(); } diff --git a/launcher/ui/setupwizard/ThemeWizardPage.cpp b/launcher/ui/setupwizard/ThemeWizardPage.cpp index 1c336921..fe11ed9a 100644 --- a/launcher/ui/setupwizard/ThemeWizardPage.cpp +++ b/launcher/ui/setupwizard/ThemeWizardPage.cpp @@ -61,7 +61,7 @@ void ThemeWizardPage::updateIcons() void ThemeWizardPage::updateCat() { qDebug() << "Setting Cat"; - ui->catImagePreviewButton->setIcon(QIcon(QString(R"(%1)").arg(APPLICATION->getCatPack()))); + ui->catImagePreviewButton->setIcon(QIcon(QString(R"(%1)").arg(APPLICATION->themeManager()->getCatPack()))); } void ThemeWizardPage::retranslate() diff --git a/launcher/ui/themes/IconTheme.cpp b/launcher/ui/themes/IconTheme.cpp new file mode 100644 index 00000000..4bd88985 --- /dev/null +++ b/launcher/ui/themes/IconTheme.cpp @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "IconTheme.h" + +#include <QFile> +#include <QSettings> + +IconTheme::IconTheme(const QString& id, const QString& path) : m_id(id), m_path(path) {} + +bool IconTheme::load() +{ + const QString path = m_path + "/index.theme"; + + if (!QFile::exists(path)) + return false; + + QSettings settings(path, QSettings::IniFormat); + settings.beginGroup("Icon Theme"); + m_name = settings.value("Name").toString(); + 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 new file mode 100644 index 00000000..4e466c6a --- /dev/null +++ b/launcher/ui/themes/IconTheme.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <QString> + +class IconTheme { + public: + IconTheme(const QString& id, const QString& path); + IconTheme() = default; + + bool load(); + QString id(); + QString path(); + QString name(); + + private: + QString m_id; + QString m_path; + QString m_name; +}; diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp index 321f7db4..bce13b45 100644 --- a/launcher/ui/themes/ThemeManager.cpp +++ b/launcher/ui/themes/ThemeManager.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Tayou <git@tayou.org> + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,9 +32,8 @@ #include "Application.h" -ThemeManager::ThemeManager(MainWindow* mainWindow) +ThemeManager::ThemeManager() { - m_mainWindow = mainWindow; initializeThemes(); initializeCatPacks(); } @@ -59,53 +59,110 @@ ITheme* ThemeManager::getTheme(QString themeId) return m_themes[themeId].get(); } +QString ThemeManager::addIconTheme(IconTheme theme) +{ + QString id = theme.id(); + if (m_icons.find(id) == m_icons.end()) + m_icons.emplace(id, std::move(theme)); + else + themeWarningLog() << "IconTheme(" << id << ") not added to prevent id duplication"; + return id; +} + void ThemeManager::initializeThemes() { // Icon themes - { - // TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies! - // set icon theme search path! - auto searchPaths = QIcon::themeSearchPaths(); - searchPaths.append("iconthemes"); - QIcon::setThemeSearchPaths(searchPaths); - themeDebugLog() << "<> Icon themes initialized."; - } + initializeIcons(); // Initialize widget themes - { - themeDebugLog() << "<> Initializing Widget Themes"; - themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique<SystemTheme>()); - auto darkThemeId = addTheme(std::make_unique<DarkTheme>()); - themeDebugLog() << "Loading Built-in Theme:" << darkThemeId; - themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique<BrightTheme>()); - - // TODO: need some way to differentiate same name themes in different subdirectories (maybe smaller grey text next to theme name in - // dropdown?) - QString themeFolder = QDir("./themes/").absoluteFilePath(""); - themeDebugLog() << "Theme Folder Path: " << themeFolder; - - QDirIterator directoryIterator(themeFolder, QDir::Dirs | QDir::NoDotAndDotDot); - while (directoryIterator.hasNext()) { - QDir dir(directoryIterator.next()); - QFileInfo themeJson(dir.absoluteFilePath("theme.json")); - if (themeJson.exists()) { - // Load "theme.json" based themes - themeDebugLog() << "Loading JSON Theme from:" << themeJson.absoluteFilePath(); - addTheme(std::make_unique<CustomTheme>(getTheme(darkThemeId), themeJson, true)); - } else { - // Load pure QSS Themes - QDirIterator stylesheetFileIterator(dir.absoluteFilePath(""), { "*.qss", "*.css" }, QDir::Files); - while (stylesheetFileIterator.hasNext()) { - QFile customThemeFile(stylesheetFileIterator.next()); - QFileInfo customThemeFileInfo(customThemeFile); - themeDebugLog() << "Loading QSS Theme from:" << customThemeFileInfo.absoluteFilePath(); - addTheme(std::make_unique<CustomTheme>(getTheme(darkThemeId), customThemeFileInfo, false)); - } + initializeWidgets(); +} + +void ThemeManager::initializeIcons() +{ + // TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies! + // set icon theme search path! + themeDebugLog() << "<> Initializing Icon Themes"; + + auto searchPaths = QIcon::themeSearchPaths(); + searchPaths.append(m_iconThemeFolder.path()); + QIcon::setThemeSearchPaths(searchPaths); + + for (const QString& id : builtinIcons) { + IconTheme theme(id, QString(":/icons/%1").arg(id)); + if (!theme.load()) { + themeWarningLog() << "Couldn't load built-in icon theme" << id; + continue; + } + + addIconTheme(std::move(theme)); + themeDebugLog() << "Loaded Built-In Icon Theme" << id; + } + + if (!m_iconThemeFolder.mkpath(".")) + themeWarningLog() << "Couldn't create icon theme folder"; + themeDebugLog() << "Icon Theme Folder Path: " << m_iconThemeFolder.absolutePath(); + + QDirIterator directoryIterator(m_iconThemeFolder.path(), QDir::Dirs | QDir::NoDotAndDotDot); + while (directoryIterator.hasNext()) { + QDir dir(directoryIterator.next()); + IconTheme theme(dir.dirName(), dir.path()); + if (!theme.load()) + continue; + + addIconTheme(std::move(theme)); + themeDebugLog() << "Loaded Custom Icon Theme from" << dir.path(); + } + + themeDebugLog() << "<> Icon themes initialized."; +} + +void ThemeManager::initializeWidgets() +{ + themeDebugLog() << "<> Initializing Widget Themes"; + themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique<SystemTheme>()); + auto darkThemeId = addTheme(std::make_unique<DarkTheme>()); + themeDebugLog() << "Loading Built-in Theme:" << darkThemeId; + themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique<BrightTheme>()); + + // TODO: need some way to differentiate same name themes in different subdirectories (maybe smaller grey text next to theme name in + // dropdown?) + + if (!m_applicationThemeFolder.mkpath(".")) + themeWarningLog() << "Couldn't create theme folder"; + themeDebugLog() << "Theme Folder Path: " << m_applicationThemeFolder.absolutePath(); + + QDirIterator directoryIterator(m_applicationThemeFolder.path(), QDir::Dirs | QDir::NoDotAndDotDot); + while (directoryIterator.hasNext()) { + QDir dir(directoryIterator.next()); + QFileInfo themeJson(dir.absoluteFilePath("theme.json")); + if (themeJson.exists()) { + // Load "theme.json" based themes + themeDebugLog() << "Loading JSON Theme from:" << themeJson.absoluteFilePath(); + addTheme(std::make_unique<CustomTheme>(getTheme(darkThemeId), themeJson, true)); + } else { + // Load pure QSS Themes + QDirIterator stylesheetFileIterator(dir.absoluteFilePath(""), { "*.qss", "*.css" }, QDir::Files); + while (stylesheetFileIterator.hasNext()) { + QFile customThemeFile(stylesheetFileIterator.next()); + QFileInfo customThemeFileInfo(customThemeFile); + themeDebugLog() << "Loading QSS Theme from:" << customThemeFileInfo.absoluteFilePath(); + addTheme(std::make_unique<CustomTheme>(getTheme(darkThemeId), customThemeFileInfo, false)); } } + } - themeDebugLog() << "<> Widget themes initialized."; + themeDebugLog() << "<> Widget themes initialized."; +} + +QList<IconTheme*> ThemeManager::getValidIconThemes() +{ + QList<IconTheme*> ret; + ret.reserve(m_icons.size()); + for (auto&& [id, theme] : m_icons) { + ret.append(&theme); } + return ret; } QList<ITheme*> ThemeManager::getValidApplicationThemes() @@ -128,17 +185,39 @@ QList<CatPack*> ThemeManager::getValidCatPacks() return ret; } -void ThemeManager::setIconTheme(const QString& name) +bool ThemeManager::isValidIconTheme(const QString& id) { - QIcon::setThemeName(name); + return !id.isEmpty() && m_icons.find(id) != m_icons.end(); } -void ThemeManager::applyCurrentlySelectedTheme(bool initial) +bool ThemeManager::isValidApplicationTheme(const QString& id) { - setIconTheme(APPLICATION->settings()->get("IconTheme").toString()); - themeDebugLog() << "<> Icon theme set."; - setApplicationTheme(APPLICATION->settings()->get("ApplicationTheme").toString(), initial); - themeDebugLog() << "<> Application theme set."; + return !id.isEmpty() && m_themes.find(id) != m_themes.end(); +} + +QDir ThemeManager::getIconThemesFolder() +{ + return m_iconThemeFolder; +} + +QDir ThemeManager::getApplicationThemesFolder() +{ + return m_applicationThemeFolder; +} + +QDir ThemeManager::getCatPacksFolder() +{ + return m_catPacksFolder; +} + +void ThemeManager::setIconTheme(const QString& name) +{ + if (m_icons.find(name) == m_icons.end()) { + themeWarningLog() << "Tried to set invalid icon theme:" << name; + return; + } + + QIcon::setThemeName(name); } void ThemeManager::setApplicationTheme(const QString& name, bool initial) @@ -154,6 +233,15 @@ void ThemeManager::setApplicationTheme(const QString& name, bool initial) } } +void ThemeManager::applyCurrentlySelectedTheme(bool initial) +{ + auto settings = APPLICATION->settings(); + setIconTheme(settings->get("IconTheme").toString()); + themeDebugLog() << "<> Icon theme set."; + setApplicationTheme(settings->get("ApplicationTheme").toString(), initial); + themeDebugLog() << "<> Application theme set."; +} + QString ThemeManager::getCatPack(QString catName) { auto catIter = m_catPacks.find(!catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString()); @@ -187,9 +275,9 @@ void ThemeManager::initializeCatPacks() for (auto [id, name] : defaultCats) { addCatPack(std::unique_ptr<CatPack>(new BasicCatPack(id, name))); } - QDir catpacksDir("catpacks"); - QString catpacksFolder = catpacksDir.absoluteFilePath(""); - themeDebugLog() << "CatPacks Folder Path:" << catpacksFolder; + if (!m_catPacksFolder.mkpath(".")) + themeWarningLog() << "Couldn't create theme folder"; + themeDebugLog() << "CatPacks Folder Path:" << m_catPacksFolder.absolutePath(); QStringList supportedImageFormats; for (auto format : QImageReader::supportedImageFormats()) { @@ -206,9 +294,9 @@ void ThemeManager::initializeCatPacks() } }; - loadFiles(catpacksDir); + loadFiles(m_catPacksFolder); - QDirIterator directoryIterator(catpacksFolder, QDir::Dirs | QDir::NoDotAndDotDot); + QDirIterator directoryIterator(m_catPacksFolder.path(), QDir::Dirs | QDir::NoDotAndDotDot); while (directoryIterator.hasNext()) { QDir dir(directoryIterator.next()); QFileInfo manifest(dir.absoluteFilePath("catpack.json")); diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h index 1ce8c6f4..b5c66677 100644 --- a/launcher/ui/themes/ThemeManager.h +++ b/launcher/ui/themes/ThemeManager.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Tayou <git@tayou.org> + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +20,7 @@ #include <QString> +#include "IconTheme.h" #include "ui/MainWindow.h" #include "ui/themes/CatPack.h" #include "ui/themes/ITheme.h" @@ -34,11 +36,17 @@ inline auto themeWarningLog() class ThemeManager { public: - ThemeManager(MainWindow* mainWindow); + ThemeManager(); + QList<IconTheme*> getValidIconThemes(); QList<ITheme*> getValidApplicationThemes(); - void setIconTheme(const QString& name); + bool isValidIconTheme(const QString& id); + bool isValidApplicationTheme(const QString& id); + QDir getIconThemesFolder(); + QDir getApplicationThemesFolder(); + QDir getCatPacksFolder(); void applyCurrentlySelectedTheme(bool initial = false); + void setIconTheme(const QString& name); void setApplicationTheme(const QString& name, bool initial = false); /// @brief Returns the background based on selected and with events (Birthday, XMas, etc.) @@ -49,12 +57,21 @@ class ThemeManager { private: std::map<QString, std::unique_ptr<ITheme>> m_themes; + std::map<QString, IconTheme> m_icons; + QDir m_iconThemeFolder{ "iconthemes" }; + QDir m_applicationThemeFolder{ "themes" }; + QDir m_catPacksFolder{ "catpacks" }; std::map<QString, std::unique_ptr<CatPack>> m_catPacks; - MainWindow* m_mainWindow; void initializeThemes(); void initializeCatPacks(); QString addTheme(std::unique_ptr<ITheme> theme); ITheme* getTheme(QString themeId); + QString addIconTheme(IconTheme theme); QString addCatPack(std::unique_ptr<CatPack> catPack); + void initializeIcons(); + void initializeWidgets(); + + const QStringList builtinIcons{ "pe_colored", "pe_light", "pe_dark", "pe_blue", "breeze_light", "breeze_dark", + "OSX", "iOS", "flat", "flat_white", "multimc" }; }; diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp index 291f8ed9..c999ac92 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp @@ -19,17 +19,23 @@ #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) +ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget* parent) : QWidget(parent), ui(new Ui::ThemeCustomizationWidget) { ui->setupUi(this); loadSettings(); connect(ui->iconsComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); - connect(ui->widgetStyleComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyWidgetTheme); + connect(ui->widgetStyleComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, + &ThemeCustomizationWidget::applyWidgetTheme); connect(ui->backgroundCatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyCatTheme); + + connect(ui->iconsFolder, &QPushButton::clicked, this, [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); }); + connect(ui->widgetStyleFolder, &QPushButton::clicked, this, [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); }); + connect(ui->catPackFolder, &QPushButton::clicked, this, [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); }); } ThemeCustomizationWidget::~ThemeCustomizationWidget() @@ -40,7 +46,7 @@ ThemeCustomizationWidget::~ThemeCustomizationWidget() /// <summary> /// 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); @@ -48,7 +54,7 @@ ThemeCustomizationWidget::~ThemeCustomizationWidget() /// 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); @@ -61,35 +67,37 @@ ThemeCustomizationWidget::~ThemeCustomizationWidget() /// } /// </summary> /// <param name="features"></param> -void ThemeCustomizationWidget::showFeatures(ThemeFields features) { +void ThemeCustomizationWidget::showFeatures(ThemeFields features) +{ ui->iconsComboBox->setEnabled(features & ThemeFields::ICONS); ui->iconsLabel->setEnabled(features & ThemeFields::ICONS); ui->widgetStyleComboBox->setEnabled(features & ThemeFields::WIDGETS); - ui->widgetThemeLabel->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) { +void ThemeCustomizationWidget::applyIconTheme(int index) +{ auto settings = APPLICATION->settings(); auto originalIconTheme = settings->get("IconTheme").toString(); - auto& newIconTheme = m_iconThemeOptions[index].first; - settings->set("IconTheme", newIconTheme); - + auto newIconTheme = ui->iconsComboBox->currentData().toString(); if (originalIconTheme != newIconTheme) { - APPLICATION->applyCurrentlySelectedTheme(); + settings->set("IconTheme", newIconTheme); + APPLICATION->themeManager()->applyCurrentlySelectedTheme(); } emit currentIconThemeChanged(index); } -void ThemeCustomizationWidget::applyWidgetTheme(int index) { +void ThemeCustomizationWidget::applyWidgetTheme(int index) +{ auto settings = APPLICATION->settings(); auto originalAppTheme = settings->get("ApplicationTheme").toString(); auto newAppTheme = ui->widgetStyleComboBox->currentData().toString(); if (originalAppTheme != newAppTheme) { settings->set("ApplicationTheme", newAppTheme); - APPLICATION->applyCurrentlySelectedTheme(); + APPLICATION->themeManager()->applyCurrentlySelectedTheme(); } emit currentWidgetThemeChanged(index); @@ -117,18 +125,23 @@ void ThemeCustomizationWidget::loadSettings() { auto settings = APPLICATION->settings(); - auto iconTheme = settings->get("IconTheme").toString(); - for (auto& iconThemeFromList : m_iconThemeOptions) { - QIcon iconForComboBox = QIcon(QString(":/icons/%1/scalable/settings").arg(iconThemeFromList.first)); - ui->iconsComboBox->addItem(iconForComboBox, iconThemeFromList.second); - if (iconTheme == iconThemeFromList.first) { - ui->iconsComboBox->setCurrentIndex(ui->iconsComboBox->count() - 1); + { + 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->getValidApplicationThemes(); + auto themes = APPLICATION->themeManager()->getValidApplicationThemes(); int idx = 0; for (auto& theme : themes) { ui->widgetStyleComboBox->addItem(theme->name(), theme->id()); @@ -140,7 +153,7 @@ void ThemeCustomizationWidget::loadSettings() } auto cat = settings->get("BackgroundCat").toString(); - for (auto& catFromList : APPLICATION->getValidCatPacks()) { + 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()) { diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.h b/launcher/ui/widgets/ThemeCustomizationWidget.h index af47c788..cef5fb6c 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.h +++ b/launcher/ui/widgets/ThemeCustomizationWidget.h @@ -31,7 +31,7 @@ class ThemeCustomizationWidget : public QWidget { public: explicit ThemeCustomizationWidget(QWidget* parent = nullptr); - ~ThemeCustomizationWidget(); + ~ThemeCustomizationWidget() override; void showFeatures(ThemeFields features); @@ -52,18 +52,4 @@ class ThemeCustomizationWidget : public QWidget { private: Ui::ThemeCustomizationWidget* ui; - - // TODO finish implementing - QList<std::pair<QString, QString>> m_iconThemeOptions{ { "pe_colored", QObject::tr("Simple (Colored Icons)") }, - { "pe_light", QObject::tr("Simple (Light Icons)") }, - { "pe_dark", QObject::tr("Simple (Dark Icons)") }, - { "pe_blue", QObject::tr("Simple (Blue Icons)") }, - { "breeze_light", QObject::tr("Breeze Light") }, - { "breeze_dark", QObject::tr("Breeze Dark") }, - { "OSX", QObject::tr("OSX") }, - { "iOS", QObject::tr("iOS") }, - { "flat", QObject::tr("Flat") }, - { "flat_white", QObject::tr("Flat (White)") }, - { "multimc", QObject::tr("Legacy") }, - { "custom", QObject::tr("Custom") } }; }; diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.ui b/launcher/ui/widgets/ThemeCustomizationWidget.ui index f216a610..4503181c 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.ui +++ b/launcher/ui/widgets/ThemeCustomizationWidget.ui @@ -40,22 +40,43 @@ </widget> </item> <item row="0" column="1"> - <widget class="QComboBox" name="iconsComboBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="focusPolicy"> - <enum>Qt::StrongFocus</enum> - </property> - </widget> + <layout class="QHBoxLayout" name="iconsLayout"> + <item> + <widget class="QComboBox" name="iconsComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="iconsFolder"> + <property name="toolTip"> + <string>View icon themes folder.</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="viewfolder"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> </item> <item row="1" column="0"> - <widget class="QLabel" name="widgetThemeLabel"> + <widget class="QLabel" name="widgetStyleLabel"> <property name="text"> - <string>&Colors</string> + <string>&Widgets</string> </property> <property name="buddy"> <cstring>widgetStyleComboBox</cstring> @@ -63,17 +84,38 @@ </widget> </item> <item row="1" column="1"> - <widget class="QComboBox" name="widgetStyleComboBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="focusPolicy"> - <enum>Qt::StrongFocus</enum> - </property> - </widget> + <layout class="QHBoxLayout" name="widgetStyleLayout"> + <item> + <widget class="QComboBox" name="widgetStyleComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="widgetStyleFolder"> + <property name="toolTip"> + <string>View widget themes folder.</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="viewfolder"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> </item> <item row="2" column="0"> <widget class="QLabel" name="backgroundCatLabel"> @@ -89,7 +131,7 @@ </widget> </item> <item row="2" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout"> + <layout class="QHBoxLayout" name="catLayout"> <item> <widget class="QComboBox" name="backgroundCatComboBox"> <property name="sizePolicy"> @@ -107,15 +149,15 @@ </widget> </item> <item> - <widget class="QPushButton" name="catInfoLabel"> + <widget class="QPushButton" name="catPackFolder"> <property name="toolTip"> - <string>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.</string> + <string>View cat packs folder.</string> </property> <property name="text"> <string/> </property> <property name="icon"> - <iconset theme="about"> + <iconset theme="viewfolder"> <normaloff>.</normaloff>.</iconset> </property> <property name="flat"> |