Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into feature/java-downloader

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2024-08-23 08:16:59 +03:00
commit 92495494ca
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
25 changed files with 251 additions and 374 deletions

View File

@ -25,7 +25,7 @@ jobs:
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs - name: Create backport PRs
uses: korthout/backport-action@v3.0.2 uses: korthout/backport-action@v3.1.0
with: with:
# Config README: https://github.com/korthout/backport-action#backport-action # Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |- pull_description: |-

12
flake.lock generated
View File

@ -75,11 +75,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1723175592, "lastModified": 1723637854,
"narHash": "sha256-M0xJ3FbDUc4fRZ84dPGx5VvgFsOzds77KiBMW/mMTnI=", "narHash": "sha256-med8+5DSWa2UnOqtdICndjDAEjxr5D7zaIiK4pn0Q7c=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "5e0ca22929f3342b19569b21b2f3462f053e497b", "rev": "c3aa7b8938b17aebd2deecf7be0636000d62a2b9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -103,11 +103,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1723202784, "lastModified": 1723803910,
"narHash": "sha256-qbhjc/NEGaDbyy0ucycubq4N3//gDFFH3DOmp1D3u1Q=", "narHash": "sha256-yezvUuFiEnCFbGuwj/bQcqg7RykIEqudOy/RBrId0pc=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "c7012d0c18567c889b948781bc74a501e92275d1", "rev": "bfef0ada09e2c8ac55bbcd0831bd0c9d42e651ba",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -800,8 +800,6 @@ SET(LAUNCHER_SOURCES
# GUI - windows # GUI - windows
ui/GuiUtil.h ui/GuiUtil.h
ui/GuiUtil.cpp ui/GuiUtil.cpp
ui/ColorCache.h
ui/ColorCache.cpp
ui/MainWindow.h ui/MainWindow.h
ui/MainWindow.cpp ui/MainWindow.cpp
ui/InstanceWindow.h ui/InstanceWindow.h

View File

