diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 5772d7cad..73a062e16 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Lenny McLennington + * 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 @@ -54,12 +55,6 @@ #include "ui/pages/global/APIPage.h" #include "ui/pages/global/CustomCommandsPage.h" -#include "ui/themes/ITheme.h" -#include "ui/themes/SystemTheme.h" -#include "ui/themes/DarkTheme.h" -#include "ui/themes/BrightTheme.h" -#include "ui/themes/CustomTheme.h" - #ifdef Q_OS_WIN #include "ui/WinDarkmode.h" #include @@ -74,6 +69,8 @@ #include "ui/pagedialog/PageDialog.h" +#include "ui/themes/ThemeManager.h" + #include "ApplicationMessage.h" #include @@ -750,29 +747,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) qDebug() << "<> Instance icons intialized."; } - // 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); - qDebug() << "<> Icon themes initialized."; - } - - // Initialize widget themes - { - auto insertTheme = [this](ITheme * theme) - { - m_themes.insert(std::make_pair(theme->id(), std::unique_ptr(theme))); - }; - auto darkTheme = new DarkTheme(); - insertTheme(new SystemTheme()); - insertTheme(darkTheme); - insertTheme(new BrightTheme()); - insertTheme(new CustomTheme(darkTheme, "custom")); - qDebug() << "<> Widget themes initialized."; - } + // Themes + m_themeManager = std::make_unique(m_mainWindow); // initialize and load all instances { @@ -1126,45 +1102,19 @@ std::shared_ptr Application::javalist() return m_javalist; } -std::vector Application::getValidApplicationThemes() +QList Application::getValidApplicationThemes() { - std::vector ret; - auto iter = m_themes.cbegin(); - while (iter != m_themes.cend()) - { - ret.push_back((*iter).second.get()); - iter++; - } - return ret; + return m_themeManager->getValidApplicationThemes(); } void Application::setApplicationTheme(const QString& name, bool initial) { - auto systemPalette = qApp->palette(); - auto themeIter = m_themes.find(name); - if(themeIter != m_themes.end()) - { - auto & theme = (*themeIter).second; - theme->apply(initial); -#ifdef Q_OS_WIN - if (m_mainWindow && IsWindows10OrGreater()) { - if (QString::compare(theme->id(), "dark") == 0) { - WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true); - } else { - WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false); - } - } -#endif - } - else - { - qWarning() << "Tried to set invalid theme:" << name; - } + m_themeManager->setApplicationTheme(name, initial); } void Application::setIconTheme(const QString& name) { - QIcon::setThemeName(name); + m_themeManager->setIconTheme(name); } QIcon Application::getThemedIcon(const QString& name) diff --git a/launcher/Application.h b/launcher/Application.h index 8fa0ab10e..78ab8fbd0 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * 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 @@ -68,6 +69,7 @@ class BaseDetachedToolFactory; class TranslationsModel; class ITheme; class MCEditTool; +class ThemeManager; namespace Meta { class Index; @@ -118,7 +120,7 @@ public: void setIconTheme(const QString& name); - std::vector getValidApplicationThemes(); + QList getValidApplicationThemes(); void setApplicationTheme(const QString& name, bool initial); @@ -255,9 +257,9 @@ private: std::shared_ptr m_javalist; std::shared_ptr m_translations; std::shared_ptr m_globalSettingsProvider; - std::map> m_themes; std::unique_ptr m_mcedit; QSet m_features; + std::unique_ptr m_themeManager; QMap> m_profilers; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index fd1dd03bf..bc1f5d5e8 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -647,6 +647,8 @@ SET(LAUNCHER_SOURCES ui/themes/ITheme.h ui/themes/SystemTheme.cpp ui/themes/SystemTheme.h + ui/themes/ThemeManager.cpp + ui/themes/ThemeManager.h # Processes LaunchController.h diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 822c69b0d..6661bf0f4 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (c) 2022 dada513 + * 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 @@ -396,46 +397,8 @@ void LauncherPage::loadSettings() m_currentUpdateChannel = s->get("UpdateChannel").toString(); //FIXME: make generic auto theme = s->get("IconTheme").toString(); - if (theme == "pe_colored") - { - ui->themeComboBox->setCurrentIndex(0); - } - else if (theme == "pe_light") - { - ui->themeComboBox->setCurrentIndex(1); - } - else if (theme == "pe_dark") - { - ui->themeComboBox->setCurrentIndex(2); - } - else if (theme == "pe_blue") - { - ui->themeComboBox->setCurrentIndex(3); - } - else if (theme == "OSX") - { - ui->themeComboBox->setCurrentIndex(4); - } - else if (theme == "iOS") - { - ui->themeComboBox->setCurrentIndex(5); - } - else if (theme == "flat") - { - ui->themeComboBox->setCurrentIndex(6); - } - else if (theme == "flat_white") - { - ui->themeComboBox->setCurrentIndex(7); - } - else if (theme == "multimc") - { - ui->themeComboBox->setCurrentIndex(8); - } - else if (theme == "custom") - { - ui->themeComboBox->setCurrentIndex(9); - } + QStringList iconThemeOptions{"pe_colored", "pe_light", "pe_dark", "pe_blue", "OSX", "iOS", "flat", "flat_white", "multimc", "custom"}; + ui->themeComboBox->setCurrentIndex(iconThemeOptions.indexOf(theme)); auto cat = s->get("BackgroundCat").toString(); if (cat == "kitteh") { diff --git a/launcher/ui/themes/CustomTheme.cpp b/launcher/ui/themes/CustomTheme.cpp index 3e3e27de3..3ad61668e 100644 --- a/launcher/ui/themes/CustomTheme.cpp +++ b/launcher/ui/themes/CustomTheme.cpp @@ -1,48 +1,81 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * 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 "CustomTheme.h" -#include -#include #include +#include +#include "ThemeManager.h" -const char * themeFile = "theme.json"; -const char * styleFile = "themeStyle.css"; +const char* themeFile = "theme.json"; -static bool readThemeJson(const QString &path, QPalette &palette, double &fadeAmount, QColor &fadeColor, QString &name, QString &widgets) +static bool readThemeJson(const QString& path, + QPalette& palette, + double& fadeAmount, + QColor& fadeColor, + QString& name, + QString& widgets, + QString& qssFilePath, + bool& dataIncomplete) { QFileInfo pathInfo(path); - if(pathInfo.exists() && pathInfo.isFile()) - { - try - { + if (pathInfo.exists() && pathInfo.isFile()) { + try { auto doc = Json::requireDocument(path, "Theme JSON file"); const QJsonObject root = doc.object(); + dataIncomplete = !root.contains("qssFilePath"); name = Json::requireString(root, "name", "Theme name"); widgets = Json::requireString(root, "widgets", "Qt widget theme"); + qssFilePath = Json::ensureString(root, "qssFilePath", "themeStyle.css"); auto colorsRoot = Json::requireObject(root, "colors", "colors object"); - auto readColor = [&](QString colorName) -> QColor - { + auto readColor = [&](QString colorName) -> QColor { auto colorValue = Json::ensureString(colorsRoot, colorName, QString()); - if(!colorValue.isEmpty()) - { + if (!colorValue.isEmpty()) { QColor color(colorValue); - if(!color.isValid()) - { - qWarning() << "Color value" << colorValue << "for" << colorName << "was not recognized."; + if (!color.isValid()) { + themeWarningLog() << "Color value" << colorValue << "for" << colorName << "was not recognized."; return QColor(); } return color; } return QColor(); }; - auto readAndSetColor = [&](QPalette::ColorRole role, QString colorName) - { + auto readAndSetColor = [&](QPalette::ColorRole role, QString colorName) { auto color = readColor(colorName); - if(color.isValid()) - { + if (color.isValid()) { palette.setColor(role, color); - } - else - { - qDebug() << "Color value for" << colorName << "was not present."; + } else { + themeDebugLog() << "Color value for" << colorName << "was not present."; } }; @@ -61,36 +94,36 @@ static bool readThemeJson(const QString &path, QPalette &palette, double &fadeAm readAndSetColor(QPalette::Highlight, "Highlight"); readAndSetColor(QPalette::HighlightedText, "HighlightedText"); - //fade + // fade fadeColor = readColor("fadeColor"); fadeAmount = Json::ensureDouble(colorsRoot, "fadeAmount", 0.5, "fade amount"); - } - catch (const Exception &e) - { - qWarning() << "Couldn't load theme json: " << e.cause(); + } catch (const Exception& e) { + themeWarningLog() << "Couldn't load theme json: " << e.cause(); return false; } - } - else - { - qDebug() << "No theme json present."; + } else { + themeDebugLog() << "No theme json present."; return false; } return true; } -static bool writeThemeJson(const QString &path, const QPalette &palette, double fadeAmount, QColor fadeColor, QString name, QString widgets) +static bool writeThemeJson(const QString& path, + const QPalette& palette, + double fadeAmount, + QColor fadeColor, + QString name, + QString widgets, + QString qssFilePath) { QJsonObject rootObj; rootObj.insert("name", name); rootObj.insert("widgets", widgets); + rootObj.insert("qssFilePath", qssFilePath); QJsonObject colorsObj; - auto insertColor = [&](QPalette::ColorRole role, QString colorName) - { - colorsObj.insert(colorName, palette.color(role).name()); - }; + auto insertColor = [&](QPalette::ColorRole role, QString colorName) { colorsObj.insert(colorName, palette.color(role).name()); }; // palette insertColor(QPalette::Window, "Window"); @@ -112,82 +145,95 @@ static bool writeThemeJson(const QString &path, const QPalette &palette, double colorsObj.insert("fadeAmount", fadeAmount); rootObj.insert("colors", colorsObj); - try - { + try { Json::write(rootObj, path); return true; - } - catch (const Exception &e) - { - qWarning() << "Failed to write theme json to" << path; + } catch (const Exception& e) { + themeWarningLog() << "Failed to write theme json to" << path; return false; } } -CustomTheme::CustomTheme(ITheme* baseTheme, QString folder) +/// @param baseTheme Base Theme +/// @param fileInfo FileInfo object for file to load +/// @param isManifest whether to load a theme manifest or a qss file +CustomTheme::CustomTheme(ITheme* baseTheme, QFileInfo& fileInfo, bool isManifest) { - m_id = folder; - QString path = FS::PathCombine("themes", m_id); - QString pathResources = FS::PathCombine("themes", m_id, "resources"); + if (isManifest) { + m_id = fileInfo.dir().dirName(); - qDebug() << "Loading theme" << m_id; + QString path = FS::PathCombine("themes", m_id); + QString pathResources = FS::PathCombine("themes", m_id, "resources"); - if(!FS::ensureFolderPathExists(path) || !FS::ensureFolderPathExists(pathResources)) - { - qWarning() << "couldn't create folder for theme!"; - m_palette = baseTheme->colorScheme(); - m_styleSheet = baseTheme->appStyleSheet(); - return; - } - - auto themeFilePath = FS::PathCombine(path, themeFile); - - m_palette = baseTheme->colorScheme(); - if (!readThemeJson(themeFilePath, m_palette, m_fadeAmount, m_fadeColor, m_name, m_widgets)) - { - m_name = "Custom"; - m_palette = baseTheme->colorScheme(); - m_fadeColor = baseTheme->fadeColor(); - m_fadeAmount = baseTheme->fadeAmount(); - m_widgets = baseTheme->qtTheme(); - - QFileInfo info(themeFilePath); - if(!info.exists()) - { - writeThemeJson(themeFilePath, m_palette, m_fadeAmount, m_fadeColor, "Custom", m_widgets); - } - } - else - { - m_palette = fadeInactive(m_palette, m_fadeAmount, m_fadeColor); - } - - auto cssFilePath = FS::PathCombine(path, styleFile); - QFileInfo info (cssFilePath); - if(info.isFile()) - { - try - { - // TODO: validate css? - m_styleSheet = QString::fromUtf8(FS::read(cssFilePath)); - } - catch (const Exception &e) - { - qWarning() << "Couldn't load css:" << e.cause() << "from" << cssFilePath; + if (!FS::ensureFolderPathExists(path) || !FS::ensureFolderPathExists(pathResources)) { + themeWarningLog() << "couldn't create folder for theme!"; + m_palette = baseTheme->colorScheme(); m_styleSheet = baseTheme->appStyleSheet(); + return; } - } - else - { - qDebug() << "No theme css present."; - m_styleSheet = baseTheme->appStyleSheet(); - try - { - FS::write(cssFilePath, m_styleSheet.toUtf8()); + + auto themeFilePath = FS::PathCombine(path, themeFile); + + bool jsonDataIncomplete = false; + + m_palette = baseTheme->colorScheme(); + if (!readThemeJson(themeFilePath, m_palette, m_fadeAmount, m_fadeColor, m_name, m_widgets, m_qssFilePath, jsonDataIncomplete)) { + themeDebugLog() << "Did not read theme json file correctly, writing new one to: " << themeFilePath; + m_name = "Custom"; + m_palette = baseTheme->colorScheme(); + m_fadeColor = baseTheme->fadeColor(); + m_fadeAmount = baseTheme->fadeAmount(); + m_widgets = baseTheme->qtTheme(); + m_qssFilePath = "themeStyle.css"; + } else { + m_palette = fadeInactive(m_palette, m_fadeAmount, m_fadeColor); } - catch (const Exception &e) - { - qWarning() << "Couldn't write css:" << e.cause() << "to" << cssFilePath; + + if (jsonDataIncomplete) { + writeThemeJson(fileInfo.absoluteFilePath(), m_palette, m_fadeAmount, m_fadeColor, m_name, m_widgets, m_qssFilePath); + } + + auto qssFilePath = FS::PathCombine(path, m_qssFilePath); + QFileInfo info(qssFilePath); + if (info.isFile()) { + try { + // TODO: validate css? + m_styleSheet = QString::fromUtf8(FS::read(qssFilePath)); + } catch (const Exception& e) { + themeWarningLog() << "Couldn't load css:" << e.cause() << "from" << qssFilePath; + m_styleSheet = baseTheme->appStyleSheet(); + } + } else { + themeDebugLog() << "No theme css present."; + m_styleSheet = baseTheme->appStyleSheet(); + try { + FS::write(qssFilePath, m_styleSheet.toUtf8()); + } catch (const Exception& e) { + themeWarningLog() << "Couldn't write css:" << e.cause() << "to" << qssFilePath; + } + } + } else { + m_id = fileInfo.fileName(); + m_name = fileInfo.baseName(); + QString path = fileInfo.filePath(); + // themeDebugLog << "Theme ID: " << m_id; + // themeDebugLog << "Theme Name: " << m_name; + // themeDebugLog << "Theme Path: " << path; + + if (!FS::ensureFilePathExists(path)) { + themeWarningLog() << m_name << " Theme file path doesn't exist!"; + m_palette = baseTheme->colorScheme(); + m_styleSheet = baseTheme->appStyleSheet(); + return; + } + + m_palette = baseTheme->colorScheme(); + try { + // TODO: validate qss? + m_styleSheet = QString::fromUtf8(FS::read(path)); + } catch (const Exception& e) { + themeWarningLog() << "Couldn't load qss:" << e.cause() << "from" << path; + m_styleSheet = baseTheme->appStyleSheet(); } } } @@ -197,7 +243,6 @@ QStringList CustomTheme::searchPaths() return { FS::PathCombine("themes", m_id, "resources") }; } - QString CustomTheme::id() { return m_id; diff --git a/launcher/ui/themes/CustomTheme.h b/launcher/ui/themes/CustomTheme.h index d216895de..f2b1b06ed 100644 --- a/launcher/ui/themes/CustomTheme.h +++ b/launcher/ui/themes/CustomTheme.h @@ -1,11 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * 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. + */ #pragma once +#include #include "ITheme.h" -class CustomTheme: public ITheme -{ -public: - CustomTheme(ITheme * baseTheme, QString folder); +class CustomTheme : public ITheme { + public: + CustomTheme(ITheme* baseTheme, QFileInfo& file, bool isManifest); virtual ~CustomTheme() {} QString id() override; @@ -19,7 +53,7 @@ public: QString qtTheme() override; QStringList searchPaths() override; -private: /* data */ + private: /* data */ QPalette m_palette; QColor m_fadeColor; double m_fadeAmount; @@ -27,5 +61,5 @@ private: /* data */ QString m_name; QString m_id; QString m_widgets; + QString m_qssFilePath; }; - diff --git a/launcher/ui/themes/SystemTheme.cpp b/launcher/ui/themes/SystemTheme.cpp index 49b1afaa1..a63d17411 100644 --- a/launcher/ui/themes/SystemTheme.cpp +++ b/launcher/ui/themes/SystemTheme.cpp @@ -1,30 +1,65 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * 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 "SystemTheme.h" #include #include #include #include +#include "ThemeManager.h" SystemTheme::SystemTheme() { - qDebug() << "Determining System Theme..."; + themeDebugLog() << "Determining System Theme..."; const auto & style = QApplication::style(); systemPalette = style->standardPalette(); QString lowerThemeName = style->objectName(); - qDebug() << "System theme seems to be:" << lowerThemeName; + themeDebugLog() << "System theme seems to be:" << lowerThemeName; QStringList styles = QStyleFactory::keys(); for(auto &st: styles) { - qDebug() << "Considering theme from theme factory:" << st.toLower(); + themeDebugLog() << "Considering theme from theme factory:" << st.toLower(); if(st.toLower() == lowerThemeName) { systemTheme = st; - qDebug() << "System theme has been determined to be:" << systemTheme; + themeDebugLog() << "System theme has been determined to be:" << systemTheme; return; } } // fall back to fusion if we can't find the current theme. systemTheme = "Fusion"; - qDebug() << "System theme not found, defaulted to Fusion"; + themeDebugLog() << "System theme not found, defaulted to Fusion"; } void SystemTheme::apply(bool initial) diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp new file mode 100644 index 000000000..01a38a864 --- /dev/null +++ b/launcher/ui/themes/ThemeManager.cpp @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * 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 . + */ +#include "ThemeManager.h" + +#include +#include +#include +#include +#include "ui/themes/BrightTheme.h" +#include "ui/themes/CustomTheme.h" +#include "ui/themes/DarkTheme.h" +#include "ui/themes/SystemTheme.h" + +#include "Application.h" + +#ifdef Q_OS_WIN +#include +// this is needed for versionhelpers.h, it is also included in WinDarkmode, but we can't rely on that. +// Ultimately this should be included in versionhelpers, but that is outside of the project. +#include "ui/WinDarkmode.h" +#include +#endif + +ThemeManager::ThemeManager(MainWindow* mainWindow) +{ + m_mainWindow = mainWindow; + InitializeThemes(); +} + +/// @brief Adds the Theme to the list of themes +/// @param theme The Theme to add +/// @return Theme ID +QString ThemeManager::AddTheme(std::unique_ptr theme) +{ + QString id = theme->id(); + m_themes.emplace(id, std::move(theme)); + return id; +} + +/// @brief Gets the Theme from the List via ID +/// @param themeId Theme ID of theme to fetch +/// @return Theme at themeId +ITheme* ThemeManager::GetTheme(QString themeId) +{ + return m_themes[themeId].get(); +} + +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."; + } + + // 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()); + + // 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, QDirIterator::Subdirectories); + 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::getValidApplicationThemes() +{ + QList ret; + ret.reserve(m_themes.size()); + for (auto&& [id, theme] : m_themes) { + ret.append(theme.get()); + } + return ret; +} + +void ThemeManager::setIconTheme(const QString& name) +{ + QIcon::setThemeName(name); +} + +void ThemeManager::applyCurrentlySelectedTheme() +{ + setIconTheme(APPLICATION->settings()->get("IconTheme").toString()); + themeDebugLog() << "<> Icon theme set."; + setApplicationTheme(APPLICATION->settings()->get("ApplicationTheme").toString(), true); + themeDebugLog() << "<> Application theme set."; +} + +void ThemeManager::setApplicationTheme(const QString& name, bool initial) +{ + auto systemPalette = qApp->palette(); + auto themeIter = m_themes.find(name); + if (themeIter != m_themes.end()) { + auto& theme = themeIter->second; + themeDebugLog() << "applying theme" << theme->name(); + theme->apply(initial); +#ifdef Q_OS_WIN + if (m_mainWindow && IsWindows10OrGreater()) { + if (QString::compare(theme->id(), "dark") == 0) { + WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true); + } else { + WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false); + } + } +#endif + } else { + themeWarningLog() << "Tried to set invalid theme:" << name; + } +} diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h new file mode 100644 index 000000000..b85cb7423 --- /dev/null +++ b/launcher/ui/themes/ThemeManager.h @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * 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 . + */ +#pragma once + +#include + +#include "ui/MainWindow.h" +#include "ui/themes/ITheme.h" + +inline auto themeDebugLog() +{ + return qDebug() << "[Theme]"; +} +inline auto themeWarningLog() +{ + return qWarning() << "[Theme]"; +} + +class ThemeManager { + public: + ThemeManager(MainWindow* mainWindow); + + // maybe make private? Or put in ctor? + void InitializeThemes(); + + QList getValidApplicationThemes(); + void setIconTheme(const QString& name); + void applyCurrentlySelectedTheme(); + void setApplicationTheme(const QString& name, bool initial); + + private: + std::map> m_themes; + MainWindow* m_mainWindow; + + QString AddTheme(std::unique_ptr theme); + ITheme* GetTheme(QString themeId); +};