diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 6194527c5..a13935101 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -492,7 +492,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")); @@ -754,7 +754,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } // Themes - m_themeManager = std::make_unique(m_mainWindow); + m_themeManager = std::make_unique(); // initialize and load all instances { @@ -840,14 +840,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } }); - applyCurrentlySelectedTheme(true); - updateCapabilities(); if (createSetupWizard()) { return; } + m_themeManager->applyCurrentlySelectedTheme(true); performMainStartupAction(); } @@ -873,10 +872,20 @@ 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) { m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard)); @@ -891,9 +900,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; @@ -1070,26 +1079,6 @@ std::shared_ptr Application::javalist() return m_javalist; } -QList 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") { @@ -1098,16 +1087,6 @@ QIcon Application::getThemedIcon(const QString& name) return QIcon::fromTheme(name); } -QList 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 cf7967a56..6bc332749 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; @@ -109,17 +110,7 @@ class Application : public QApplication { QIcon getThemedIcon(const QString& name); - void setIconTheme(const QString& name); - - void applyCurrentlySelectedTheme(bool initial = false); - - QList getValidApplicationThemes(); - - void setApplicationTheme(const QString& name); - - QList getValidCatPacks(); - - QString getCatPack(QString catName = ""); + ThemeManager* themeManager() { return m_themeManager.get(); } shared_qobject_ptr updater() { return m_updater; } diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 84edab59e..18e0acab1 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -774,6 +774,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 6b738f2cf..004e5e085 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -101,7 +101,7 @@ bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists) qDebug() << "Opening directory" << path; QDir parentPath; QDir dir(path); - if (!dir.exists()) { + if (ensureExists && !dir.exists()) { parentPath.mkpath(dir.absolutePath()); } auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); }; diff --git a/launcher/resources/multimc/index.theme b/launcher/resources/multimc/index.theme index 070e23f10..4da8072d9 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 c9e0d93ad..6d842b5d2 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 b757bbd79..bca5494f1 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 b7d1ad011..4cfbf09c4 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 c106acc8b..87b76d139 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 bb88d74e3..a83ff1a2d 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,16 +1134,35 @@ 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::on_actionViewIconThemeFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); +} + +void MainWindow::on_actionViewWidgetThemeFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); +} + +void MainWindow::on_actionViewCatPackFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); } void MainWindow::refreshInstances() @@ -1151,11 +1170,6 @@ void MainWindow::refreshInstances() APPLICATION->instances()->loadList(); } -void MainWindow::on_actionViewCentralModsFolder_triggered() -{ - DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true); -} - void MainWindow::checkForUpdates() { if (BuildConfig.UPDATER_ENABLED) { diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index e6434dae2..be9c4994b 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -109,16 +109,19 @@ class MainWindow : public QMainWindow { void on_actionChangeInstIcon_triggered(); - void on_actionViewInstanceFolder_triggered(); - void on_actionViewLauncherRootFolder_triggered(); + void on_actionViewInstanceFolder_triggered(); + void on_actionViewCentralModsFolder_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 e4421d400..6ef32099f 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -186,9 +186,14 @@ true - + + + + + + @@ -465,7 +470,8 @@ - + + .. Prism Launcher (zip) @@ -473,7 +479,8 @@ - + + .. Modrinth (mrpack) @@ -481,15 +488,17 @@ - + + .. - CurseForge (zip) + CurseForge (zip) - + + .. Mod List @@ -552,7 +561,7 @@ .. - &View Instance Folder + View &Instance Folder Open the instance folder in a file browser. @@ -564,7 +573,7 @@ .. - &View Launcher Root Folder + View Launcher &Root Folder Open the launcher's root folder in a file browser. @@ -719,6 +728,38 @@ Open the %1 wiki + + + + .. + + + View &Widget Themes Folder + + + View Widget Theme Folder + + + + + + .. + + + View I&con Theme Folder + + + View Icon Theme Folder + + + + + + + + View Cat Packs Folder + + diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 607f3cadb..690dc917b 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -47,8 +47,8 @@ #include #include -#include #include "VisualGroup.h" +#include "ui/themes/ThemeManager.h" #include #include @@ -446,7 +446,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 1c3369219..fe11ed9ae 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 000000000..4bd889854 --- /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 + * + * 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 "IconTheme.h" + +#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"; + + 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 000000000..4e466c6ae --- /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 + * + * 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 . + */ + +#pragma once + +#include + +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 321f7db4a..0bcac100c 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 + * Copyright (C) 2023 TheKodeToad * * 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()); - auto darkThemeId = addTheme(std::make_unique()); - themeDebugLog() << "Loading Built-in Theme:" << darkThemeId; - themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique()); + initializeWidgets(); +} - // 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; +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"; - 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(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(getTheme(darkThemeId), customThemeFileInfo, false)); - } - } + 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; } - themeDebugLog() << "<> Widget themes initialized."; + 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()); + auto darkThemeId = addTheme(std::make_unique()); + themeDebugLog() << "Loading Built-in Theme:" << darkThemeId; + themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique()); + + // TODO: need some way to differentiate same name themes in different subdirectories (maybe smaller grey text next to theme name in + // dropdown?) + + 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(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(getTheme(darkThemeId), customThemeFileInfo, false)); + } + } + } + + themeDebugLog() << "<> Widget themes initialized."; +} + +QList ThemeManager::getValidIconThemes() +{ + QList ret; + ret.reserve(m_icons.size()); + for (auto&& [id, theme] : m_icons) { + ret.append(&theme); + } + return ret; } QList ThemeManager::getValidApplicationThemes() @@ -128,17 +185,39 @@ QList 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(new BasicCatPack(id, name))); } - QDir catpacksDir("catpacks"); - QString catpacksFolder = catpacksDir.absoluteFilePath(""); - themeDebugLog() << "CatPacks Folder Path:" << catpacksFolder; + if (!m_catPacksFolder.mkpath(".")) + themeWarningLog() << "Couldn't create catpacks 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 1ce8c6f49..b5c66677b 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 + * Copyright (C) 2023 TheKodeToad * * 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 +#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 getValidIconThemes(); QList 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> m_themes; + std::map m_icons; + QDir m_iconThemeFolder{ "iconthemes" }; + QDir m_applicationThemeFolder{ "themes" }; + QDir m_catPacksFolder{ "catpacks" }; std::map> m_catPacks; - MainWindow* m_mainWindow; void initializeThemes(); void initializeCatPacks(); QString addTheme(std::unique_ptr theme); ITheme* getTheme(QString themeId); + QString addIconTheme(IconTheme theme); QString addCatPack(std::unique_ptr 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 d26906bd3..0de97441f 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp @@ -19,6 +19,7 @@ #include "ui_ThemeCustomizationWidget.h" #include "Application.h" +#include "DesktopServices.h" #include "ui/themes/ITheme.h" #include "ui/themes/ThemeManager.h" @@ -31,6 +32,13 @@ ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget* parent) : QWidget(pa 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::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() @@ -67,7 +75,7 @@ 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); } @@ -76,11 +84,10 @@ 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); @@ -93,7 +100,7 @@ void ThemeCustomizationWidget::applyWidgetTheme(int index) auto newAppTheme = ui->widgetStyleComboBox->currentData().toString(); if (originalAppTheme != newAppTheme) { settings->set("ApplicationTheme", newAppTheme); - APPLICATION->applyCurrentlySelectedTheme(); + APPLICATION->themeManager()->applyCurrentlySelectedTheme(); } emit currentWidgetThemeChanged(index); @@ -121,18 +128,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()); @@ -144,7 +156,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 af47c788a..cef5fb6c6 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> 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 f216a610e..4503181c2 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.ui +++ b/launcher/ui/widgets/ThemeCustomizationWidget.ui @@ -40,22 +40,43 @@ - - - - 0 - 0 - - - - Qt::StrongFocus - - + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + View icon themes folder. + + + + + + + .. + + + true + + + + - + - &Colors + &Widgets widgetStyleComboBox @@ -63,17 +84,38 @@ - - - - 0 - 0 - - - - Qt::StrongFocus - - + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + View widget themes folder. + + + + + + + .. + + + true + + + + @@ -89,7 +131,7 @@ - + @@ -107,15 +149,15 @@ - + - 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. - + ..