@ -46,20 +46,23 @@ void CheckJava::executeTask()
{ {
auto instance = m_parent->instance(); auto instance = m_parent->instance();
auto settings = instance->settings(); auto settings = instance->settings();
m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString());
bool perInstance = settings->get("OverrideJavaLocation").toBool(); QString javaPathSetting = settings->get("JavaPath").toString();
m_javaPath = FS::ResolveExecutable(javaPathSetting);
bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool();
auto realJavaPath = QStandardPaths::findExecutable(m_javaPath); auto realJavaPath = QStandardPaths::findExecutable(m_javaPath);
if (realJavaPath.isEmpty()) { if (realJavaPath.isEmpty()) {
if (perInstance) { if (perInstance) {
emit logLine(QString("The java binary \"%1\" couldn't be found. Please fix the java path " emit logLine(QString("The Java binary \"%1\" couldn't be found. Please fix the Java path "
"override in the instance's settings or disable it.") "override in the instance's settings or disable it.")
.arg(m_javaPath), .arg(javaPathSetting),
MessageLevel::Warning); MessageLevel::Warning);
} else { } else {
emit logLine(QString("The java binary \"%1\" couldn't be found. Please set up java in " emit logLine(QString("The Java binary \"%1\" couldn't be found. Please set up Java in "
"the settings.") "the settings.")
.arg(m_javaPath), .arg(javaPathSetting),
MessageLevel::Warning); MessageLevel::Warning);
} }
emitFailed(QString("Java path is not valid.")); emitFailed(QString("Java path is not valid."));

View File

@ -1,32 +0,0 @@
#include "ColorCache.h"
/**
* Blend the color with the front color, adapting to the back color
*/
QColor ColorCache::blend(QColor color)
{
if (Rainbow::luma(m_front) > Rainbow::luma(m_back)) {
// for dark color schemes, produce a fitting color first
color = Rainbow::tint(m_front, color, 0.5);
}
// adapt contrast
return Rainbow::mix(m_front, color, m_bias);
}
/**
* Blend the color with the back color
*/
QColor ColorCache::blendBackground(QColor color)
{
// adapt contrast
return Rainbow::mix(m_back, color, m_bias);
}
void ColorCache::recolorAll()
{
auto iter = m_colors.begin();
while (iter != m_colors.end()) {
iter->front = blend(iter->original);
iter->back = blendBackground(iter->original);
}
}

View File

@ -1,106 +0,0 @@
#pragma once
#include <MessageLevel.h>
#include <rainbow.h>
#include <QMap>
#include <QtGui/QColor>
class ColorCache {
public:
ColorCache(QColor front, QColor back, qreal bias)
{
m_front = front;
m_back = back;
m_bias = bias;
};
void addColor(int key, QColor color) { m_colors[key] = { color, blend(color), blendBackground(color) }; }
void setForeground(QColor front)
{
if (m_front != front) {
m_front = front;
recolorAll();
}
}
void setBackground(QColor back)
{
if (m_back != back) {
m_back = back;
recolorAll();
}
}
QColor getFront(int key)
{
auto iter = m_colors.find(key);
if (iter == m_colors.end()) {
return QColor();
}
return (*iter).front;
}
QColor getBack(int key)
{
auto iter = m_colors.find(key);
if (iter == m_colors.end()) {
return QColor();
}
return (*iter).back;
}
/**
* Blend the color with the front color, adapting to the back color
*/
QColor blend(QColor color);
/**
* Blend the color with the back color
*/
QColor blendBackground(QColor color);
protected:
void recolorAll();
protected:
struct ColorEntry {
QColor original;
QColor front;
QColor back;
};
protected:
qreal m_bias;
QColor m_front;
QColor m_back;
QMap<int, ColorEntry> m_colors;
};
class LogColorCache : public ColorCache {
public:
LogColorCache(QColor front, QColor back) : ColorCache(front, back, 1.0)
{
addColor((int)MessageLevel::Launcher, QColor("purple"));
addColor((int)MessageLevel::Debug, QColor("green"));
addColor((int)MessageLevel::Warning, QColor("orange"));
addColor((int)MessageLevel::Error, QColor("red"));
addColor((int)MessageLevel::Fatal, QColor("red"));
addColor((int)MessageLevel::Message, front);
}
QColor getFront(MessageLevel::Enum level)
{
if (!m_colors.contains((int)level)) {
return ColorCache::getFront((int)MessageLevel::Message);
}
return ColorCache::getFront((int)level);
}
QColor getBack(MessageLevel::Enum level)
{
if (level == MessageLevel::Fatal) {
return QColor(Qt::black);
}
return QColor(Qt::transparent);
}
};

View File

@ -121,7 +121,7 @@ void VersionSelectDialog::setResizeOn(int column)
int VersionSelectDialog::exec() int VersionSelectDialog::exec()
{ {
QDialog::open(); QDialog::open();
m_versionWidget->initialize(m_vlist); m_versionWidget->initialize(m_vlist, true);
m_versionWidget->selectSearch(); m_versionWidget->selectSearch();
if (resizeOnColumn != -1) { if (resizeOnColumn != -1) {
m_versionWidget->setResizeOn(resizeOnColumn); m_versionWidget->setResizeOn(resizeOnColumn);

View File

@ -33,7 +33,7 @@ class VersionSelectDialog : public QDialog {
public: public:
explicit VersionSelectDialog(BaseVersionList* vlist, QString title, QWidget* parent = 0, bool cancelable = true); explicit VersionSelectDialog(BaseVersionList* vlist, QString title, QWidget* parent = 0, bool cancelable = true);
virtual ~VersionSelectDialog() {}; virtual ~VersionSelectDialog() = default;
int exec() override; int exec() override;

View File

@ -4,6 +4,7 @@
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (c) 2022 dada513 <dada513@protonmail.com> * Copyright (c) 2022 dada513 <dada513@protonmail.com>
* Copyright (C) 2022 Tayou <git@tayou.org> * Copyright (C) 2022 Tayou <git@tayou.org>
* Copyright (C) 2024 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -50,6 +51,7 @@
#include "DesktopServices.h" #include "DesktopServices.h"
#include "settings/SettingsObject.h" #include "settings/SettingsObject.h"
#include "ui/themes/ITheme.h" #include "ui/themes/ITheme.h"
#include "ui/themes/ThemeManager.h"
#include "updater/ExternalUpdater.h" #include "updater/ExternalUpdater.h"
#include <QApplication> #include <QApplication>
@ -66,9 +68,6 @@ enum InstSortMode {
LauncherPage::LauncherPage(QWidget* parent) : QWidget(parent), ui(new Ui::LauncherPage) LauncherPage::LauncherPage(QWidget* parent) : QWidget(parent), ui(new Ui::LauncherPage)
{ {
ui->setupUi(this); ui->setupUi(this);
auto origForeground = ui->fontPreview->palette().color(ui->fontPreview->foregroundRole());
auto origBackground = ui->fontPreview->palette().color(ui->fontPreview->backgroundRole());
m_colors.reset(new LogColorCache(origForeground, origBackground));
ui->sortingModeGroup->setId(ui->sortByNameBtn, Sort_Name); ui->sortingModeGroup->setId(ui->sortByNameBtn, Sort_Name);
ui->sortingModeGroup->setId(ui->sortLastLaunchedBtn, Sort_LastLaunch); ui->sortingModeGroup->setId(ui->sortLastLaunchedBtn, Sort_LastLaunch);
@ -80,8 +79,9 @@ LauncherPage::LauncherPage(QWidget* parent) : QWidget(parent), ui(new Ui::Launch
ui->updateSettingsBox->setHidden(!APPLICATION->updater()); ui->updateSettingsBox->setHidden(!APPLICATION->updater());
connect(ui->fontSizeBox, SIGNAL(valueChanged(int)), SLOT(refreshFontPreview())); connect(ui->fontSizeBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &LauncherPage::refreshFontPreview);
connect(ui->consoleFont, SIGNAL(currentFontChanged(QFont)), SLOT(refreshFontPreview())); connect(ui->consoleFont, &QFontComboBox::currentFontChanged, this, &LauncherPage::refreshFontPreview);
connect(ui->themeCustomizationWidget, &ThemeCustomizationWidget::currentWidgetThemeChanged, this, &LauncherPage::refreshFontPreview);
connect(ui->themeCustomizationWidget, &ThemeCustomizationWidget::currentCatChanged, APPLICATION, &Application::currentCatChanged); connect(ui->themeCustomizationWidget, &ThemeCustomizationWidget::currentCatChanged, APPLICATION, &Application::currentCatChanged);
} }
@ -323,37 +323,47 @@ void LauncherPage::loadSettings()
void LauncherPage::refreshFontPreview() void LauncherPage::refreshFontPreview()
{ {
const LogColors& colors = APPLICATION->themeManager()->getLogColors();
int fontSize = ui->fontSizeBox->value(); int fontSize = ui->fontSizeBox->value();
QString fontFamily = ui->consoleFont->currentFont().family(); QString fontFamily = ui->consoleFont->currentFont().family();
ui->fontPreview->clear(); ui->fontPreview->clear();
defaultFormat->setFont(QFont(fontFamily, fontSize)); defaultFormat->setFont(QFont(fontFamily, fontSize));
{
auto print = [this, colors](const QString& message, MessageLevel::Enum level) {
QTextCharFormat format(*defaultFormat); QTextCharFormat format(*defaultFormat);
format.setForeground(m_colors->getFront(MessageLevel::Error));
QColor bg = colors.background.value(level);
QColor fg = colors.foreground.value(level);
if (bg.isValid())
format.setBackground(bg);
if (fg.isValid())
format.setForeground(fg);
// append a paragraph/line // append a paragraph/line
auto workCursor = ui->fontPreview->textCursor(); auto workCursor = ui->fontPreview->textCursor();
workCursor.movePosition(QTextCursor::End); workCursor.movePosition(QTextCursor::End);
workCursor.insertText(tr("[Something/ERROR] A spooky error!"), format); workCursor.insertText(message, format);
workCursor.insertBlock(); workCursor.insertBlock();
} };
{
QTextCharFormat format(*defaultFormat); print(QString("%1 version: %2 (%3)\n")
format.setForeground(m_colors->getFront(MessageLevel::Message)); .arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM),
// append a paragraph/line MessageLevel::Launcher);
auto workCursor = ui->fontPreview->textCursor();
workCursor.movePosition(QTextCursor::End); QDate today = QDate::currentDate();
workCursor.insertText(tr("[Test/INFO] A harmless message..."), format);
workCursor.insertBlock(); if (today.month() == 10 && today.day() == 31)
} print(tr("[Test/ERROR] OOoooOOOoooo! A spooky error!"), MessageLevel::Error);
{ else
QTextCharFormat format(*defaultFormat); print(tr("[Test/ERROR] A spooky error!"), MessageLevel::Error);
format.setForeground(m_colors->getFront(MessageLevel::Warning));
// append a paragraph/line print(tr("[Test/INFO] A harmless message..."), MessageLevel::Info);
auto workCursor = ui->fontPreview->textCursor(); print(tr("[Test/WARN] A not so spooky warning."), MessageLevel::Warning);
workCursor.movePosition(QTextCursor::End); print(tr("[Test/DEBUG] A secret debugging message..."), MessageLevel::Debug);
workCursor.insertText(tr("[Something/WARN] A not so spooky warning."), format); print(tr("[Test/FATAL] A terrifying fatal error!"), MessageLevel::Fatal);
workCursor.insertBlock();
}
} }
void LauncherPage::retranslate() void LauncherPage::retranslate()

View File

@ -41,7 +41,6 @@
#include <Application.h> #include <Application.h>
#include <translations/TranslationsModel.h> #include <translations/TranslationsModel.h>
#include "java/JavaChecker.h" #include "java/JavaChecker.h"
#include "ui/ColorCache.h"
#include "ui/pages/BasePage.h" #include "ui/pages/BasePage.h"
class QTextCharFormat; class QTextCharFormat;
@ -94,7 +93,5 @@ class LauncherPage : public QWidget, public BasePage {
// default format for the font preview... // default format for the font preview...
QTextCharFormat* defaultFormat; QTextCharFormat* defaultFormat;
std::unique_ptr<LogColorCache> m_colors;
std::shared_ptr<TranslationsModel> m_languageModel; std::shared_ptr<TranslationsModel> m_languageModel;
}; };

View File

@ -3,7 +3,7 @@
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> * Copyright (C) 2024 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -47,8 +47,8 @@
#include "launch/LaunchTask.h" #include "launch/LaunchTask.h"
#include "settings/Setting.h" #include "settings/Setting.h"
#include "ui/ColorCache.h"
#include "ui/GuiUtil.h" #include "ui/GuiUtil.h"
#include "ui/themes/ThemeManager.h"
#include <BuildConfig.h> #include <BuildConfig.h>
@ -57,26 +57,36 @@ class LogFormatProxyModel : public QIdentityProxyModel {
LogFormatProxyModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {} LogFormatProxyModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {}
QVariant data(const QModelIndex& index, int role) const override QVariant data(const QModelIndex& index, int role) const override
{ {
const LogColors& colors = APPLICATION->themeManager()->getLogColors();
switch (role) { switch (role) {
case Qt::FontRole: case Qt::FontRole:
return m_font; return m_font;
case Qt::ForegroundRole: { case Qt::ForegroundRole: {
MessageLevel::Enum level = (MessageLevel::Enum)QIdentityProxyModel::data(index, LogModel::LevelRole).toInt(); auto level = static_cast<MessageLevel::Enum>(QIdentityProxyModel::data(index, LogModel::LevelRole).toInt());
return m_colors->getFront(level); QColor result = colors.foreground.value(level);
if (result.isValid())
return result;
break;
} }
case Qt::BackgroundRole: { case Qt::BackgroundRole: {
MessageLevel::Enum level = (MessageLevel::Enum)QIdentityProxyModel::data(index, LogModel::LevelRole).toInt(); auto level = static_cast<MessageLevel::Enum>(QIdentityProxyModel::data(index, LogModel::LevelRole).toInt());
return m_colors->getBack(level); QColor result = colors.background.value(level);
if (result.isValid())
return result;
break;
} }
default:
return QIdentityProxyModel::data(index, role);
} }
return QIdentityProxyModel::data(index, role);
} }
void setFont(QFont font) { m_font = font; } void setFont(QFont font) { m_font = font; }
void setColors(LogColorCache* colors) { m_colors.reset(colors); }
QModelIndex find(const QModelIndex& start, const QString& value, bool reverse) const QModelIndex find(const QModelIndex& start, const QString& value, bool reverse) const
{ {
QModelIndex parentIndex = parent(start); QModelIndex parentIndex = parent(start);
@ -125,7 +135,6 @@ class LogFormatProxyModel : public QIdentityProxyModel {
private: private:
QFont m_font; QFont m_font;
std::unique_ptr<LogColorCache> m_colors;
}; };
LogPage::LogPage(InstancePtr instance, QWidget* parent) : QWidget(parent), ui(new Ui::LogPage), m_instance(instance) LogPage::LogPage(InstancePtr instance, QWidget* parent) : QWidget(parent), ui(new Ui::LogPage), m_instance(instance)
@ -134,12 +143,6 @@ LogPage::LogPage(InstancePtr instance, QWidget* parent) : QWidget(parent), ui(ne
ui->tabWidget->tabBar()->hide(); ui->tabWidget->tabBar()->hide();
m_proxy = new LogFormatProxyModel(this); m_proxy = new LogFormatProxyModel(this);
// set up text colors in the log proxy and adapt them to the current theme foreground and background
{
auto origForeground = ui->text->palette().color(ui->text->foregroundRole());
auto origBackground = ui->text->palette().color(ui->text->backgroundRole());
m_proxy->setColors(new LogColorCache(origForeground, origBackground));
}
// set up fonts in the log proxy // set up fonts in the log proxy
{ {

View File

@ -46,11 +46,6 @@ QString BrightTheme::name()
return QObject::tr("Bright"); return QObject::tr("Bright");
} }
bool BrightTheme::hasColorScheme()
{
return true;
}
QPalette BrightTheme::colorScheme() QPalette BrightTheme::colorScheme()
{ {
QPalette brightPalette; QPalette brightPalette;

View File

@ -45,7 +45,6 @@ class BrightTheme : public FusionTheme {
QString tooltip() override; QString tooltip() override;
bool hasStyleSheet() override; bool hasStyleSheet() override;
QString appStyleSheet() override; QString appStyleSheet() override;
bool hasColorScheme() override;
QPalette colorScheme() override; QPalette colorScheme() override;
double fadeAmount() override; double fadeAmount() override;
QColor fadeColor() override; QColor fadeColor() override;

View File

@ -2,6 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2024 Tayou <git@tayou.org> * Copyright (C) 2024 Tayou <git@tayou.org>
* Copyright (C) 2024 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -39,121 +40,6 @@
const char* themeFile = "theme.json"; const char* themeFile = "theme.json";
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 {
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 colorValue = Json::ensureString(colorsRoot, colorName, QString());
if (!colorValue.isEmpty()) {
QColor color(colorValue);
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 color = readColor(colorName);
if (color.isValid()) {
palette.setColor(role, color);
} else {
themeDebugLog() << "Color value for" << colorName << "was not present.";
}
};
// palette
readAndSetColor(QPalette::Window, "Window");
readAndSetColor(QPalette::WindowText, "WindowText");
readAndSetColor(QPalette::Base, "Base");
readAndSetColor(QPalette::AlternateBase, "AlternateBase");
readAndSetColor(QPalette::ToolTipBase, "ToolTipBase");
readAndSetColor(QPalette::ToolTipText, "ToolTipText");
readAndSetColor(QPalette::Text, "Text");
readAndSetColor(QPalette::Button, "Button");
readAndSetColor(QPalette::ButtonText, "ButtonText");
readAndSetColor(QPalette::BrightText, "BrightText");
readAndSetColor(QPalette::Link, "Link");
readAndSetColor(QPalette::Highlight, "Highlight");
readAndSetColor(QPalette::HighlightedText, "HighlightedText");
// fade
fadeColor = readColor("fadeColor");
fadeAmount = Json::ensureDouble(colorsRoot, "fadeAmount", 0.5, "fade amount");
} catch (const Exception& e) {
themeWarningLog() << "Couldn't load theme json: " << e.cause();
return false;
}
} 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,
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()); };
// palette
insertColor(QPalette::Window, "Window");
insertColor(QPalette::WindowText, "WindowText");
insertColor(QPalette::Base, "Base");
insertColor(QPalette::AlternateBase, "AlternateBase");
insertColor(QPalette::ToolTipBase, "ToolTipBase");
insertColor(QPalette::ToolTipText, "ToolTipText");
insertColor(QPalette::Text, "Text");
insertColor(QPalette::Button, "Button");
insertColor(QPalette::ButtonText, "ButtonText");
insertColor(QPalette::BrightText, "BrightText");
insertColor(QPalette::Link, "Link");
insertColor(QPalette::Highlight, "Highlight");
insertColor(QPalette::HighlightedText, "HighlightedText");
// fade
colorsObj.insert("fadeColor", fadeColor.name());
colorsObj.insert("fadeAmount", fadeAmount);
rootObj.insert("colors", colorsObj);
try {
Json::write(rootObj, path);
return true;
} catch ([[maybe_unused]] const Exception& e) {
themeWarningLog() << "Failed to write theme json to" << path;
return false;
}
}
/// @param baseTheme Base Theme /// @param baseTheme Base Theme
/// @param fileInfo FileInfo object for file to load /// @param fileInfo FileInfo object for file to load
/// @param isManifest whether to load a theme manifest or a qss file /// @param isManifest whether to load a theme manifest or a qss file
@ -176,23 +62,22 @@ CustomTheme::CustomTheme(ITheme* baseTheme, QFileInfo& fileInfo, bool isManifest
auto themeFilePath = FS::PathCombine(path, themeFile); auto themeFilePath = FS::PathCombine(path, themeFile);
bool jsonDataIncomplete = false;
m_palette = baseTheme->colorScheme(); m_palette = baseTheme->colorScheme();
if (readThemeJson(themeFilePath, m_palette, m_fadeAmount, m_fadeColor, m_name, m_widgets, m_qssFilePath, jsonDataIncomplete)) {
bool hasCustomLogColors = false;
if (read(themeFilePath, hasCustomLogColors)) {
// If theme data was found, fade "Disabled" color of each role according to FadeAmount // If theme data was found, fade "Disabled" color of each role according to FadeAmount
m_palette = fadeInactive(m_palette, m_fadeAmount, m_fadeColor); m_palette = fadeInactive(m_palette, m_fadeAmount, m_fadeColor);
if (!hasCustomLogColors)
m_logColors = defaultLogColors(m_palette);
} else { } else {
themeDebugLog() << "Did not read theme json file correctly, not changing theme, keeping previous."; themeDebugLog() << "Did not read theme json file correctly, not changing theme, keeping previous.";
m_logColors = defaultLogColors(m_palette);
return; return;
} }
// FIXME: This is kinda jank, it only actually checks if the qss file path is not present. It should actually check for any relevant
// missing data (e.g. name, colors)
if (jsonDataIncomplete) {
writeThemeJson(fileInfo.absoluteFilePath(), m_palette, m_fadeAmount, m_fadeColor, m_name, m_widgets, m_qssFilePath);
}
auto qssFilePath = FS::PathCombine(path, m_qssFilePath); auto qssFilePath = FS::PathCombine(path, m_qssFilePath);
QFileInfo info(qssFilePath); QFileInfo info(qssFilePath);
if (info.isFile()) { if (info.isFile()) {
@ -251,11 +136,6 @@ QString CustomTheme::name()
return m_name; return m_name;
} }
bool CustomTheme::hasColorScheme()
{
return true;
}
QPalette CustomTheme::colorScheme() QPalette CustomTheme::colorScheme()
{ {
return m_palette; return m_palette;
@ -289,3 +169,99 @@ QString CustomTheme::tooltip()
{ {
return m_tooltip; return m_tooltip;
} }
bool CustomTheme::read(const QString& path, bool& hasCustomLogColors)
{
QFileInfo pathInfo(path);
if (pathInfo.exists() && pathInfo.isFile()) {
try {
auto doc = Json::requireDocument(path, "Theme JSON file");
const QJsonObject root = doc.object();
m_name = Json::requireString(root, "name", "Theme name");
m_widgets = Json::requireString(root, "widgets", "Qt widget theme");
m_qssFilePath = Json::ensureString(root, "qssFilePath", "themeStyle.css");
auto readColor = [&](const QJsonObject& colors, const QString& colorName) -> QColor {
auto colorValue = Json::ensureString(colors, colorName, QString());
if (!colorValue.isEmpty()) {
QColor color(colorValue);
if (!color.isValid()) {
themeWarningLog() << "Color value" << colorValue << "for" << colorName << "was not recognized.";
return {};
}
return color;
}
return {};
};
if (root.contains("colors")) {
auto colorsRoot = Json::requireObject(root, "colors");
auto readAndSetPaletteColor = [&](QPalette::ColorRole role, const QString& colorName) {
auto color = readColor(colorsRoot, colorName);
if (color.isValid()) {
m_palette.setColor(role, color);
} else {
themeDebugLog() << "Color value for" << colorName << "was not present.";
}
};
// palette
readAndSetPaletteColor(QPalette::Window, "Window");
readAndSetPaletteColor(QPalette::WindowText, "WindowText");
readAndSetPaletteColor(QPalette::Base, "Base");
readAndSetPaletteColor(QPalette::AlternateBase, "AlternateBase");
readAndSetPaletteColor(QPalette::ToolTipBase, "ToolTipBase");
readAndSetPaletteColor(QPalette::ToolTipText, "ToolTipText");
readAndSetPaletteColor(QPalette::Text, "Text");
readAndSetPaletteColor(QPalette::Button, "Button");
readAndSetPaletteColor(QPalette::ButtonText, "ButtonText");
readAndSetPaletteColor(QPalette::BrightText, "BrightText");
readAndSetPaletteColor(QPalette::Link, "Link");
readAndSetPaletteColor(QPalette::Highlight, "Highlight");
readAndSetPaletteColor(QPalette::HighlightedText, "HighlightedText");
// fade
m_fadeColor = readColor(colorsRoot, "fadeColor");
m_fadeAmount = Json::ensureDouble(colorsRoot, "fadeAmount", 0.5, "fade amount");
}
if (root.contains("logColors")) {
hasCustomLogColors = true;
auto logColorsRoot = Json::requireObject(root, "logColors");
auto readAndSetLogColor = [&](MessageLevel::Enum level, bool fg, const QString& colorName) {
auto color = readColor(logColorsRoot, colorName);
if (color.isValid()) {
if (fg)
m_logColors.foreground[level] = color;
else
m_logColors.background[level] = color;
} else {
themeDebugLog() << "Color value for" << colorName << "was not present.";
}
};
readAndSetLogColor(MessageLevel::Message, false, "MessageHighlight");
readAndSetLogColor(MessageLevel::Launcher, false, "LauncherHighlight");
readAndSetLogColor(MessageLevel::Debug, false, "DebugHighlight");
readAndSetLogColor(MessageLevel::Warning, false, "WarningHighlight");
readAndSetLogColor(MessageLevel::Error, false, "ErrorHighlight");
readAndSetLogColor(MessageLevel::Fatal, false, "FatalHighlight");
readAndSetLogColor(MessageLevel::Message, true, "Message");
readAndSetLogColor(MessageLevel::Launcher, true, "Launcher");
readAndSetLogColor(MessageLevel::Debug, true, "Debug");
readAndSetLogColor(MessageLevel::Warning, true, "Warning");
readAndSetLogColor(MessageLevel::Error, true, "Error");
readAndSetLogColor(MessageLevel::Fatal, true, "Fatal");
}
} catch (const Exception& e) {
themeWarningLog() << "Couldn't load theme json: " << e.cause();
return false;
}
} else {
themeDebugLog() << "No theme json present.";
return false;
}
return true;
}

View File

@ -2,6 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2024 Tayou <git@tayou.org> * Copyright (C) 2024 Tayou <git@tayou.org>
* Copyright (C) 2024 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -47,14 +48,16 @@ class CustomTheme : public ITheme {
QString tooltip() override; QString tooltip() override;
bool hasStyleSheet() override; bool hasStyleSheet() override;
QString appStyleSheet() override; QString appStyleSheet() override;
bool hasColorScheme() override;
QPalette colorScheme() override; QPalette colorScheme() override;
double fadeAmount() override; double fadeAmount() override;
QColor fadeColor() override; QColor fadeColor() override;
QString qtTheme() override; QString qtTheme() override;
LogColors logColorScheme() override { return m_logColors; }
QStringList searchPaths() override; QStringList searchPaths() override;
private: /* data */ private:
bool read(const QString& path, bool& hasCustomLogColors);
QPalette m_palette; QPalette m_palette;
QColor m_fadeColor; QColor m_fadeColor;
double m_fadeAmount; double m_fadeAmount;
@ -63,6 +66,7 @@ class CustomTheme : public ITheme {
QString m_id; QString m_id;
QString m_widgets; QString m_widgets;
QString m_qssFilePath; QString m_qssFilePath;
LogColors m_logColors;
/** /**
* The tooltip could be defined in the theme json, * The tooltip could be defined in the theme json,
* or composed of other fields that could be in there. * or composed of other fields that could be in there.

View File

@ -2,6 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2024 Tayou <git@tayou.org> * Copyright (C) 2024 Tayou <git@tayou.org>
* Copyright (C) 2024 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -46,11 +47,6 @@ QString DarkTheme::name()
return QObject::tr("Dark"); return QObject::tr("Dark");
} }
bool DarkTheme::hasColorScheme()
{
return true;
}
QPalette DarkTheme::colorScheme() QPalette DarkTheme::colorScheme()
{ {
QPalette darkPalette; QPalette darkPalette;
@ -90,6 +86,7 @@ QString DarkTheme::appStyleSheet()
{ {
return "QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }"; return "QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }";
} }
QString DarkTheme::tooltip() QString DarkTheme::tooltip()
{ {
return ""; return "";

View File

@ -45,7 +45,6 @@ class DarkTheme : public FusionTheme {
QString tooltip() override; QString tooltip() override;
bool hasStyleSheet() override; bool hasStyleSheet() override;
QString appStyleSheet() override; QString appStyleSheet() override;
bool hasColorScheme() override;
QPalette colorScheme() override; QPalette colorScheme() override;
double fadeAmount() override; double fadeAmount() override;
QColor fadeColor() override; QColor fadeColor() override;

View File

@ -44,9 +44,7 @@ void ITheme::apply(bool)
{ {
APPLICATION->setStyleSheet(QString()); APPLICATION->setStyleSheet(QString());
QApplication::setStyle(new HintOverrideProxyStyle(QStyleFactory::create(qtTheme()))); QApplication::setStyle(new HintOverrideProxyStyle(QStyleFactory::create(qtTheme())));
if (hasColorScheme()) { QApplication::setPalette(colorScheme());
QApplication::setPalette(colorScheme());
}
APPLICATION->setStyleSheet(appStyleSheet()); APPLICATION->setStyleSheet(appStyleSheet());
QDir::setSearchPaths("theme", searchPaths()); QDir::setSearchPaths("theme", searchPaths());
} }
@ -73,3 +71,30 @@ QPalette ITheme::fadeInactive(QPalette in, qreal bias, QColor color)
blend(QPalette::HighlightedText); blend(QPalette::HighlightedText);
return in; return in;
} }
LogColors ITheme::defaultLogColors(const QPalette& palette)
{
LogColors result;
const QColor& bg = palette.color(QPalette::Base);
const QColor& fg = palette.color(QPalette::Text);
auto blend = [bg, fg](QColor color) {
if (Rainbow::luma(fg) > Rainbow::luma(bg)) {
// for dark color schemes, produce a fitting color first
color = Rainbow::tint(fg, color, 0.5);
}
// adapt contrast
return Rainbow::mix(fg, color, 1);
};
result.background[MessageLevel::Fatal] = Qt::black;
result.foreground[MessageLevel::Launcher] = blend(QColor("purple"));
result.foreground[MessageLevel::Debug] = blend(QColor("green"));
result.foreground[MessageLevel::Warning] = blend(QColor("orange"));
result.foreground[MessageLevel::Error] = blend(QColor("red"));
result.foreground[MessageLevel::Fatal] = blend(QColor("red"));
return result;
}

View File

@ -2,6 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Tayou <git@tayou.org> * Copyright (C) 2022 Tayou <git@tayou.org>
* Copyright (C) 2024 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -33,11 +34,19 @@
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <MessageLevel.h>
#include <QMap>
#include <QPalette> #include <QPalette>
#include <QString> #include <QString>
class QStyle; class QStyle;
struct LogColors {
QMap<MessageLevel::Enum, QColor> background;
QMap<MessageLevel::Enum, QColor> foreground;
};
// TODO: rename to Theme; this is not an interface as it contains method implementations
class ITheme { class ITheme {
public: public:
virtual ~ITheme() {} virtual ~ITheme() {}
@ -48,11 +57,12 @@ class ITheme {
virtual bool hasStyleSheet() = 0; virtual bool hasStyleSheet() = 0;
virtual QString appStyleSheet() = 0; virtual QString appStyleSheet() = 0;
virtual QString qtTheme() = 0; virtual QString qtTheme() = 0;
virtual bool hasColorScheme() = 0;
virtual QPalette colorScheme() = 0; virtual QPalette colorScheme() = 0;
virtual QColor fadeColor() = 0; virtual QColor fadeColor() = 0;
virtual double fadeAmount() = 0; virtual double fadeAmount() = 0;
virtual LogColors logColorScheme() { return defaultLogColors(colorScheme()); }
virtual QStringList searchPaths() { return {}; } virtual QStringList searchPaths() { return {}; }
static QPalette fadeInactive(QPalette in, qreal bias, QColor color); static QPalette fadeInactive(QPalette in, qreal bias, QColor color);
static LogColors defaultLogColors(const QPalette& palette);
}; };

View File

@ -125,8 +125,3 @@ bool SystemTheme::hasStyleSheet()
{ {
return false; return false;
} }
bool SystemTheme::hasColorScheme()
{
return true;
}

View File

@ -48,7 +48,6 @@ class SystemTheme : public ITheme {
QString qtTheme() override; QString qtTheme() override;
bool hasStyleSheet() override; bool hasStyleSheet() override;
QString appStyleSheet() override; QString appStyleSheet() override;
bool hasColorScheme() override;
QPalette colorScheme() override; QPalette colorScheme() override;
double fadeAmount() override; double fadeAmount() override;
QColor fadeColor() override; QColor fadeColor() override;

View File

@ -123,11 +123,11 @@ void ThemeManager::initializeWidgets()
{ {
themeDebugLog() << "Determining System Widget Theme..."; themeDebugLog() << "Determining System Widget Theme...";
const auto& style = QApplication::style(); const auto& style = QApplication::style();
currentlySelectedSystemTheme = style->objectName(); m_currentlySelectedSystemTheme = style->objectName();
themeDebugLog() << "System theme seems to be:" << currentlySelectedSystemTheme; themeDebugLog() << "System theme seems to be:" << m_currentlySelectedSystemTheme;
themeDebugLog() << "<> Initializing Widget Themes"; themeDebugLog() << "<> Initializing Widget Themes";
themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique<SystemTheme>(currentlySelectedSystemTheme, true)); themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique<SystemTheme>(m_currentlySelectedSystemTheme, true));
auto darkThemeId = addTheme(std::make_unique<DarkTheme>()); auto darkThemeId = addTheme(std::make_unique<DarkTheme>());
themeDebugLog() << "Loading Built-in Theme:" << darkThemeId; themeDebugLog() << "Loading Built-in Theme:" << darkThemeId;
themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique<BrightTheme>()); themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique<BrightTheme>());
@ -196,8 +196,8 @@ QList<ITheme*> ThemeManager::getValidApplicationThemes()
QList<CatPack*> ThemeManager::getValidCatPacks() QList<CatPack*> ThemeManager::getValidCatPacks()
{ {
QList<CatPack*> ret; QList<CatPack*> ret;
ret.reserve(m_cat_packs.size()); ret.reserve(m_catPacks.size());
for (auto&& [id, theme] : m_cat_packs) { for (auto&& [id, theme] : m_catPacks) {
ret.append(theme.get()); ret.append(theme.get());
} }
return ret; return ret;
@ -246,6 +246,8 @@ void ThemeManager::setApplicationTheme(const QString& name, bool initial)
auto& theme = themeIter->second; auto& theme = themeIter->second;
themeDebugLog() << "applying theme" << theme->name(); themeDebugLog() << "applying theme" << theme->name();
theme->apply(initial); theme->apply(initial);
m_logColors = theme->logColorScheme();
} else { } else {
themeWarningLog() << "Tried to set invalid theme:" << name; themeWarningLog() << "Tried to set invalid theme:" << name;
} }
@ -258,7 +260,7 @@ void ThemeManager::applyCurrentlySelectedTheme(bool initial)
themeDebugLog() << "<> Icon theme set."; themeDebugLog() << "<> Icon theme set.";
auto applicationTheme = settings->get("ApplicationTheme").toString(); auto applicationTheme = settings->get("ApplicationTheme").toString();
if (applicationTheme == "") { if (applicationTheme == "") {
applicationTheme = currentlySelectedSystemTheme; applicationTheme = m_currentlySelectedSystemTheme;
} }
setApplicationTheme(applicationTheme, initial); setApplicationTheme(applicationTheme, initial);
themeDebugLog() << "<> Application theme set."; themeDebugLog() << "<> Application theme set.";
@ -266,8 +268,8 @@ void ThemeManager::applyCurrentlySelectedTheme(bool initial)
QString ThemeManager::getCatPack(QString catName) QString ThemeManager::getCatPack(QString catName)
{ {
auto catIter = m_cat_packs.find(!catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString()); auto catIter = m_catPacks.find(!catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString());
if (catIter != m_cat_packs.end()) { if (catIter != m_catPacks.end()) {
auto& catPack = catIter->second; auto& catPack = catIter->second;
themeDebugLog() << "applying catpack" << catPack->id(); themeDebugLog() << "applying catpack" << catPack->id();
return catPack->path(); return catPack->path();
@ -275,14 +277,14 @@ QString ThemeManager::getCatPack(QString catName)
themeWarningLog() << "Tried to get invalid catPack:" << catName; themeWarningLog() << "Tried to get invalid catPack:" << catName;
} }
return m_cat_packs.begin()->second->path(); return m_catPacks.begin()->second->path();
} }
QString ThemeManager::addCatPack(std::unique_ptr<CatPack> catPack) QString ThemeManager::addCatPack(std::unique_ptr<CatPack> catPack)
{ {
QString id = catPack->id(); QString id = catPack->id();
if (m_cat_packs.find(id) == m_cat_packs.end()) if (m_catPacks.find(id) == m_catPacks.end())
m_cat_packs.emplace(id, std::move(catPack)); m_catPacks.emplace(id, std::move(catPack));
else else
themeWarningLog() << "CatPack(" << id << ") not added to prevent id duplication"; themeWarningLog() << "CatPack(" << id << ") not added to prevent id duplication";
return id; return id;
@ -340,8 +342,8 @@ void ThemeManager::refresh()
{ {
m_themes.clear(); m_themes.clear();
m_icons.clear(); m_icons.clear();
m_cat_packs.clear(); m_catPacks.clear();
initializeThemes(); initializeThemes();
initializeCatPacks(); initializeCatPacks();
}; }

View File

@ -2,7 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2024 Tayou <git@tayou.org> * Copyright (C) 2024 Tayou <git@tayou.org>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * Copyright (C) 2024 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -55,6 +55,8 @@ class ThemeManager {
QString getCatPack(QString catName = ""); QString getCatPack(QString catName = "");
QList<CatPack*> getValidCatPacks(); QList<CatPack*> getValidCatPacks();
const LogColors& getLogColors() { return m_logColors; }
void refresh(); void refresh();
private: private:
@ -63,8 +65,9 @@ class ThemeManager {
QDir m_iconThemeFolder{ "iconthemes" }; QDir m_iconThemeFolder{ "iconthemes" };
QDir m_applicationThemeFolder{ "themes" }; QDir m_applicationThemeFolder{ "themes" };
QDir m_catPacksFolder{ "catpacks" }; QDir m_catPacksFolder{ "catpacks" };
std::map<QString, std::unique_ptr<CatPack>> m_cat_packs; std::map<QString, std::unique_ptr<CatPack>> m_catPacks;
QString currentlySelectedSystemTheme; QString m_currentlySelectedSystemTheme;
LogColors m_logColors;
void initializeThemes(); void initializeThemes();
void initializeCatPacks(); void initializeCatPacks();

View File

@ -105,14 +105,14 @@ bool VersionSelectWidget::eventFilter(QObject* watched, QEvent* event)
return QObject::eventFilter(watched, event); return QObject::eventFilter(watched, event);
} }
void VersionSelectWidget::initialize(BaseVersionList* vlist) void VersionSelectWidget::initialize(BaseVersionList* vlist, bool forceLoad)
{ {
m_vlist = vlist; m_vlist = vlist;
m_proxyModel->setSourceModel(vlist); m_proxyModel->setSourceModel(vlist);
listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
if (!m_vlist->isLoaded()) { if (!m_vlist->isLoaded() || forceLoad) {
loadList(); loadList();
} else { } else {
if (m_proxyModel->rowCount() == 0) { if (m_proxyModel->rowCount() == 0) {

View File

@ -54,7 +54,7 @@ class VersionSelectWidget : public QWidget {
~VersionSelectWidget(); ~VersionSelectWidget();
//! loads the list if needed. //! loads the list if needed.
void initialize(BaseVersionList* vlist); void initialize(BaseVersionList* vlist, bool forceLoad = false);
//! Starts a task that loads the list. //! Starts a task that loads the list.
void loadList(); void loadList();