mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-25 16:13:54 -07:00
Merge 3de441a823 into 80426d77bc
This commit is contained in:
commit
bf6834b953
15 changed files with 524 additions and 104 deletions
|
|
@ -19,13 +19,15 @@
|
|||
PaletteEditorDialog::PaletteEditorDialog(const QString &_themeDirPath, const QString &_themeName, QWidget *parent)
|
||||
: QDialog(parent), themeDirPath(_themeDirPath), themeName(_themeName)
|
||||
{
|
||||
userThemeDirPath = ThemeManager::userThemeDirFor(themeName);
|
||||
|
||||
setMinimumSize(740, 220);
|
||||
setupUi();
|
||||
|
||||
// Load both scheme configs upfront so switching is instant
|
||||
loadSchemes();
|
||||
|
||||
loadedScheme = themeManager->isDarkMode(themeDirPath) ? "Dark" : "Light";
|
||||
loadedScheme = themeManager->isDarkMode(themeDirPath, userThemeDirPath) ? "Dark" : "Light";
|
||||
|
||||
schemeComboBox->blockSignals(true);
|
||||
schemeComboBox->setCurrentText(loadedScheme);
|
||||
|
|
@ -33,7 +35,6 @@ PaletteEditorDialog::PaletteEditorDialog(const QString &_themeDirPath, const QSt
|
|||
|
||||
paletteGrid->loadPalette(workingConfig[loadedScheme]);
|
||||
seedAccentFromScheme(loadedScheme);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
|
|
@ -157,20 +158,22 @@ void PaletteEditorDialog::setupUi()
|
|||
});
|
||||
}
|
||||
|
||||
void PaletteEditorDialog::updateUserOverrideState()
|
||||
{
|
||||
const bool hasUserOverride =
|
||||
QFile::exists(QDir(userThemeDirPath).absoluteFilePath(PaletteConfig::fileName(loadedScheme)));
|
||||
|
||||
revertButton->setEnabled(hasUserOverride);
|
||||
revertButton->setToolTip(hasUserOverride ? tr("Delete your custom palette and restore the shipped defaults")
|
||||
: tr("No custom palette overrides exist for this scheme"));
|
||||
}
|
||||
|
||||
void PaletteEditorDialog::retranslateUi()
|
||||
{
|
||||
setWindowTitle(tr("Palette Editor — %1").arg(themeName));
|
||||
titleLabel->setText(tr("<b>Palette Editor</b> · %1").arg(themeName));
|
||||
|
||||
// Revert button only makes sense when the theme ships default palette files
|
||||
const bool hasDefault = PaletteConfig::fromDefault(themeDirPath, "Light").hasPalette() ||
|
||||
PaletteConfig::fromDefault(themeDirPath, "Dark").hasPalette();
|
||||
revertButton->setEnabled(hasDefault);
|
||||
if (!hasDefault) {
|
||||
revertButton->setToolTip(tr("This theme ships no default palette files"));
|
||||
} else {
|
||||
revertButton->setToolTip(tr("Replace current colours with the theme author's defaults"));
|
||||
}
|
||||
updateUserOverrideState();
|
||||
|
||||
schemeComboBox->setToolTip(tr("Switch between the light and dark palette files"));
|
||||
editingLabel->setText(tr("Editing:"));
|
||||
|
|
@ -195,8 +198,11 @@ void PaletteEditorDialog::loadSchemes()
|
|||
{
|
||||
const QStringList schemes = {"Light", "Dark"};
|
||||
for (const QString &scheme : schemes) {
|
||||
PaletteConfig cfg = PaletteConfig::fromScheme(themeDirPath, scheme);
|
||||
|
||||
// user customisation → system palette → system default → app palette
|
||||
PaletteConfig cfg = PaletteConfig::fromScheme(userThemeDirPath, scheme);
|
||||
if (!cfg.hasPalette()) {
|
||||
cfg = PaletteConfig::fromScheme(themeDirPath, scheme);
|
||||
}
|
||||
if (!cfg.hasPalette()) {
|
||||
cfg = PaletteConfig::fromDefault(themeDirPath, scheme);
|
||||
}
|
||||
|
|
@ -235,6 +241,9 @@ void PaletteEditorDialog::onSchemeChanged(const QString &scheme)
|
|||
loadedScheme = scheme;
|
||||
paletteGrid->loadPalette(workingConfig.value(scheme));
|
||||
seedAccentFromScheme(scheme);
|
||||
|
||||
updateUserOverrideState();
|
||||
|
||||
onApply();
|
||||
}
|
||||
|
||||
|
|
@ -258,15 +267,17 @@ void PaletteEditorDialog::onSave()
|
|||
|
||||
PaletteConfig cfg = paletteGrid->currentPaletteConfig();
|
||||
|
||||
if (!ThemeManager::savePaletteConfig(themeDirPath, loadedScheme, cfg)) {
|
||||
QMessageBox::warning(this, tr("Save failed"),
|
||||
tr("Could not write %1 to:\n%2").arg(PaletteConfig::fileName(loadedScheme), themeDirPath));
|
||||
if (!ThemeManager::savePaletteConfig(userThemeDirPath, loadedScheme, cfg)) {
|
||||
QMessageBox::warning(
|
||||
this, tr("Save failed"),
|
||||
tr("Could not write %1 to:\n%2").arg(PaletteConfig::fileName(loadedScheme), userThemeDirPath));
|
||||
return;
|
||||
}
|
||||
|
||||
ThemeConfig globalCfg = ThemeConfig::fromThemeDir(themeDirPath);
|
||||
// Record the active scheme in the user dir — never touch the system (read-only) dir
|
||||
ThemeConfig globalCfg = themeManager->effectiveThemeConfig(themeName);
|
||||
globalCfg.colorScheme = loadedScheme;
|
||||
globalCfg.save(themeDirPath);
|
||||
globalCfg.save(userThemeDirPath);
|
||||
|
||||
savedConfig[loadedScheme] = cfg;
|
||||
workingConfig[loadedScheme] = cfg;
|
||||
|
|
@ -282,14 +293,50 @@ void PaletteEditorDialog::onReset()
|
|||
|
||||
void PaletteEditorDialog::onRevertToDefault()
|
||||
{
|
||||
PaletteConfig def = PaletteConfig::fromDefault(themeDirPath, loadedScheme);
|
||||
if (!def.hasPalette()) {
|
||||
QMessageBox::information(this, tr("No default found"),
|
||||
tr("No default palette file found for the \"%1\" scheme.").arg(loadedScheme));
|
||||
const QString filePath = QDir(userThemeDirPath).absoluteFilePath(PaletteConfig::fileName(loadedScheme));
|
||||
|
||||
if (!QFile::exists(filePath)) {
|
||||
// Button should already be disabled in this state, but be defensive
|
||||
return;
|
||||
}
|
||||
|
||||
const auto reply =
|
||||
QMessageBox::question(this, tr("Revert to default?"),
|
||||
tr("This will permanently delete your custom \"%1\" palette for \"%2\".\n\nContinue?")
|
||||
.arg(loadedScheme, themeName),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (reply != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile::remove(filePath);
|
||||
|
||||
// Reload via fallthrough: system palette → system default → app palette
|
||||
PaletteConfig def = PaletteConfig::fromScheme(themeDirPath, loadedScheme);
|
||||
if (!def.hasPalette()) {
|
||||
def = PaletteConfig::fromDefault(themeDirPath, loadedScheme);
|
||||
}
|
||||
if (!def.hasPalette()) {
|
||||
const QPalette appPal = qApp->palette();
|
||||
for (auto group : {QPalette::Active, QPalette::Disabled, QPalette::Inactive}) {
|
||||
for (int i = 0; i < QPalette::NColorRoles; ++i) {
|
||||
auto role = static_cast<QPalette::ColorRole>(i);
|
||||
if (role != QPalette::NoRole) {
|
||||
def.colors[group][role] = appPal.color(group, role);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
savedConfig[loadedScheme] = def;
|
||||
workingConfig[loadedScheme] = def;
|
||||
paletteGrid->loadPalette(def);
|
||||
|
||||
ThemeConfig globalCfg = themeManager->effectiveThemeConfig(themeName);
|
||||
globalCfg.colorScheme = loadedScheme;
|
||||
globalCfg.save(userThemeDirPath);
|
||||
|
||||
themeManager->reloadCurrentTheme();
|
||||
accept();
|
||||
}
|
||||
|
||||
void PaletteEditorDialog::changeEvent(QEvent *e)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ private slots:
|
|||
|
||||
private:
|
||||
void setupUi();
|
||||
void updateUserOverrideState();
|
||||
void retranslateUi();
|
||||
void refreshChromePalettes();
|
||||
void loadScheme(const QString &scheme); // snapshot current, switch, load
|
||||
|
|
@ -54,6 +55,7 @@ private:
|
|||
QPushButton *revertButton = nullptr;
|
||||
|
||||
// State
|
||||
QString userThemeDirPath;
|
||||
QString themeDirPath;
|
||||
QString themeName;
|
||||
QString loadedScheme;
|
||||
|
|
|
|||
|
|
@ -248,10 +248,6 @@ PaletteConfig PaletteConfig::fromDefault(const QString &themeDirPath, const QStr
|
|||
PaletteConfig cfg =
|
||||
fromFile(dir.absoluteFilePath(wantDark ? "palette-default-dark.toml" : "palette-default-light.toml"));
|
||||
|
||||
if (!cfg.hasPalette()) {
|
||||
cfg = fromFile(dir.absoluteFilePath(wantDark ? "palette-default-light.toml" : "palette-default-dark.toml"));
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,13 @@ ThemeManager::ThemeManager(QObject *parent) : QObject(parent)
|
|||
defaultStyleName = "windowsvista";
|
||||
}
|
||||
ensureThemeDirectoryExists();
|
||||
|
||||
// Capture the QPA-initialised palette before we ever call qApp->setPalette().
|
||||
// Once setPalette() is called, is_app_palette is locked true and neither
|
||||
// setStyle() nor colorSchemeChanged will update it automatically.
|
||||
// This snapshot is our guaranteed-clean base for applyStyleAndPalette.
|
||||
systemPalette = qApp->palette();
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
|
||||
connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, &ThemeManager::themeChangedSlot);
|
||||
#endif
|
||||
|
|
@ -113,21 +120,26 @@ void ThemeManager::ensureThemeDirectoryExists()
|
|||
}
|
||||
}
|
||||
|
||||
bool ThemeManager::isDarkMode(const QString &themeDirPath)
|
||||
bool ThemeManager::isDarkMode(const QString &themeDirPath, const QString &userDirPath)
|
||||
{
|
||||
ThemeConfig themeConfig = ThemeConfig::fromThemeDir(themeDirPath);
|
||||
if (themeConfig.colorScheme.compare("Dark", Qt::CaseInsensitive) == 0) {
|
||||
return true;
|
||||
} else if (themeConfig.colorScheme.compare("Light", Qt::CaseInsensitive) == 0) {
|
||||
return false;
|
||||
} else {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||
bool osDark = (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark);
|
||||
#else
|
||||
bool osDark = false;
|
||||
#endif
|
||||
return osDark;
|
||||
// User override takes precedence over system config
|
||||
for (const QString &path : {userDirPath, themeDirPath}) {
|
||||
if (path.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
ThemeConfig cfg = ThemeConfig::fromThemeDir(path);
|
||||
if (cfg.colorScheme.compare("Dark", Qt::CaseInsensitive) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (cfg.colorScheme.compare("Light", Qt::CaseInsensitive) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||
return (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ThemeManager::isBuiltInTheme()
|
||||
|
|
@ -137,45 +149,87 @@ bool ThemeManager::isBuiltInTheme()
|
|||
return themeName == NONE_THEME_NAME || themeName == FUSION_THEME_NAME;
|
||||
}
|
||||
|
||||
QString ThemeManager::userThemeDirFor(const QString &themeName)
|
||||
{
|
||||
return QDir(SettingsCache::instance().getThemesPath()).absoluteFilePath(themeName);
|
||||
}
|
||||
|
||||
QStringMap &ThemeManager::getAvailableThemes()
|
||||
{
|
||||
QDir dir;
|
||||
availableThemes.clear();
|
||||
|
||||
// load themes from user profile dir
|
||||
dir.setPath(SettingsCache::instance().getThemesPath());
|
||||
|
||||
// add default value
|
||||
availableThemes.insert(NONE_THEME_NAME, dir.absoluteFilePath("Default"));
|
||||
|
||||
availableThemes.insert(FUSION_THEME_NAME, dir.absoluteFilePath("Fusion"));
|
||||
|
||||
for (QString themeName : dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) {
|
||||
if (!availableThemes.contains(themeName)) {
|
||||
availableThemes.insert(themeName, dir.absoluteFilePath(themeName));
|
||||
}
|
||||
}
|
||||
|
||||
// load themes from cockatrice system dir
|
||||
dir.setPath(qApp->applicationDirPath() +
|
||||
// ── 1. System themes (read-only, shipped with the application) ──────────
|
||||
QDir sysDir(qApp->applicationDirPath() +
|
||||
#ifdef Q_OS_MAC
|
||||
"/../Resources/themes"
|
||||
#elif defined(Q_OS_WIN)
|
||||
"/themes"
|
||||
#else // linux
|
||||
#else
|
||||
"/../share/cockatrice/themes"
|
||||
#endif
|
||||
);
|
||||
for (const QString &name : sysDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) {
|
||||
availableThemes.insert(name, sysDir.absoluteFilePath(name));
|
||||
}
|
||||
|
||||
for (QString themeName : dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) {
|
||||
if (!availableThemes.contains(themeName)) {
|
||||
availableThemes.insert(themeName, dir.absoluteFilePath(themeName));
|
||||
// ── 2. User-only themes (AppData) ────────────────────────────────────────
|
||||
// We only add themes that don't already exist in the system directory.
|
||||
// Customisations to system themes are handled via the fallthrough read
|
||||
// logic (userThemeDirFor → system path); we intentionally keep the system
|
||||
// path in the map so shipped assets are always locatable.
|
||||
QDir userDir(SettingsCache::instance().getThemesPath());
|
||||
for (const QString &name : userDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) {
|
||||
if (!availableThemes.contains(name)) {
|
||||
availableThemes.insert(name, userDir.absoluteFilePath(name));
|
||||
}
|
||||
}
|
||||
|
||||
// ── 3. Ensure built-in sentinels always exist (dev builds without install)
|
||||
if (!availableThemes.contains(NONE_THEME_NAME)) {
|
||||
availableThemes.insert(NONE_THEME_NAME, userDir.absoluteFilePath("Default"));
|
||||
}
|
||||
if (!availableThemes.contains(FUSION_THEME_NAME)) {
|
||||
availableThemes.insert(FUSION_THEME_NAME, userDir.absoluteFilePath("Fusion"));
|
||||
}
|
||||
|
||||
return availableThemes;
|
||||
}
|
||||
|
||||
ThemeConfig ThemeManager::effectiveThemeConfig(const QString &themeName)
|
||||
{
|
||||
const QString dirPath = getAvailableThemes().value(themeName);
|
||||
const QString userDirPath = userThemeDirFor(themeName);
|
||||
|
||||
ThemeConfig userCfg = ThemeConfig::fromThemeDir(userDirPath);
|
||||
ThemeConfig systemCfg = ThemeConfig::fromThemeDir(dirPath);
|
||||
|
||||
ThemeConfig result = systemCfg;
|
||||
|
||||
// User values override system values on a per-field basis
|
||||
if (!userCfg.colorScheme.isEmpty()) {
|
||||
result.colorScheme = userCfg.colorScheme;
|
||||
}
|
||||
|
||||
if (!userCfg.styleName.isEmpty()) {
|
||||
result.styleName = userCfg.styleName;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ThemeManager::setStyleName(const QString &style)
|
||||
{
|
||||
const QString themeName = SettingsCache::instance().getThemeName();
|
||||
const QString userDirPath = userThemeDirFor(themeName);
|
||||
|
||||
ThemeConfig cfg = effectiveThemeConfig(themeName);
|
||||
|
||||
cfg.styleName = style;
|
||||
cfg.save(userDirPath);
|
||||
|
||||
reloadCurrentTheme();
|
||||
}
|
||||
|
||||
QBrush ThemeManager::loadBrush(QString fileName, QColor fallbackColor)
|
||||
{
|
||||
QBrush brush;
|
||||
|
|
@ -244,12 +298,14 @@ bool ThemeManager::savePaletteConfig(const QString &themeDirPath, const QString
|
|||
|
||||
void ThemeManager::setColorScheme(const QString &scheme)
|
||||
{
|
||||
const QString dirPath = getAvailableThemes().value(SettingsCache::instance().getThemeName());
|
||||
ThemeConfig cfg = ThemeConfig::fromThemeDir(dirPath);
|
||||
const QString themeName = SettingsCache::instance().getThemeName();
|
||||
const QString userDirPath = userThemeDirFor(themeName);
|
||||
|
||||
ThemeConfig cfg = effectiveThemeConfig(themeName);
|
||||
|
||||
cfg.colorScheme = scheme;
|
||||
cfg.save(userDirPath);
|
||||
|
||||
cfg.save(dirPath);
|
||||
reloadCurrentTheme();
|
||||
}
|
||||
|
||||
|
|
@ -261,8 +317,8 @@ void ThemeManager::reloadCurrentTheme()
|
|||
void ThemeManager::previewPalette(const PaletteConfig &cfg, const QString &scheme)
|
||||
{
|
||||
const QString themeName = SettingsCache::instance().getThemeName();
|
||||
const QString dirPath = getAvailableThemes().value(themeName);
|
||||
const ThemeConfig themeCfg = ThemeConfig::fromThemeDir(dirPath);
|
||||
|
||||
ThemeConfig themeCfg = effectiveThemeConfig(themeName);
|
||||
applyStyleAndPalette(themeName, themeCfg, cfg, scheme);
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +351,7 @@ void ThemeManager::applyStyleAndPalette(const QString &themeName,
|
|||
}
|
||||
#endif
|
||||
} else {
|
||||
base = qApp->palette();
|
||||
base = systemPalette;
|
||||
}
|
||||
|
||||
// Overlay custom palette colours
|
||||
|
|
@ -327,48 +383,54 @@ void ThemeManager::applyStyleAndPalette(const QString &themeName,
|
|||
|
||||
void ThemeManager::themeChangedSlot()
|
||||
{
|
||||
QString themeName = SettingsCache::instance().getThemeName();
|
||||
QString dirPath = getAvailableThemes().value(themeName);
|
||||
const QString themeName = SettingsCache::instance().getThemeName();
|
||||
const QString dirPath = getAvailableThemes().value(themeName); // system path
|
||||
const QString userDirPath = userThemeDirFor(themeName); // user override path
|
||||
currentThemePath = dirPath;
|
||||
QDir dir(dirPath);
|
||||
|
||||
// CSS
|
||||
if (!dirPath.isEmpty() && dir.exists(STYLE_CSS_NAME)) {
|
||||
qApp->setStyleSheet("file:///" + dir.absoluteFilePath(STYLE_CSS_NAME));
|
||||
} else {
|
||||
// CSS — user override first, then system
|
||||
const auto tryLoadCss = [](const QString &path) -> bool {
|
||||
QDir d(path);
|
||||
if (!path.isEmpty() && d.exists(STYLE_CSS_NAME)) {
|
||||
qApp->setStyleSheet("file:///" + d.absoluteFilePath(STYLE_CSS_NAME));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (!tryLoadCss(userDirPath) && !tryLoadCss(dirPath)) {
|
||||
qApp->setStyleSheet("");
|
||||
}
|
||||
|
||||
// load theme.cfg for style + scheme preference
|
||||
ThemeConfig themeCfg = ThemeConfig::fromThemeDir(dirPath);
|
||||
// ThemeConfig — user override first, then system
|
||||
ThemeConfig themeCfg = effectiveThemeConfig(themeName);
|
||||
|
||||
// Resolve active scheme:
|
||||
// theme.cfg says Dark/Light → use that
|
||||
// theme.cfg says System or is absent → follow the OS
|
||||
QString activeScheme = isDarkMode(dirPath) ? "Dark" : "Light";
|
||||
const QString activeScheme = isDarkMode(dirPath, userDirPath) ? "Dark" : "Light";
|
||||
|
||||
// ── Load palette: custom first, then theme default ────────────────────
|
||||
PaletteConfig palette = PaletteConfig::fromScheme(dirPath, activeScheme);
|
||||
// Palette — user customisation → system palette → system default
|
||||
PaletteConfig palette = PaletteConfig::fromScheme(userDirPath, activeScheme);
|
||||
if (!palette.hasPalette()) {
|
||||
palette = PaletteConfig::fromScheme(dirPath, activeScheme);
|
||||
}
|
||||
if (!palette.hasPalette()) {
|
||||
palette = PaletteConfig::fromDefault(dirPath, activeScheme);
|
||||
}
|
||||
|
||||
applyStyleAndPalette(themeName, themeCfg, palette, activeScheme);
|
||||
|
||||
// Search paths — user assets shadow system assets, both fall through to builtins
|
||||
QStringList resources;
|
||||
if (QDir(userDirPath).exists()) {
|
||||
resources << userDirPath;
|
||||
}
|
||||
if (!dirPath.isEmpty()) {
|
||||
resources << dir.absolutePath();
|
||||
resources << dirPath;
|
||||
}
|
||||
resources << DEFAULT_RESOURCE_PATHS;
|
||||
|
||||
QDir::setSearchPaths("theme", resources);
|
||||
|
||||
brushes[Role::Hand] = loadBrush(HANDZONE_BG_NAME, HANDZONE_BG_DEFAULT);
|
||||
|
||||
brushes[Role::Table] = loadBrush(TABLEZONE_BG_NAME, TABLEZONE_BG_DEFAULT);
|
||||
|
||||
brushes[Role::Player] = loadBrush(PLAYERZONE_BG_NAME, PLAYERZONE_BG_DEFAULT);
|
||||
|
||||
brushes[Role::Stack] = loadBrush(STACKZONE_BG_NAME, STACKZONE_BG_DEFAULT);
|
||||
for (auto &brushCache : brushesCache) {
|
||||
brushCache.clear();
|
||||
|
|
@ -379,6 +441,20 @@ void ThemeManager::themeChangedSlot()
|
|||
emit themeChanged();
|
||||
}
|
||||
|
||||
void ThemeManager::onColorSchemeChanged()
|
||||
{
|
||||
// qApp->palette() is locked (is_app_palette = true), so the QPA won't push
|
||||
// the new OS palette through automatically. style->polish(QPalette&) queries
|
||||
// GetSysColor on Windows — adequate for light mode, imperfect for Win11 dark
|
||||
// mode (which uses a different API), but better than a stale light snapshot.
|
||||
QPalette fresh;
|
||||
qApp->style()->polish(fresh);
|
||||
if (fresh.color(QPalette::Window).isValid()) {
|
||||
systemPalette = fresh;
|
||||
}
|
||||
themeChangedSlot();
|
||||
}
|
||||
|
||||
static QString roleBgName(ThemeManager::Role role)
|
||||
{
|
||||
switch (role) {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
QPalette systemPalette;
|
||||
QString defaultStyleName;
|
||||
QString currentThemePath;
|
||||
std::array<QBrush, Role::MaxRole + 1> brushes;
|
||||
|
|
@ -63,7 +64,11 @@ protected:
|
|||
public:
|
||||
bool isBuiltInTheme();
|
||||
bool isDarkMode(const QString &themeDirPath);
|
||||
static bool isDarkMode(const QString &themeDirPath, const QString &userDirPath = {});
|
||||
static QString userThemeDirFor(const QString &themeName);
|
||||
QStringMap &getAvailableThemes();
|
||||
ThemeConfig effectiveThemeConfig(const QString &themeName);
|
||||
void setStyleName(const QString &styleName);
|
||||
// Returns the path to the currently active theme directory (empty = default)
|
||||
QString getCurrentThemePath() const
|
||||
{
|
||||
|
|
@ -85,6 +90,7 @@ public:
|
|||
QBrush getExtraBgBrush(Role zone, int zoneId = 0);
|
||||
protected slots:
|
||||
void themeChangedSlot();
|
||||
void onColorSchemeChanged();
|
||||
signals:
|
||||
void themeChanged();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QDesktopServices>
|
||||
#include <QGridLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QStyleFactory>
|
||||
#include <QTimer>
|
||||
|
||||
AppearanceSettingsPage::AppearanceSettingsPage()
|
||||
|
|
@ -31,32 +32,33 @@ AppearanceSettingsPage::AppearanceSettingsPage()
|
|||
connect(&themeBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &AppearanceSettingsPage::themeBoxChanged);
|
||||
connect(&openThemeButton, &QPushButton::clicked, this, &AppearanceSettingsPage::openThemeLocation);
|
||||
|
||||
// Populate Scheme
|
||||
|
||||
schemeCombo.addItem(tr("Light"), QStringLiteral("Light"));
|
||||
schemeCombo.addItem(tr("Dark"), QStringLiteral("Dark"));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||
schemeCombo.addItem(tr("System"), QStringLiteral("System"));
|
||||
#endif
|
||||
|
||||
// Seed from whatever the current theme already has saved
|
||||
const QString dirPath = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName());
|
||||
const ThemeConfig cfg = ThemeConfig::fromThemeDir(dirPath);
|
||||
const QString current = cfg.colorScheme;
|
||||
const int seedIdx = schemeCombo.findData(current);
|
||||
schemeCombo.setCurrentIndex(seedIdx >= 0 ? seedIdx : 0);
|
||||
|
||||
connect(&schemeCombo, &QComboBox::currentIndexChanged, this,
|
||||
[this] { themeManager->setColorScheme(schemeCombo.currentData().toString()); });
|
||||
|
||||
connect(themeManager, &ThemeManager::themeChanged, this, [this, dirPath] {
|
||||
const QString newDir = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName());
|
||||
const ThemeConfig cfg = ThemeConfig::fromThemeDir(newDir);
|
||||
const QString current = cfg.colorScheme;
|
||||
// Populate Style
|
||||
|
||||
schemeCombo.blockSignals(true);
|
||||
const int idx = schemeCombo.findData(current);
|
||||
schemeCombo.setCurrentIndex(idx >= 0 ? idx : 0);
|
||||
schemeCombo.blockSignals(false);
|
||||
});
|
||||
styleComboBox.addItem(tr("Default"), QString());
|
||||
|
||||
for (const QString &style : QStyleFactory::keys()) {
|
||||
styleComboBox.addItem(style, style);
|
||||
}
|
||||
|
||||
connect(&styleComboBox, &QComboBox::currentIndexChanged, this,
|
||||
[this] { themeManager->setStyleName(styleComboBox.currentData().toString()); });
|
||||
|
||||
// Refresh theme and style
|
||||
|
||||
reloadThemeSettings();
|
||||
|
||||
connect(themeManager, &ThemeManager::themeChanged, this, &AppearanceSettingsPage::reloadThemeSettings);
|
||||
|
||||
connect(&editPaletteButton, &QPushButton::clicked, this, &AppearanceSettingsPage::editPalette);
|
||||
|
||||
|
|
@ -66,7 +68,9 @@ AppearanceSettingsPage::AppearanceSettingsPage()
|
|||
themeGrid->addWidget(&openThemeButton, 1, 1);
|
||||
themeGrid->addWidget(&schemeComboLabel, 2, 0);
|
||||
themeGrid->addWidget(&schemeCombo, 2, 1);
|
||||
themeGrid->addWidget(&editPaletteButton, 3, 1);
|
||||
themeGrid->addWidget(&styleLabel, 3, 0);
|
||||
themeGrid->addWidget(&styleComboBox, 3, 1);
|
||||
themeGrid->addWidget(&editPaletteButton, 4, 1);
|
||||
|
||||
themeGroupBox = new QGroupBox;
|
||||
themeGroupBox->setLayout(themeGrid);
|
||||
|
|
@ -320,6 +324,19 @@ void AppearanceSettingsPage::openThemeLocation()
|
|||
}
|
||||
}
|
||||
|
||||
void AppearanceSettingsPage::reloadThemeSettings()
|
||||
{
|
||||
const ThemeConfig cfg = themeManager->effectiveThemeConfig(SettingsCache::instance().getThemeName());
|
||||
|
||||
schemeCombo.blockSignals(true);
|
||||
schemeCombo.setCurrentIndex(std::max(0, schemeCombo.findData(cfg.colorScheme)));
|
||||
schemeCombo.blockSignals(false);
|
||||
|
||||
styleComboBox.blockSignals(true);
|
||||
styleComboBox.setCurrentIndex(qMax(0, styleComboBox.findData(cfg.styleName)));
|
||||
styleComboBox.blockSignals(false);
|
||||
}
|
||||
|
||||
void AppearanceSettingsPage::editPalette()
|
||||
{
|
||||
PaletteEditorDialog dlg(themeManager->getCurrentThemePath(), SettingsCache::instance().getThemeName(), this);
|
||||
|
|
@ -390,6 +407,7 @@ void AppearanceSettingsPage::retranslateUi()
|
|||
themeLabel.setText(tr("Current theme:"));
|
||||
openThemeButton.setText(tr("Open themes folder"));
|
||||
schemeComboLabel.setText(tr("Active theme palette:"));
|
||||
styleLabel.setText(tr("Active theme style:"));
|
||||
editPaletteButton.setText(tr("Edit theme palette"));
|
||||
|
||||
homeTabGroupBox->setTitle(tr("Home tab settings"));
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class AppearanceSettingsPage : public AbstractSettingsPage
|
|||
private slots:
|
||||
void themeBoxChanged(int index);
|
||||
void openThemeLocation();
|
||||
void reloadThemeSettings();
|
||||
void editPalette();
|
||||
void updateHomeTabSettingsVisibility();
|
||||
void showShortcutsChanged(QT_STATE_CHANGED_T enabled);
|
||||
|
|
@ -31,6 +32,8 @@ private:
|
|||
QPushButton openThemeButton;
|
||||
QLabel schemeComboLabel;
|
||||
QComboBox schemeCombo;
|
||||
QLabel styleLabel;
|
||||
QComboBox styleComboBox;
|
||||
QPushButton editPaletteButton;
|
||||
QLabel homeTabBackgroundSourceLabel;
|
||||
QComboBox homeTabBackgroundSourceBox;
|
||||
|
|
|
|||
63
cockatrice/themes/Fabric/palette-default-dark.toml
Normal file
63
cockatrice/themes/Fabric/palette-default-dark.toml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
[Palette]
|
||||
WindowText = #ffffffff
|
||||
Button = #ff383838
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ffffffff
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ffffffff
|
||||
Base = #ff2b2b2b
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ff000000
|
||||
Link = #ffcde4b6
|
||||
LinkVisited = #ff99d999
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #6effffff
|
||||
|
||||
[Palette.Disabled]
|
||||
WindowText = #ff9d9d9d
|
||||
Button = #ff1c1c1c
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ff9d9d9d
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ff787878
|
||||
Base = #ff1c1c1c
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ff9d9d9d
|
||||
Link = #ff308cc6
|
||||
LinkVisited = #ffb450ff
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #46ffffff
|
||||
|
||||
[Palette.Inactive]
|
||||
WindowText = #ffffffff
|
||||
Button = #ff383838
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ffffffff
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ffffffff
|
||||
Base = #ff2b2b2b
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ffffffff
|
||||
Link = #ffcde4b6
|
||||
LinkVisited = #ff99d999
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #6effffff
|
||||
|
||||
5
cockatrice/themes/Fabric/theme.cfg
Normal file
5
cockatrice/themes/Fabric/theme.cfg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[Appearance]
|
||||
ColorScheme = Light
|
||||
|
||||
[Style]
|
||||
Name = Default
|
||||
63
cockatrice/themes/Leather/palette-default-dark.toml
Normal file
63
cockatrice/themes/Leather/palette-default-dark.toml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
[Palette]
|
||||
WindowText = #ffffffff
|
||||
Button = #ff383838
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ffffffff
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ffffffff
|
||||
Base = #ff2b2b2b
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ff000000
|
||||
Link = #ffcde4b6
|
||||
LinkVisited = #ff99d999
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #6effffff
|
||||
|
||||
[Palette.Disabled]
|
||||
WindowText = #ff9d9d9d
|
||||
Button = #ff1c1c1c
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ff9d9d9d
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ff787878
|
||||
Base = #ff1c1c1c
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ff9d9d9d
|
||||
Link = #ff308cc6
|
||||
LinkVisited = #ffb450ff
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #46ffffff
|
||||
|
||||
[Palette.Inactive]
|
||||
WindowText = #ffffffff
|
||||
Button = #ff383838
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ffffffff
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ffffffff
|
||||
Base = #ff2b2b2b
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ffffffff
|
||||
Link = #ffcde4b6
|
||||
LinkVisited = #ff99d999
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #6effffff
|
||||
|
||||
5
cockatrice/themes/Leather/theme.cfg
Normal file
5
cockatrice/themes/Leather/theme.cfg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[Appearance]
|
||||
ColorScheme = Light
|
||||
|
||||
[Style]
|
||||
Name = Default
|
||||
63
cockatrice/themes/Plasma/palette-default-dark.toml
Normal file
63
cockatrice/themes/Plasma/palette-default-dark.toml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
[Palette]
|
||||
WindowText = #ffffffff
|
||||
Button = #ff383838
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ffffffff
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ffffffff
|
||||
Base = #ff2b2b2b
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ff000000
|
||||
Link = #ffcde4b6
|
||||
LinkVisited = #ff99d999
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #6effffff
|
||||
|
||||
[Palette.Disabled]
|
||||
WindowText = #ff9d9d9d
|
||||
Button = #ff1c1c1c
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ff9d9d9d
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ff787878
|
||||
Base = #ff1c1c1c
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ff9d9d9d
|
||||
Link = #ff308cc6
|
||||
LinkVisited = #ffb450ff
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #46ffffff
|
||||
|
||||
[Palette.Inactive]
|
||||
WindowText = #ffffffff
|
||||
Button = #ff383838
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ffffffff
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ffffffff
|
||||
Base = #ff2b2b2b
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ffffffff
|
||||
Link = #ffcde4b6
|
||||
LinkVisited = #ff99d999
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #6effffff
|
||||
|
||||
5
cockatrice/themes/Plasma/theme.cfg
Normal file
5
cockatrice/themes/Plasma/theme.cfg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[Appearance]
|
||||
ColorScheme = Light
|
||||
|
||||
[Style]
|
||||
Name = Default
|
||||
63
cockatrice/themes/VelvetMarble/palette-default-dark.toml
Normal file
63
cockatrice/themes/VelvetMarble/palette-default-dark.toml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
[Palette]
|
||||
WindowText = #ffffffff
|
||||
Button = #ff383838
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ffffffff
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ffffffff
|
||||
Base = #ff2b2b2b
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ff000000
|
||||
Link = #ffcde4b6
|
||||
LinkVisited = #ff99d999
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #6effffff
|
||||
|
||||
[Palette.Disabled]
|
||||
WindowText = #ff9d9d9d
|
||||
Button = #ff1c1c1c
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ff9d9d9d
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ff787878
|
||||
Base = #ff1c1c1c
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ff9d9d9d
|
||||
Link = #ff308cc6
|
||||
LinkVisited = #ffb450ff
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #46ffffff
|
||||
|
||||
[Palette.Inactive]
|
||||
WindowText = #ffffffff
|
||||
Button = #ff383838
|
||||
Light = #ff737373
|
||||
Midlight = #ff525252
|
||||
Dark = #ff161616
|
||||
Mid = #ff252525
|
||||
Text = #ffffffff
|
||||
BrightText = #ffb4dd8b
|
||||
ButtonText = #ffffffff
|
||||
Base = #ff2b2b2b
|
||||
Window = #ff1c1c1c
|
||||
Shadow = #ff000000
|
||||
HighlightedText = #ffffffff
|
||||
Link = #ffcde4b6
|
||||
LinkVisited = #ff99d999
|
||||
AlternateBase = #ff242424
|
||||
ToolTipBase = #ffffffdc
|
||||
ToolTipText = #ff000000
|
||||
PlaceholderText = #6effffff
|
||||
|
||||
5
cockatrice/themes/VelvetMarble/theme.cfg
Normal file
5
cockatrice/themes/VelvetMarble/theme.cfg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[Appearance]
|
||||
ColorScheme = Light
|
||||
|
||||
[Style]
|
||||
Name = Default
|
||||
Loading…
Add table
Add a link
Reference in a new issue