[Themes] Add identity default palettes for the image themes (#7280)

* [Themes] Add identity default palettes for the image themes

The Fabric, Leather, Plasma and VelvetMarble themes shipped only zone art and no palette, so their chrome fell back to the bare OS palette. Each theme now ships light and dark curated defaults written in the palette editor's own conventions, with [AppColors] so the home-tab buttons keep the theme's identity over static backgrounds.

- Add palette-default-light.toml / palette-default-dark.toml for the four themes; scheme resolution follows the OS since the themes declare no color scheme.
- Tint the window/base/button chrome and bevels towards each theme's identity: navy cloth (Fabric), black-brown with brass accents (Leather), electric violet with cyan sparks (Plasma), charcoal velvet with slate marble (VelvetMarble).
- Fill [AppColors] AccentStrong / AccentSoft per theme so the home-tab button gradient matches instead of falling back to the default greens.
- Derive Disabled and Inactive groups with the same conventions as PaletteGenerator::fromAccent.

* [Themes] Add home backgrounds for the image themes

* [Themes] Ship Fusion style + scheme backgrounds for the image themes

Address PR review:
- Add theme.cfg ([Style] Name = Fusion, ColorScheme = System) to Fabric,
  Leather, Plasma and VelvetMarble so their curated palettes actually apply.
  Without it the native style paints button chrome from the OS (windowsvista
  on Windows has no dark mode), making the dark palettes' light ButtonText
  unreadable on light buttons.
- Leverage the scheme-variant asset resolution: register home-dark.png and
  home-light.png in resources and add them to the qrc so the built-in home
  background also flips with the palette instead of staying static.
- Fix Leather [Palette.Inactive] Accent, which copied the Active Highlight
  color instead of the theme accent (dark #4a5f8f -> #c9995a, light
  #34508c -> #a5712f) in both palette files.

* [Themes] Align light plasma home background with the dark variant

Recolored the Plasma light home background to mirror the dark one:
brighter violet/cyan blooms, vivid azure spark arcs, and higher accent
saturation against the same pale-lavender key. Brightness is unchanged
so it still reads as a light scheme.

* [Home] Add option to disable the home tab background dim

Adds a 'Dim the home tab background' checkbox to the Home tab settings
page (Appearance). When unchecked, HomeWidget skips the translucent
black overlay it paints over the whole background. Default is on,
preserving current behavior; the home tab repaints live on change.

* Update leather backgrounds

* Update velvet marble backgrounds

* Update light plasma background

* Update light fabric background

* WIP [UI] Theme-aware onboarding banner with frosted light mode

Banner colours now derive from palette tokens at ~60fps (tick-driven,
equality-guarded setters) so scheme switches and live accent-picker
previews apply instantly:

- dark stages: byte-for-byte the original treatment (near-black stage
  from window hue, Highlight accent, white centre halo, vignette 0.62)
- light stages: pastel accent-hue wash instead of a neutral grey copy,
  brightness-lifted accent for additive glow legibility, deep-Highlight
  halo (uGlowColor) instead of white blowout, gentler vignette
  (uVignetteMin 0.88) so corners don't go muddy
- black logo silhouette variant selected on light stages
- theme picker preseeded with brand green (brand_colors.h single source)

WIP notes for next session:
- real-pixel wizard screenshot check still pending (headless capture
  exists: Xvfb :77 + isolated XDG_DATA_HOME; shader vs fallback pixel
  analysis not finished)
- user plans separately: promote Fusion to default theme, Default -> system

* [Themes] Align Fusion accent tokens with the SVG brand gradient

Align AccentStrong (#139740) and AccentSoft (#c9fd62) and the linked
Link/Accent roles with cockatrice.svg's linearGradient4265-7-8 stops so the
identity gradient used by the logo emulation matches the static art the icon
shipped.

* [Onboarding] Draw the banner logo as a static gradient plate

Replace the black/white logo tint switch with a ShaderEffect plate that
repaints the SVG's brand gradient (light AccentSoft -> dark AccentStrong
along the baked-in userSpaceOnUse axis) clipped to the full-color logo's
alpha silhouette, with the white highlight path overlaid on top — matching
the home widget's QPainter composite. The plate is static: no glow or
breathing. Brand colors flow from BannerShaderConfig's new brandStrong/
brandSoft pair instead of the removed logoDark flag, and the background
motifs get a touch more accent so the mark keeps its coloured surround.

* [Home] Draw the featured logo as a theme gradient composite

Repaint cockatrice.svg in Qt instead of showing the baked-in static art:
fill the full-color logo's alpha silhouette with the same brand gradient the
banner plate uses (light AccentSoft grading to dark AccentStrong along the
SVG's userSpaceOnUse axis), then overlay the white highlight path. Renders
an explicit QPixmap so the mark stays crisp at the 200px display size, and
re-seeds it on theme/palette/appearance changes so it never goes stale.

* [Resources] Drop the unused black logo asset

No consumer remains after the banner's logoDark toggle was replaced by the
static gradient plate (unit-tested in d7529e0c6a), so remove
cockatrice-logo-black.svg and its qrc entry.

* [Onboarding] Seed the theme picker from the theme's identity accent

Replace the hardcoded brand-green preseed with the shipped theme's own
AccentStrong (Plasma seeds violet, Fusion green), resolved from the default
palette so auto/user-generated palettes can't mask it, and re-seed whenever
the theme changes so the swatch never goes stale. Also consult the shipped
palette in maybeAutoGeneratePalette so scheme flips don't regenerate a fresh
palette over curated theme colors.

* Regenerate zone assets for Fabric, Leather, VelvetMarble themes

Align zone textures with each theme's palette identity:
- Fabric: linen weave in navy/steel tones (was generic green/blue/red/gray)
- Leather: grain texture in warm brown/amber tones
- VelvetMarble: smooth charcoal marble veining (replaces .jpg with .png)

All zones now have dark + light scheme variants for OS color scheme
adaptation. Plasma zones deferred to separate iteration.

* Regenerate Plasma zone assets: horizontal-hand layout, seamless tiling

Bake each zone's phase from its true world position in the horizontal-hand
layout (player 0, stack 172, table 280, hand 172+406) so the diagonal sheen
continues continuously across zone boundaries. All zones share one
mathematically tileable (1,1) diagonal at frequency 3; playerzone gets a
distinct blue-violet identity and the light scheme gets extra contrast.

* Regenerate VelvetMarble zones: classic Perlin marble, seamless tiling

* Regenerate VelvetMarble zones: domain-warped fractal veins, stone grain

* Promote Fusion to default theme, rename Default to System

Fresh installs and new profiles now default to the Fusion (dark) theme
instead of the platform-native theme. The old "Default" theme is
renamed "System" to better describe its purpose — using the OS-native
Qt style (windowsvista, macOS, etc.).

Existing users who had "Default" selected are automatically migrated
to "System" so they keep their platform-native styling. Users with
an empty or invalid theme name now fall back to Fusion.

* Move checkbox.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-09-18 15:16:07 +02:00 committed by GitHub
parent 6c1d1c7b58
commit 7b39cf98d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
86 changed files with 1160 additions and 85 deletions

View file

@ -403,6 +403,7 @@ set(cockatrice_SOURCES
src/interface/widgets/tabs/api/commander_spellbook/commander_bracket_widget.cpp
src/interface/widgets/tabs/api/commander_spellbook/handle_commander_brackets.cpp
src/interface/widgets/onboarding/banner_shader_config.h
src/interface/widgets/onboarding/brand_colors.h
src/interface/widgets/onboarding/first_run_wizard.cpp
src/interface/widgets/onboarding/first_run_wizard.h
src/interface/widgets/onboarding/first_run_wizard_page.cpp
@ -530,6 +531,7 @@ qt6_add_shaders(
"src/interface/widgets/onboarding/shaders"
FILES
src/interface/widgets/onboarding/shaders/brand_banner.frag
src/interface/widgets/onboarding/shaders/brand_plate.frag
)
qt6_add_resources(

View file

@ -63,6 +63,8 @@
<file>resources/icons/mana/W.svg</file>
<file>resources/backgrounds/home.png</file>
<file>resources/backgrounds/home-dark.png</file>
<file>resources/backgrounds/home-light.png</file>
<file>resources/backgrounds/card_triplet.svg</file>
<file>resources/backgrounds/placeholder_printing_selector.svg</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 16 MiB

Before After
Before After

View file

@ -16,7 +16,7 @@ QString ThemeConfig::toIni() const
out += "[Appearance]\n";
out += QString("ColorScheme = %1\n").arg(colorScheme.isEmpty() ? "System" : colorScheme);
out += "\n[Style]\n";
out += QString("Name = %1\n").arg(styleName.isEmpty() ? "Default" : styleName);
out += QString("Name = %1\n").arg(styleName.isEmpty() ? "System" : styleName);
return out;
}

View file

@ -22,7 +22,7 @@
#include <Qt>
#include <libcockatrice/settings/paths_settings.h>
#define NONE_THEME_NAME "Default"
#define SYSTEM_THEME_NAME "System"
#define FUSION_THEME_NAME "Fusion"
#define STYLE_CSS_NAME "style.css"
#define HANDZONE_BG_NAME "handzone"
@ -96,7 +96,7 @@ struct PaletteColorInfo
static QString usableDefaultStyle(const QString &style)
{
// The Windows 11 native style is broken: when the OS default
// ("Default" theme selection) would use it, fall back to the Vista style.
// ("System" theme selection) would use it, fall back to the Vista style.
// Explicitly choosing "windows11" in a theme is still honored.
return style.compare("windows11", Qt::CaseInsensitive) == 0 ? QStringLiteral("windowsvista") : style;
}
@ -119,10 +119,16 @@ ThemeManager::ThemeManager(QObject *parent) : QObject(parent)
void ThemeManager::ensureThemeDirectoryExists()
{
if (SettingsCache::instance().getThemeName().isEmpty() ||
!getAvailableThemes().contains(SettingsCache::instance().getThemeName())) {
auto &settings = SettingsCache::instance();
// Migrate the old "Default" theme name to "System"
if (settings.getThemeName() == "Default") {
settings.setThemeName(SYSTEM_THEME_NAME);
}
if (settings.getThemeName().isEmpty() || !getAvailableThemes().contains(settings.getThemeName())) {
qCInfo(ThemeManagerLog) << "Theme name not set, setting default value";
SettingsCache::instance().setThemeName(NONE_THEME_NAME);
settings.setThemeName(FUSION_THEME_NAME);
}
}
@ -235,9 +241,7 @@ QStringMap &ThemeManager::getAvailableThemes()
// load themes from user profile dir
dir.setPath(SettingsCache::instance().paths().getThemesPath());
// add default value
availableThemes.insert(NONE_THEME_NAME, dir.absoluteFilePath("Default"));
availableThemes.insert(SYSTEM_THEME_NAME, dir.absoluteFilePath("System"));
availableThemes.insert(FUSION_THEME_NAME, dir.absoluteFilePath("Fusion"));
for (QString themeName : dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name)) {
@ -395,7 +399,7 @@ void ThemeManager::applyStyleAndPalette(const QString &themeName,
Q_UNUSED(activeScheme)
#endif
QString styleName = themeCfg.styleName;
if (styleName.isEmpty() || styleName.compare("Default", Qt::CaseInsensitive) == 0) {
if (styleName.isEmpty() || styleName.compare("System", Qt::CaseInsensitive) == 0) {
if (themeName == FUSION_THEME_NAME) {
styleName = "Fusion";
} else {

View file

@ -11,6 +11,8 @@
#include "home_tab_button_color.h"
#include <QGroupBox>
#include <QLabel>
#include <QLinearGradient>
#include <QPainter>
#include <QPainterPath>
#include <QPushButton>
@ -21,8 +23,7 @@
#include <libcockatrice/settings/paths_settings.h>
HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
: QWidget(parent), tabSupervisor(_tabSupervisor), background(themePixmap(QStringLiteral("backgrounds/home"))),
overlay(themePixmap(QStringLiteral("cockatrice")))
: QWidget(parent), tabSupervisor(_tabSupervisor), background(themePixmap(QStringLiteral("backgrounds/home")))
{
layout = new QGridLayout(this);
@ -54,6 +55,8 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
// Lambda is cleaner to read than overloading this
connect(&SettingsCache::instance().appearance(), &AppearanceSettings::homeTabDisplayCardNameChanged, this,
[this] { repaint(); });
connect(&SettingsCache::instance().appearance(), &AppearanceSettings::homeTabBackgroundDimChanged, this,
[this] { repaint(); });
connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this,
&HomeWidget::initializeBackgroundFromSource);
connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this,
@ -62,12 +65,17 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
// not on SettingsCache::themeChanged, so re-resolve the variant background.
connect(themeManager, &ThemeManager::themeChanged, this, &HomeWidget::initializeBackgroundFromSource);
connect(themeManager, &ThemeManager::paletteChanged, this, &HomeWidget::updateButtonsToBackgroundColor);
connect(themeManager, &ThemeManager::paletteChanged, this, &HomeWidget::updateLogoOverlay);
connect(&SettingsCache::instance().appearance(), &AppearanceSettings::homeTabButtonColorChanged, this,
&HomeWidget::updateButtonsToBackgroundColor);
}
void HomeWidget::initializeBackgroundFromSource()
{
// The featured logo is theme/scheme-derived too; reload it alongside the
// background so a theme or appearance switch doesn't leave it stale.
updateLogoOverlay();
if (CardDatabaseManager::getInstance()->getLoadStatus() != LoadStatus::Ok) {
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
&HomeWidget::initializeBackgroundFromSource);
@ -227,10 +235,10 @@ QGroupBox *HomeWidget::createButtons()
QVBoxLayout *boxLayout = new QVBoxLayout;
boxLayout->setAlignment(Qt::AlignHCenter);
QLabel *logoLabel = new QLabel;
logoLabel->setPixmap(overlay.scaledToWidth(200, Qt::SmoothTransformation));
logoLabel = new QLabel;
logoLabel->setAlignment(Qt::AlignCenter);
boxLayout->addWidget(logoLabel);
updateLogoOverlay();
boxLayout->addSpacing(25);
connectButton = new HomeStyledButton("Connect/Play", gradientColors);
@ -358,6 +366,7 @@ void HomeWidget::paintEvent(QPaintEvent *event)
painter.drawPixmap(topLeft, toDraw);
}
if (SettingsCache::instance().appearance().getHomeTabBackgroundDim()) {
// Draw translucent black overlay with rounded corners
QRectF overlayRect(5, 5, width() - 10, height() - 10);
QPainterPath roundedRectPath;
@ -365,6 +374,7 @@ void HomeWidget::paintEvent(QPaintEvent *event)
QColor semiTransparentBlack(0, 0, 0, static_cast<int>(255 * 0.33));
painter.fillPath(roundedRectPath, semiTransparentBlack);
}
// Card name overlay (above the attribution, bottom-right)
QString cardName;
@ -429,3 +439,56 @@ void HomeWidget::paintEvent(QPaintEvent *event)
QWidget::paintEvent(event);
}
void HomeWidget::updateLogoOverlay()
{
// Emulate cockatrice.svg in Qt rather than rendering the baked-in SVG.
// The SVG has no separate plate: the gradient fills the bird's silhouette
// paths (light #c9fd62/AccentSoft at the top-left, dark #139740/AccentStrong
// toward the bottom-right — the SVG's linearGradient4265-7-8 stops along
// its userSpaceOnUse axis), and the white highlight path
// (cockatrice-logo-white) sits on top. So we paint that gradient clipped to
// the full logo silhouette (the full-color logo's alpha), then overlay the
// white mark. Colours stay fully theme-driven and independent of the static
// greens baked into the SVG.
const QColor strong = themeManager->appColor(AppColor::AccentStrong);
const QColor soft = themeManager->appColor(AppColor::AccentSoft);
const QPixmap silhouette = themePixmap(QStringLiteral("cockatrice")).scaledToWidth(200, Qt::SmoothTransformation);
const QPixmap whiteMark =
themePixmap(QStringLiteral("cockatrice-logo-white")).scaledToWidth(200, Qt::SmoothTransformation);
if (silhouette.isNull() || whiteMark.isNull()) {
return;
}
QPixmap composite(silhouette.size());
composite.fill(Qt::transparent);
{
QPainter painter(&composite);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
// Recreate cockatrice.svg's own gradient geometry (linearGradient
// 4265-7-8, userSpaceOnUse): light AccentSoft at S=(-8.097,-97.746),
// dark AccentStrong at E=(162.455,295.208), on the SVG's 300x300
// canvas. Scale those coordinates to this composite's size.
const qreal scale = composite.width() / 300.0;
QLinearGradient gradient(QPointF(-8.097, -97.746) * scale, QPointF(162.455, 295.208) * scale);
gradient.setColorAt(0.0, soft);
gradient.setColorAt(1.0, strong);
painter.fillRect(composite.rect(), gradient);
// Clip the gradient to the full logo silhouette exactly as the SVG's
// gradient paths are confined to the bird.
painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
painter.drawPixmap(0, 0, silhouette);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawPixmap(0, 0, whiteMark);
}
if (logoLabel) {
logoLabel->setPixmap(composite);
}
}

View file

@ -15,6 +15,9 @@
#include <QWidget>
#include <libcockatrice/network/client/abstract/abstract_client.h>
class QGridLayout;
class QLabel;
class HomeWidget : public QWidget
{
@ -41,13 +44,14 @@ private:
QPixmap background;
CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr;
DeckList backgroundSourceDeck;
QPixmap overlay;
QLabel *logoLabel = nullptr;
QPair<QColor, QColor> gradientColors;
HomeStyledButton *connectButton;
void setRandomCard(ExactCard &newCard);
void loadBackgroundSourceDeck();
QPair<QColor, QColor> determineButtonColor() const;
void updateLogoOverlay();
};
#endif // HOME_WIDGET_H

View file

@ -38,6 +38,10 @@ class BannerShaderConfig : public QObject
Q_PROPERTY(QColor colorA READ colorA WRITE setColorA NOTIFY colorAChanged)
Q_PROPERTY(QColor colorB READ colorB WRITE setColorB NOTIFY colorBChanged)
Q_PROPERTY(QColor accent READ accent WRITE setAccent NOTIFY accentChanged)
Q_PROPERTY(QColor glowColor READ glowColor WRITE setGlowColor NOTIFY glowColorChanged)
Q_PROPERTY(QColor brandStrong READ brandStrong WRITE setBrandStrong NOTIFY brandStrongChanged)
Q_PROPERTY(QColor brandSoft READ brandSoft WRITE setBrandSoft NOTIFY brandSoftChanged)
Q_PROPERTY(qreal vignetteMin READ vignetteMin WRITE setVignetteMin NOTIFY vignetteMinChanged)
Q_PROPERTY(bool logoVisible READ logoVisible WRITE setLogoVisible NOTIFY logoVisibleChanged)
Q_PROPERTY(qreal logoGlow READ logoGlow WRITE setLogoGlow NOTIFY logoGlowChanged)
@ -185,6 +189,54 @@ public:
}
}
QColor glowColor() const
{
return m_glowColor;
}
void setGlowColor(const QColor &c)
{
if (c != m_glowColor) {
m_glowColor = c;
emit glowColorChanged();
}
}
QColor brandStrong() const
{
return m_brandStrong;
}
void setBrandStrong(const QColor &c)
{
if (c != m_brandStrong) {
m_brandStrong = c;
emit brandStrongChanged();
}
}
QColor brandSoft() const
{
return m_brandSoft;
}
void setBrandSoft(const QColor &c)
{
if (c != m_brandSoft) {
m_brandSoft = c;
emit brandSoftChanged();
}
}
qreal vignetteMin() const
{
return m_vignetteMin;
}
void setVignetteMin(qreal v)
{
if (v != m_vignetteMin) {
m_vignetteMin = v;
emit vignetteMinChanged();
}
}
bool logoVisible() const
{
return m_logoVisible;
@ -222,6 +274,10 @@ signals:
void colorAChanged();
void colorBChanged();
void accentChanged();
void glowColorChanged();
void brandStrongChanged();
void brandSoftChanged();
void vignetteMinChanged();
void logoVisibleChanged();
void logoGlowChanged();
@ -239,9 +295,16 @@ private:
bool m_frontIsA = true;
// Curated fallback seed values -- BannerHost overwrites these with
// palette-derived colours (see shader_banner_widget.cpp) before the first
// paint, so they only matter as a safe pre-first-apply default.
QColor m_colorA{0x1A, 0x1A, 0x20};
QColor m_colorB{0x0E, 0x0E, 0x12};
QColor m_accent{0x8B, 0xDD, 0x6B};
QColor m_glowColor{Qt::white};
QColor m_brandStrong{0x13, 0x97, 0x40};
QColor m_brandSoft{0xC9, 0xFD, 0x62};
qreal m_vignetteMin = 0.62;
bool m_logoVisible = false;
qreal m_logoGlow = 1.0;

View file

@ -0,0 +1,15 @@
#ifndef BRAND_COLORS_H
#define BRAND_COLORS_H
#include <QColor>
/** @brief Cockatrice brand green.
*
* Single source of truth for the onboarding brand accent: it backs the
* banner's shader-accent uniform as the curated fallback when the active
* palette resolves no usable Highlight, and it preseads the wizard's
* QuickSetupPanel so a freshly generated palette keeps the brand identity
* until the user picks their own look. */
inline const QColor kCockatriceBrandGreen(0x8B, 0xDD, 0x6B);
#endif // BRAND_COLORS_H

View file

@ -14,10 +14,31 @@
#include <QGroupBox>
#include <QLabel>
#include <QMessageBox>
#include <QPalette>
#include <QVBoxLayout>
#include <libcockatrice/settings/paths_settings.h>
#include <libcockatrice/settings/personal_settings.h>
namespace
{
/** @brief A theme's shipped identity accent, immune to any user- or auto-
* generated palette that may currently be masking appColor(). */
QColor themeIdentityAccent(const QString &themeDirPath, const QString &themeName)
{
for (const QString &scheme : {QStringLiteral("Light"), QStringLiteral("Dark")}) {
const PaletteConfig cfg = ThemeManager::loadDefaultPaletteConfig(themeDirPath, themeName, scheme);
if (cfg.appColors.contains(AppColor::AccentStrong)) {
return cfg.appColors.value(AppColor::AccentStrong);
}
const QColor highlight = cfg.colors.value(QPalette::Active).value(QPalette::Highlight);
if (highlight.isValid()) {
return highlight;
}
}
return {};
}
} // namespace
ThemeSetupPage::ThemeSetupPage(QWidget *parent) : FirstRunWizardPage(parent)
{
themeCombo = new QComboBox(this);
@ -30,6 +51,15 @@ ThemeSetupPage::ThemeSetupPage(QWidget *parent) : FirstRunWizardPage(parent)
quickSetupPanel = new QuickSetupPanel(this);
// Seed the picker from the current theme's own identity accent rather than
// a hardcoded brand green: Plasma seeds violet, Fusion green, etc., and it
// is immune to stale generated palettes that may mask appColor(). This was
// initially a brand-green workaround from before Fusion became the default.
// setAccentColor blocks signals, so this never triggers a generation.
lastSeededTheme = SettingsCache::instance().getThemeName();
quickSetupPanel->setAccentColor(
themeIdentityAccent(themeManager->getAvailableThemes().value(lastSeededTheme), lastSeededTheme));
connect(themeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeSetupPage::onThemeChanged);
connect(schemeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeSetupPage::onSchemeChanged);
connect(quickSetupPanel, &QuickSetupPanel::valueChanged, this, &ThemeSetupPage::onGenerateFromAccent);
@ -47,7 +77,8 @@ ThemeSetupPage::ThemeSetupPage(QWidget *parent) : FirstRunWizardPage(parent)
// Mirrors AppearanceSettingsPage's identical listener for the combo-sync
// half of this.
connect(themeManager, &ThemeManager::themeChanged, this, [this] {
const QString newDir = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName());
const QString newTheme = SettingsCache::instance().getThemeName();
const QString newDir = themeManager->getAvailableThemes().value(newTheme);
const ThemeConfig cfg = ThemeConfig::fromThemeDir(newDir);
const QString current = cfg.colorScheme;
@ -56,6 +87,14 @@ ThemeSetupPage::ThemeSetupPage(QWidget *parent) : FirstRunWizardPage(parent)
schemeCombo->setCurrentIndex(idx >= 0 ? idx : 0);
schemeCombo->blockSignals(false);
// Keep the picker's accent in step with the theme's own identity; the
// swatch seeded at construction would otherwise stay stale (e.g. green
// from a previous theme) when the user toggles themes.
if (newTheme != lastSeededTheme) {
lastSeededTheme = newTheme;
quickSetupPanel->setAccentColor(themeIdentityAccent(newDir, newTheme));
}
maybeAutoGeneratePalette();
});
@ -158,8 +197,14 @@ void ThemeSetupPage::maybeAutoGeneratePalette()
const QString dirPath = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName());
const QString scheme = resolvedScheme();
// The theme dir may resolve to the user profile even for built-in themes
// (getAvailableThemes gives the user copy precedence), so consult the
// shipped palette too -- both via loadDefaultPaletteConfig's system fallback.
// Without it, scheme flips regenerate a fresh palette from the picker accent
// and clobber the curated colours the theme explicitly ships.
if (PaletteConfig::fromScheme(dirPath, scheme).hasPalette() ||
PaletteConfig::fromDefault(dirPath, scheme).hasPalette()) {
ThemeManager::loadDefaultPaletteConfig(dirPath, SettingsCache::instance().getThemeName(), scheme)
.hasPalette()) {
return; // theme already has something real to show -- leave it alone
}

View file

@ -53,6 +53,9 @@ private:
QComboBox *homeTabBackgroundCombo;
bool paletteDirty = false;
/// Theme whose identity accent currently seeds the picker.
QString lastSeededTheme;
};
#endif // THEME_SETUP_PAGE_H

View file

@ -16,6 +16,8 @@ Item {
property vector4d uColorA: Qt.vector4d(bannerConfig.colorA.r, bannerConfig.colorA.g, bannerConfig.colorA.b, 1.0)
property vector4d uColorB: Qt.vector4d(bannerConfig.colorB.r, bannerConfig.colorB.g, bannerConfig.colorB.b, 1.0)
property vector4d uAccent: Qt.vector4d(bannerConfig.accent.r, bannerConfig.accent.g, bannerConfig.accent.b, 1.0)
property vector4d uGlowColor: Qt.vector4d(bannerConfig.glowColor.r, bannerConfig.glowColor.g, bannerConfig.glowColor.b, 1.0)
property real uVignetteMin: bannerConfig.vignetteMin
property real uLogoGlow: bannerConfig.logoGlow
fragmentShader: "qrc:/onboarding/shaders/brand_banner.frag.qsb"
}
@ -33,30 +35,62 @@ Item {
property vector4d uColorA: Qt.vector4d(bannerConfig.colorA.r, bannerConfig.colorA.g, bannerConfig.colorA.b, 1.0)
property vector4d uColorB: Qt.vector4d(bannerConfig.colorB.r, bannerConfig.colorB.g, bannerConfig.colorB.b, 1.0)
property vector4d uAccent: Qt.vector4d(bannerConfig.accent.r, bannerConfig.accent.g, bannerConfig.accent.b, 1.0)
property vector4d uGlowColor: Qt.vector4d(bannerConfig.glowColor.r, bannerConfig.glowColor.g, bannerConfig.glowColor.b, 1.0)
property real uVignetteMin: bannerConfig.vignetteMin
property real uLogoGlow: bannerConfig.logoGlow
fragmentShader: "qrc:/onboarding/shaders/brand_banner.frag.qsb"
}
// The hero logo itself breathes cleanly over a 0.51.0 opacity range
Image {
id: logo
anchors.centerIn: parent
// The white logo sits at full opacity on top of the static gradient plate
// no glow, no breathing. The plate matches home_widget's QPainter composite.
Item {
id: logoHost
visible: bannerConfig.logoVisible
source: "qrc:/resources/cockatrice-logo-white.svg"
anchors.centerIn: parent
width: root.height * 0.6
height: width * (sourceSize.height > 0 ? sourceSize.height / Math.max(sourceSize.width, 1) : 1)
height: width
// The full-color logo renders beneath the white mark and is consumed as
// a texture (layer.enabled) by the plate shader's silhouette mask, so
// the gradient is clipped to the bird exactly as the SVG's gradient
// paths are. It is never drawn to the screen itself.
Image {
id: silhouetteMask
anchors.fill: parent
source: "qrc:/resources/cockatrice.svg"
sourceSize: Qt.size(256, 256)
fillMode: Image.PreserveAspectFit
smooth: true
opacity: 0.5 + 0.5 * bannerConfig.logoGlow
visible: false
layer.enabled: true
layer.smooth: true
}
// The logo's gradient plate, drawn behind the mark: a linear
// AccentSoft (light) -> AccentStrong (dark) sheet along the same
// top-left -> bottom-right userSpaceOnUse axis the baked-in SVG used,
// clipped to the bird silhouette via uSilhouette. The white highlight
// path above is theme independent. Sized to the logo itself no
// rounded badge, matching home_widget's QPainter composite. Static.
// Small ShaderEffect, Qt 6.4-safe.
ShaderEffect {
id: brandPlate
anchors.fill: parent
property vector4d uStrong: Qt.vector4d(bannerConfig.brandStrong.r, bannerConfig.brandStrong.g,
bannerConfig.brandStrong.b, 1.0)
property vector4d uSoft: Qt.vector4d(bannerConfig.brandSoft.r, bannerConfig.brandSoft.g,
bannerConfig.brandSoft.b, 1.0)
property var uSilhouette: silhouetteMask
fragmentShader: "qrc:/onboarding/shaders/brand_plate.frag.qsb"
}
Image {
id: logoImage
anchors.fill: parent
source: "qrc:/resources/cockatrice-logo-white.svg"
sourceSize: Qt.size(256, 256)
Behavior on opacity { NumberAnimation { duration: 300; easing.type: Easing.InOutSine } }
transform: Scale {
origin.x: logo.width / 2
origin.y: logo.height / 2
xScale: 0.94 + 0.06 * bannerConfig.logoGlow
yScale: 0.94 + 0.06 * bannerConfig.logoGlow
fillMode: Image.PreserveAspectFit
smooth: true
}
}
}

View file

@ -1,7 +1,10 @@
#include "shader_banner_widget.h"
#include "../../theme_manager.h"
#include "banner_shader_config.h"
#include "brand_colors.h"
#include <QApplication>
#include <QPainter>
#include <QQmlContext>
#include <QQmlEngine>
@ -11,11 +14,98 @@
namespace
{
// Near-black base palette -- the background is dark and quiet so the green
// accent stands out.
constexpr QRgb kColorA = 0x1A1A20;
constexpr QRgb kColorB = 0x0E0E12;
constexpr QRgb kAccent = 0x8BDD6B;
// Curated near-black stage -- used only when the active palette resolves no
// usable window colour. Matches the banner's original design (dark and quiet
// so the accent stands out) and satisfies design-plans §2.1's "identity
// survives a bare palette".
constexpr QRgb kFallbackColorA = 0x1A1A20;
constexpr QRgb kFallbackColorB = 0x0E0E12;
struct SuggestedColors
{
QColor colorA;
QColor colorB;
QColor accent;
QColor glowColor;
QColor brandStrong;
QColor brandSoft;
qreal vignetteMin = 0.62;
};
SuggestedColors suggestedBannerColors()
{
const QPalette &pal = qApp->palette();
const QColor window = pal.color(QPalette::Active, QPalette::Window);
// Identity accent: the theme's [AppColors] AccentStrong, which appColor()
// resolves to QPalette::Highlight when a theme doesn't pin AccentStrong.
// Reading bare Highlight ignored curated accent tokens (Plasma's violet
// vs Default's green) whenever a palette didn't set the role itself.
const QColor accentStrong = themeManager->appColor(AppColor::AccentStrong);
if (!window.isValid() || !accentStrong.isValid()) {
return {QColor(kFallbackColorA),
QColor(kFallbackColorB),
kCockatriceBrandGreen,
QColor(Qt::white),
kCockatriceBrandGreen,
QColor(0xC9, 0xFD, 0x62),
0.62};
}
// The theme's brand pair: AccentStrong is the deep green, AccentSoft the
// lime. These two appColors form the logo's "surrounding gradient" (deep
// core grading out to the soft, brand-toned glow) on both the banner and
// the home screen.
const QColor brandStrong = accentStrong;
const QColor brandSoft = themeManager->appColor(AppColor::AccentSoft);
// Dress the stage for the scheme so the banner never fights the
// surrounding window in either mode. Dark palettes keep the original
// quiet near-black stage (lightness 29 → 16) with the theme's window
// hue; light palettes get a pastel "frosted accent" treatment built from
// the accent hue instead of a plain near-white copy: a coloured wash that
// clearly belongs to the theme.
const qreal luma = 0.299 * window.red() + 0.587 * window.green() + 0.114 * window.blue();
const bool lightStage = luma > 115.0;
if (lightStage) {
const int hue = accentStrong.hslHue();
// Achromatic accents (grey) get a neutral near-white stage instead.
const int stageSat = hue < 0 ? 0 : 64;
const int hueSafe = hue < 0 ? 0 : hue;
// Depth is what stops a light stage reading as a washed-out near-white
// copy of the page behind the banner: deepen the lower pastel band and
// raise saturation so the hue is clearly present while staying frosted.
auto pastel = [hueSafe, stageSat](int lightness) { return QColor::fromHsl(hueSafe, stageSat, lightness); };
auto pastelLower = [hueSafe](int lightness) { return QColor::fromHsl(hueSafe, 76, lightness); };
// Brightness-lifted accent for additive glows: the raw accent on a
// light stage must be mid-bright to read instead of washing out, so
// lift lightness and saturation together.
const int accentLightness = qBound(158, accentStrong.lightness() + 82, 198);
const int accentSaturation = hue < 0 ? 0 : qMax(accentStrong.hslSaturation(), 180);
const QColor liftedAccent =
hue < 0 ? accentStrong : QColor::fromHsl(hueSafe, accentSaturation, accentLightness);
// The centre glow (and logo tint in QML) uses the deep accent itself:
// a coloured halo/fill behind the logo instead of a white or black one.
return {pastel(214), pastelLower(186), liftedAccent, accentStrong, brandStrong, brandSoft, 0.80};
}
// Dark stage: force the window hue down to the banner's curated darkness,
// scaling saturation away so chromatic palettes tint it without going
// muddy. The accent is the bright, brand-driven tone (hue from the accent
// itself, never the -- often grey -- window), and it drives both the
// embers/fog and the logo glow so the mark tints like the light stage.
auto stage = [&window](int lightness) {
const int hue = window.hslHue();
const int saturation = hue < 0 ? 0 : qBound(0, qRound(window.hslSaturation() * (lightness / 40.0)), 255);
return QColor::fromHsl(hue, saturation, lightness);
};
const int accentHue = accentStrong.hslHue();
const int accentHueSafe = accentHue < 0 ? 0 : accentHue;
const int accentLightness = qBound(150, accentStrong.lightness() + 70, 185);
const int accentSaturation = accentHue < 0 ? 0 : qMax(accentStrong.hslSaturation(), 160);
const QColor accent =
accentHue < 0 ? accentStrong : QColor::fromHsl(accentHueSafe, accentSaturation, accentLightness);
return {stage(29), stage(16), accent, accent, brandStrong, brandSoft, 0.62};
}
} // namespace
class GradientFallbackWidget : public QWidget
@ -23,15 +113,27 @@ class GradientFallbackWidget : public QWidget
public:
using QWidget::QWidget;
void setColors(const QColor &a, const QColor &b)
{
if (a != colorA || b != colorB) {
colorA = a;
colorB = b;
}
}
protected:
void paintEvent(QPaintEvent *) override
{
QPainter painter(this);
QLinearGradient gradient(0, 0, width(), height());
gradient.setColorAt(0.0, QColor(kColorA));
gradient.setColorAt(1.0, QColor(kColorB));
gradient.setColorAt(0.0, colorA);
gradient.setColorAt(1.0, colorB);
painter.fillRect(rect(), gradient);
}
private:
QColor colorA{QColor(kFallbackColorA)};
QColor colorB{QColor(kFallbackColorB)};
};
BannerHost::BannerHost(QWidget *parent) : QWidget(parent)
@ -62,6 +164,9 @@ BannerHost::BannerHost(QWidget *parent) : QWidget(parent)
connect(&clock, &QTimer::timeout, this, &BannerHost::tick);
clock.setInterval(16); // ~60fps; the shader itself is cheap, this is just a wall clock
connect(themeManager, &ThemeManager::themeChanged, this, &BannerHost::applyThemeColors);
applyThemeColors();
applyMotifPreset(currentMotif);
updateAspect();
}
@ -123,9 +228,9 @@ void BannerHost::applyMotifPreset(Motif motif)
const Preset p = presetFor(motif);
config->setColorA(QColor(kColorA));
config->setColorB(QColor(kColorB));
config->setAccent(QColor(kAccent));
config->setColorA(bannerColorA);
config->setColorB(bannerColorB);
config->setAccent(bannerAccent);
config->setLogoVisible(motif == Motif::Welcome);
if (isFirstApply) {
@ -183,8 +288,34 @@ void BannerHost::hideEvent(QHideEvent *event)
clock.stop();
}
void BannerHost::applyThemeColors()
{
const SuggestedColors colors = suggestedBannerColors();
bannerColorA = colors.colorA;
bannerColorB = colors.colorB;
bannerAccent = colors.accent;
if (usingFallback) {
fallback->setColors(bannerColorA, bannerColorB);
fallback->update();
} else if (config) {
config->setColorA(bannerColorA);
config->setColorB(bannerColorB);
config->setAccent(bannerAccent);
config->setGlowColor(colors.glowColor);
config->setBrandStrong(colors.brandStrong);
config->setBrandSoft(colors.brandSoft);
config->setVignetteMin(colors.vignetteMin);
}
}
void BannerHost::tick()
{
// Palette previews (e.g. accent drags in the wizard's QuickSetupPanel)
// apply qApp->palette() without firing themeChanged, so re-derive here;
// BannerShaderConfig's setters are equality-guarded, so this is a no-op
// unless the colours actually changed.
applyThemeColors();
if (config) {
qreal t = elapsed.elapsed() / 1000.0;
config->setTime(t);

View file

@ -1,6 +1,7 @@
#ifndef SHADER_BANNER_WIDGET_H
#define SHADER_BANNER_WIDGET_H
#include <QColor>
#include <QElapsedTimer>
#include <QTimer>
#include <QWidget>
@ -53,6 +54,7 @@ protected:
private slots:
void tick();
void onSceneGraphFailed();
void applyThemeColors();
private:
struct Preset
@ -73,6 +75,12 @@ private:
BannerShaderConfig *config = nullptr;
GradientFallbackWidget *fallback = nullptr;
// Palette-derived banner colours -- the theme's window hue forced down to
// the banner's curated darkness, plus the theme's Highlight as accent.
QColor bannerColorA;
QColor bannerColorB;
QColor bannerAccent;
QTimer clock;
QElapsedTimer elapsed;
Motif currentMotif = Motif::Welcome;

View file

@ -28,6 +28,8 @@ layout(std140, binding = 0) uniform buf
vec4 uColorA;
vec4 uColorB;
vec4 uAccent;
vec4 uGlowColor;
float uVignetteMin;
float uLogoGlow;
};
@ -119,7 +121,7 @@ vec3 backgroundField(vec2 uv, float time)
// Accent-coloured fog layer: flowNoise peaks above 0.6 contribute accent
float fog = flowNoise(uv * 0.8 + vec2(100.0, 50.0), time * 0.02);
col += uAccent.rgb * max(fog - 0.6, 0.0) * 0.10;
col += uAccent.rgb * max(fog - 0.6, 0.0) * 0.14;
return col;
}
@ -136,14 +138,17 @@ vec3 motifWelcome(vec2 uv, vec3 bg, float t)
vec2 center = vec2(asp * 0.5, 0.5);
float cDist = length(ac - center);
// Centre bloom at logo position; intensity scales with uLogoGlow
// Centre bloom at logo position; intensity scales with uLogoGlow. The
// QML brandGlow halo now supplies the primary logo surround (the two
// brand appColors), so this shader bloom is deliberately kept as a subtle
// ambience rather than a competing glow.
float centreLight = bloom(cDist, 0.08 * asp, 0.40 * asp);
col += centreLight * 0.20 * uLogoGlow;
col += uGlowColor.rgb * centreLight * 0.20 * uLogoGlow;
// Flow-noise shimmer gated by Gaussian mask at centre
float shimmer = flowNoise(ac * 0.8 + vec2(55.0, 33.0), t * 0.05) * 0.5 + 0.5;
float shimmerMask = exp(-(cDist * cDist) / (0.18 * asp * 0.18 * asp));
col += shimmer * shimmerMask * 0.04 * uLogoGlow;
col += uGlowColor.rgb * shimmer * shimmerMask * 0.04 * uLogoGlow;
// 48 ember particles: hash-seeded position, speed, size, brightness.
// Embers within a distance threshold of centre are deflected into an
@ -162,8 +167,8 @@ vec3 motifWelcome(vec2 uv, vec3 bg, float t)
float pX = baseX * asp + sin(t * driftFreq + fi * 1.7) * driftAmp * asp;
float pY = fract(baseY + t * riseSpeed);
float size = 0.006 + hash21(vec2(fi * 2.9, uSeed * 4.7)) * 0.012;
float bright = 0.15 + hash21(vec2(fi * 6.1, uSeed * 0.9)) * 0.30;
float size = 0.010 + hash21(vec2(fi * 2.9, uSeed * 4.7)) * 0.014;
float bright = 0.18 + hash21(vec2(fi * 6.1, uSeed * 0.9)) * 0.32;
// Fade out near top/bottom edges
float edgeFade = smoothstep(0.0, 0.12, pY) * smoothstep(1.0, 0.88, pY);
@ -232,7 +237,7 @@ vec3 motifCardDatabase(vec2 uv, vec3 bg, float t)
// Semi-transparent dark fill
float fill = smoothstep(0.015, -0.005, d);
col = mix(col, uColorB.rgb * 0.55, fill * 0.50);
col = mix(col, uColorB.rgb * 0.60, fill * 0.62);
// Accent outline
float edge = smoothstep(0.035, 0.0, abs(d));
@ -309,7 +314,7 @@ vec3 motifAccount(vec2 uv, vec3 bg, float t)
// Node glow via bloom; intensity modulated by pulse
float dist = length(ac - pos);
col += uAccent.rgb * bloom(dist, 0.018, 0.08) * mix(0.20, 0.45, pulse);
col += uAccent.rgb * bloom(dist, 0.018, 0.08) * mix(0.30, 0.55, pulse);
}
// Edges: connect nodes within a radius threshold
@ -323,19 +328,19 @@ vec3 motifAccount(vec2 uv, vec3 bg, float t)
vec2 ba = nodePos[j] - nodePos[i];
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
float lineDist = length(pa - ba * h);
col += uAccent.rgb * smoothstep(0.010, 0.0, lineDist) * strength * 0.10;
col += uAccent.rgb * smoothstep(0.010, 0.0, lineDist) * strength * 0.14;
}
}
}
// Central bloom at banner centre
float cDist = length(ac - center);
col += uAccent.rgb * bloom(cDist, 0.04, 0.25) * 0.12;
col += uAccent.rgb * bloom(cDist, 0.04, 0.25) * 0.20;
// Periodic expanding ring from centre
float ripplePhase = t * 0.4;
float rippleDist = abs(cDist - fract(ripplePhase) * asp * 0.7);
col += uAccent.rgb * smoothstep(0.02, 0.0, rippleDist) * 0.10;
col += uAccent.rgb * smoothstep(0.02, 0.0, rippleDist) * 0.14;
return col;
}
@ -456,6 +461,8 @@ void main()
else if (uMode < 4.5) col = motifPreferences(uv, bg, t);
else col = motifFinish(uv, bg, t);
col *= mix(0.62, 1.0, vignette(uv));
// Corner vignette; uVignetteMin is scheme-driven (0.62 on dark stages,
// gentler on light ones so near-white corners don't go muddy grey).
col *= mix(uVignetteMin, 1.0, vignette(uv));
fragColor = vec4(col, 1.0) * qt_Opacity;
}

View file

@ -0,0 +1,42 @@
#version 440
// The logo's gradient plate, drawn by us rather than the baked-in SVG: a
// linear blend between the two brand appColors (light AccentSoft at the
// top-left grading to dark AccentStrong at the bottom-right, mirroring
// cockatrice.svg's linearGradient4265-7-8 userSpaceOnUse axis), clipped to
// the bird's full silhouette via the full-color logo's alpha (uSilhouette).
// The white highlight path (cockatrice-logo-white) is overlaid in QML on top,
// exactly as the SVG stacks its white path over the gradient paths. Fully
// static: no glow, no breathing — the plate just sits there like the home
// widget's QPainter composite.
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf
{
mat4 qt_Matrix;
float qt_Opacity;
vec4 uStrong;
vec4 uSoft;
};
// The full-color logo's alpha channel acts as the silhouette mask: the
// gradient only appears inside the bird, exactly like the SVG's gradient paths.
layout(binding = 1) uniform sampler2D uSilhouette;
void main()
{
// Recreate cockatrice.svg's own gradient geometry (linearGradient4265-7-8,
// userSpaceOnUse): light AccentSoft at the start point S=(-8.097,-97.746),
// dark AccentStrong at the end E=(162.455,295.208), on the SVG's 300x300
// canvas. Normalized to UV space, V=E-S=(0.5685,1.3098), so
// t = dot(uv - S_norm, V)/|V|^2 with S_norm=(-0.0270,-0.3258).
float t = clamp(dot(qt_TexCoord0 - vec2(-0.02699, -0.32582), vec2(0.56851, 1.30985)) / 2.03891, 0.0, 1.0);
vec3 color = mix(uSoft.rgb, uStrong.rgb, t);
// Anti-aliased silhouette clip from the full-color logo's alpha.
float alpha = texture(uSilhouette, qt_TexCoord0).a;
fragColor = vec4(color * alpha, alpha) * qt_Opacity;
}

View file

@ -59,8 +59,8 @@ AppearanceSettingsPage::AppearanceSettingsPage()
connect(&schemeCombo, &QComboBox::currentIndexChanged, this,
[this] { themeManager->setColorScheme(schemeCombo.currentData().toString()); });
// Qt widget style; "Default" lets the application decide
styleCombo.addItem(tr("Default"), QStringLiteral("Default"));
// Qt widget style; "System" lets the application decide
styleCombo.addItem(tr("System"), QStringLiteral("System"));
for (const QString &key : QStyleFactory::keys()) {
styleCombo.addItem(key, key);
}
@ -132,6 +132,10 @@ AppearanceSettingsPage::AppearanceSettingsPage()
connect(&homeTabDisplayCardNameCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings.appearance(),
&AppearanceSettings::setHomeTabDisplayCardName);
homeTabBackgroundDimCheckBox.setChecked(settings.appearance().getHomeTabBackgroundDim());
connect(&homeTabBackgroundDimCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings.appearance(),
&AppearanceSettings::setHomeTabBackgroundDim);
for (const auto &entry : HomeTabButtonColor::all()) {
homeTabButtonColorSourceBox.addItem(QObject::tr(entry.trKey));
}
@ -150,6 +154,7 @@ AppearanceSettingsPage::AppearanceSettingsPage()
homeTabGrid->addWidget(&homeTabDisplayCardNameCheckBox, 2, 0, 1, 2);
homeTabGrid->addWidget(&homeTabButtonColorSourceLabel, 3, 0);
homeTabGrid->addWidget(&homeTabButtonColorSourceBox, 3, 1);
homeTabGrid->addWidget(&homeTabBackgroundDimCheckBox, 4, 0, 1, 2);
homeTabGroupBox = new QGroupBox;
homeTabGroupBox->setLayout(homeTabGrid);
@ -508,6 +513,9 @@ void AppearanceSettingsPage::retranslateUi()
homeTabBackgroundShuffleFrequencyLabel.setText(tr("Home tab background shuffle frequency:"));
homeTabBackgroundShuffleFrequencySpinBox.setSpecialValueText(tr("Disabled"));
homeTabDisplayCardNameCheckBox.setText(tr("Display card name of background in bottom right"));
homeTabBackgroundDimCheckBox.setText(tr("Dim the home tab background"));
homeTabBackgroundDimCheckBox.setToolTip(
tr("Draw a translucent overlay over the home tab background so buttons and text stand out"));
homeTabButtonColorSourceLabel.setText(tr("Home tab button color:"));
homeTabButtonColorSourceBox.setToolTip(
tr("Use the theme's identity accent colors, or extract colors from the background image"));

View file

@ -41,6 +41,7 @@ private:
QLabel homeTabBackgroundShuffleFrequencyLabel;
QSpinBox homeTabBackgroundShuffleFrequencySpinBox;
QCheckBox homeTabDisplayCardNameCheckBox;
QCheckBox homeTabBackgroundDimCheckBox;
QLabel homeTabButtonColorSourceLabel;
QComboBox homeTabButtonColorSourceBox;

View file

@ -2,7 +2,7 @@
#
# add themes subfolders
set(defthemes Default Fabric Fusion Leather Plasma VelvetMarble)
set(defthemes Fabric Fusion Leather Plasma VelvetMarble System)
if(UNIX)
if(APPLE)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

View file

@ -0,0 +1,74 @@
# Fabric — dark identity palette (navy-cloth chrome)
[Palette]
WindowText = #e8ecf2
Button = #2b374a
Light = #3a4759
Midlight = #333f50
Dark = #141a23
Mid = #252c38
Text = #e8ecf2
BrightText = #8ec2ff
ButtonText = #f2f4f8
Base = #222c3d
Window = #18202e
Shadow = #0b0f15
Highlight = #4a82e6
HighlightedText = #ffffff
Link = #7fb0f2
LinkVisited = #a98ad9
AlternateBase = #1d2636
ToolTipBase = #2b3546
ToolTipText = #f2f4f8
PlaceholderText = #6ee8ecf2
Accent = #4a82e6
[Palette.Disabled]
WindowText = #9d9d9d
Button = #18202e
Light = #3a4759
Midlight = #333f50
Dark = #141a23
Mid = #252c38
Text = #9d9d9d
BrightText = #8ec2ff
ButtonText = #787878
Base = #18202e
Window = #18202e
Shadow = #0b0f15
Highlight = #222d40
HighlightedText = #9d9d9d
Link = #308cc6
LinkVisited = #b450ff
AlternateBase = #1d2636
ToolTipBase = #2b3546
ToolTipText = #f2f4f8
PlaceholderText = #6ee8ecf2
Accent = #9d9d9d
[Palette.Inactive]
WindowText = #e8ecf2
Button = #2b374a
Light = #3a4759
Midlight = #333f50
Dark = #141a23
Mid = #252c38
Text = #e8ecf2
BrightText = #8ec2ff
ButtonText = #f2f4f8
Base = #222c3d
Window = #18202e
Shadow = #0b0f15
Highlight = #222d40
HighlightedText = #ffffff
Link = #7fb0f2
LinkVisited = #a98ad9
AlternateBase = #1d2636
ToolTipBase = #2b3546
ToolTipText = #f2f4f8
PlaceholderText = #6ee8ecf2
Accent = #4a82e6
[AppColors]
AccentStrong = #3a6fd6
AccentSoft = #8fb8f2

View file

@ -0,0 +1,74 @@
# Fabric — light identity palette (navy-cloth chrome)
[Palette]
WindowText = #1c2330
Button = #c8d6ea
Light = #ffffff
Midlight = #b9c6dc
Dark = #8fa0bd
Mid = #a3b3cd
Text = #1c2330
BrightText = #1d4fa0
ButtonText = #141c29
Base = #f6f8fc
Window = #dbe3ef
Shadow = #5a6a85
Highlight = #2f6fd6
HighlightedText = #ffffff
Link = #2555b0
LinkVisited = #6a4bb8
AlternateBase = #e9eef7
ToolTipBase = #e9eff8
ToolTipText = #141c29
PlaceholderText = #7a1c2330
Accent = #2f6fd6
[Palette.Disabled]
WindowText = #787878
Button = #dbe3ef
Light = #ffffff
Midlight = #b9c6dc
Dark = #8fa0bd
Mid = #a3b3cd
Text = #787878
BrightText = #1d4fa0
ButtonText = #969696
Base = #dbe3ef
Window = #dbe3ef
Shadow = #5a6a85
Highlight = #cdd8e9
HighlightedText = #787878
Link = #0000ff
LinkVisited = #ff00ff
AlternateBase = #e9eef7
ToolTipBase = #e9eff8
ToolTipText = #141c29
PlaceholderText = #7a1c2330
Accent = #787878
[Palette.Inactive]
WindowText = #1c2330
Button = #c8d6ea
Light = #ffffff
Midlight = #b9c6dc
Dark = #8fa0bd
Mid = #a3b3cd
Text = #1c2330
BrightText = #1d4fa0
ButtonText = #141c29
Base = #f6f8fc
Window = #dbe3ef
Shadow = #5a6a85
Highlight = #cdd8e9
HighlightedText = #000000
Link = #2555b0
LinkVisited = #6a4bb8
AlternateBase = #e9eef7
ToolTipBase = #e9eff8
ToolTipText = #141c29
PlaceholderText = #7a1c2330
Accent = #2f6fd6
[AppColors]
AccentStrong = #3a6fd6
AccentSoft = #8fb8f2

View file

@ -0,0 +1,5 @@
[Appearance]
ColorScheme = System
[Style]
Name = Fusion

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Before After
Before After

View file

@ -11,15 +11,15 @@ ButtonText = #ffffffff
Base = #ff2d2d2d
Window = #ff1e1e1e
Shadow = #ff000000
Highlight = #ff148c3c
Highlight = #ff139740
HighlightedText = #ffffffff
Link = #ff00f652
LinkVisited = #ff00d346
Link = #ffc9fd62
LinkVisited = #ff9ad43e
AlternateBase = #ff353535
ToolTipBase = #ff3c3c3c
ToolTipText = #ffd4d4d4
PlaceholderText = #80ffffff
Accent = #ff00d346
Accent = #ffc9fd62
[Palette.Disabled]
WindowText = #ff9d9d9d
@ -34,7 +34,7 @@ ButtonText = #ff9d9d9d
Base = #ff1e1e1e
Window = #ff1e1e1e
Shadow = #ff000000
Highlight = #ff148c3c
Highlight = #ff139740
HighlightedText = #ffffffff
Link = #ff308cc6
LinkVisited = #ffff00ff
@ -59,8 +59,8 @@ Window = #ff1e1e1e
Shadow = #ff000000
Highlight = #ff1e1e1e
HighlightedText = #ffffffff
Link = #ff00f652
LinkVisited = #ff00d346
Link = #ffc9fd62
LinkVisited = #ff9ad43e
AlternateBase = #ff353535
ToolTipBase = #ff3c3c3c
ToolTipText = #ffd4d4d4
@ -69,5 +69,5 @@ Accent = #ff1e1e1e
[AppColors]
AccentStrong = #ff148c3c
AccentSoft = #ff78c850
AccentStrong = #ff139740
AccentSoft = #ffc9fd62

View file

@ -11,15 +11,15 @@ ButtonText = #ff000000
Base = #ffffffff
Window = #fff0f0f0
Shadow = #ff696969
Highlight = #ff148c3c
Highlight = #ff139740
HighlightedText = #ffffffff
Link = #ff0d5f28
LinkVisited = #ff08401b
Link = #ff0e6b31
LinkVisited = #ff0a521f
AlternateBase = #ffe9e7e3
ToolTipBase = #ffffffdc
ToolTipText = #ff000000
PlaceholderText = #80000000
Accent = #ff107532
Accent = #ff139740
[Palette.Disabled]
WindowText = #ff787878
@ -34,7 +34,7 @@ ButtonText = #ff787878
Base = #fff0f0f0
Window = #fff0f0f0
Shadow = #ff000000
Highlight = #ff148c3c
Highlight = #ff139740
HighlightedText = #ffffffff
Link = #ff0000ff
LinkVisited = #ffff00ff
@ -59,8 +59,8 @@ Window = #fff0f0f0
Shadow = #ff696969
Highlight = #fff0f0f0
HighlightedText = #ff000000
Link = #ff0d5f28
LinkVisited = #ff08401b
Link = #ff0e6b31
LinkVisited = #ff0a521f
AlternateBase = #ffe9e7e3
ToolTipBase = #ffffffdc
ToolTipText = #ff000000
@ -69,5 +69,5 @@ Accent = #fff0f0f0
[AppColors]
AccentStrong = #ff148c3c
AccentSoft = #ff78c850
AccentStrong = #ff139740
AccentSoft = #ffc9fd62

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View file

@ -0,0 +1,74 @@
# Leather — dark identity palette (black-brown chrome with brass accents)
[Palette]
WindowText = #eee9e2
Button = #332d26
Light = #4a4239
Midlight = #3d3730
Dark = #131009
Mid = #26221c
Text = #eee9e2
BrightText = #e3c27e
ButtonText = #f5f1ea
Base = #26221d
Window = #1d1a16
Shadow = #0d0b07
Highlight = #4a5f8f
HighlightedText = #ffffff
Link = #cfa263
LinkVisited = #9a7db8
AlternateBase = #211d19
ToolTipBase = #37302a
ToolTipText = #f5f1ea
PlaceholderText = #6eeee9e2
Accent = #c9995a
[Palette.Disabled]
WindowText = #9d9d9d
Button = #1d1a16
Light = #4a4239
Midlight = #3d3730
Dark = #131009
Mid = #26221c
Text = #9d9d9d
BrightText = #e3c27e
ButtonText = #787878
Base = #1d1a16
Window = #1d1a16
Shadow = #0d0b07
Highlight = #2d2822
HighlightedText = #9d9d9d
Link = #308cc6
LinkVisited = #b450ff
AlternateBase = #211d19
ToolTipBase = #37302a
ToolTipText = #f5f1ea
PlaceholderText = #6eeee9e2
Accent = #9d9d9d
[Palette.Inactive]
WindowText = #eee9e2
Button = #332d26
Light = #4a4239
Midlight = #3d3730
Dark = #131009
Mid = #26221c
Text = #eee9e2
BrightText = #e3c27e
ButtonText = #f5f1ea
Base = #26221d
Window = #1d1a16
Shadow = #0d0b07
Highlight = #2d2822
HighlightedText = #ffffff
Link = #cfa263
LinkVisited = #9a7db8
AlternateBase = #211d19
ToolTipBase = #37302a
ToolTipText = #f5f1ea
PlaceholderText = #6eeee9e2
Accent = #c9995a
[AppColors]
AccentStrong = #b7823b
AccentSoft = #e4c58f

View file

@ -0,0 +1,74 @@
# Leather — light identity palette (black-brown chrome with brass accents)
[Palette]
WindowText = #2a2114
Button = #dfcbb0
Light = #ffffff
Midlight = #c6b08c
Dark = #a18a64
Mid = #b29a74
Text = #2a2114
BrightText = #7a531c
ButtonText = #1f1710
Base = #fdf8ee
Window = #f0e2cb
Shadow = #5d4d33
Highlight = #34508c
HighlightedText = #ffffff
Link = #8a5e1e
LinkVisited = #6b5190
AlternateBase = #f5ebd7
ToolTipBase = #fff7e8
ToolTipText = #1f1710
PlaceholderText = #7a2a2114
Accent = #a5712f
[Palette.Disabled]
WindowText = #787878
Button = #f0e2cb
Light = #ffffff
Midlight = #c6b08c
Dark = #a18a64
Mid = #b29a74
Text = #787878
BrightText = #7a531c
ButtonText = #969696
Base = #f0e2cb
Window = #f0e2cb
Shadow = #5d4d33
Highlight = #ecd9bb
HighlightedText = #787878
Link = #0000ff
LinkVisited = #ff00ff
AlternateBase = #f5ebd7
ToolTipBase = #fff7e8
ToolTipText = #1f1710
PlaceholderText = #7a2a2114
Accent = #787878
[Palette.Inactive]
WindowText = #2a2114
Button = #dfcbb0
Light = #ffffff
Midlight = #c6b08c
Dark = #a18a64
Mid = #b29a74
Text = #2a2114
BrightText = #7a531c
ButtonText = #1f1710
Base = #fdf8ee
Window = #f0e2cb
Shadow = #5d4d33
Highlight = #ecd9bb
HighlightedText = #000000
Link = #8a5e1e
LinkVisited = #6b5190
AlternateBase = #f5ebd7
ToolTipBase = #fff7e8
ToolTipText = #1f1710
PlaceholderText = #7a2a2114
Accent = #a5712f
[AppColors]
AccentStrong = #b7823b
AccentSoft = #e4c58f

View file

@ -0,0 +1,5 @@
[Appearance]
ColorScheme = System
[Style]
Name = Fusion

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View file

@ -0,0 +1,74 @@
# Plasma — dark identity palette (electric violet chrome with cyan sparks)
[Palette]
WindowText = #eeebff
Button = #2a2440
Light = #453d63
Midlight = #383252
Dark = #0e0c16
Mid = #211e31
Text = #eeebff
BrightText = #7fd7ff
ButtonText = #f6f4ff
Base = #1e1a2e
Window = #151220
Shadow = #08070e
Highlight = #6a4df0
HighlightedText = #ffffff
Link = #37c7e8
LinkVisited = #9b7aff
AlternateBase = #1a1726
ToolTipBase = #2c2745
ToolTipText = #f6f4ff
PlaceholderText = #6eeeebff
Accent = #6a4df0
[Palette.Disabled]
WindowText = #9d9d9d
Button = #151220
Light = #453d63
Midlight = #383252
Dark = #0e0c16
Mid = #211e31
Text = #9d9d9d
BrightText = #7fd7ff
ButtonText = #787878
Base = #151220
Window = #151220
Shadow = #08070e
Highlight = #211c32
HighlightedText = #9d9d9d
Link = #308cc6
LinkVisited = #b450ff
AlternateBase = #1a1726
ToolTipBase = #2c2745
ToolTipText = #f6f4ff
PlaceholderText = #6eeeebff
Accent = #9d9d9d
[Palette.Inactive]
WindowText = #eeebff
Button = #2a2440
Light = #453d63
Midlight = #383252
Dark = #0e0c16
Mid = #211e31
Text = #eeebff
BrightText = #7fd7ff
ButtonText = #f6f4ff
Base = #1e1a2e
Window = #151220
Shadow = #08070e
Highlight = #211c32
HighlightedText = #ffffff
Link = #37c7e8
LinkVisited = #9b7aff
AlternateBase = #1a1726
ToolTipBase = #2c2745
ToolTipText = #f6f4ff
PlaceholderText = #6eeeebff
Accent = #6a4df0
[AppColors]
AccentStrong = #5b3ee0
AccentSoft = #a28bf7

View file

@ -0,0 +1,74 @@
# Plasma — light identity palette (electric violet chrome with cyan sparks)
[Palette]
WindowText = #1d1830
Button = #cfc6f0
Light = #ffffff
Midlight = #b7adde
Dark = #9589c8
Mid = #a49ad2
Text = #1d1830
BrightText = #10406e
ButtonText = #120d26
Base = #f9f7ff
Window = #e3dcf7
Shadow = #554c85
Highlight = #5a3ee2
HighlightedText = #ffffff
Link = #0f8fb5
LinkVisited = #6a45d6
AlternateBase = #ece8fa
ToolTipBase = #f5f3ff
ToolTipText = #120d26
PlaceholderText = #7a1d1830
Accent = #5a3ee2
[Palette.Disabled]
WindowText = #787878
Button = #e3dcf7
Light = #ffffff
Midlight = #b7adde
Dark = #9589c8
Mid = #a49ad2
Text = #787878
BrightText = #10406e
ButtonText = #969696
Base = #e3dcf7
Window = #e3dcf7
Shadow = #554c85
Highlight = #d6ccf3
HighlightedText = #787878
Link = #0000ff
LinkVisited = #ff00ff
AlternateBase = #ece8fa
ToolTipBase = #f5f3ff
ToolTipText = #120d26
PlaceholderText = #7a1d1830
Accent = #787878
[Palette.Inactive]
WindowText = #1d1830
Button = #cfc6f0
Light = #ffffff
Midlight = #b7adde
Dark = #9589c8
Mid = #a49ad2
Text = #1d1830
BrightText = #10406e
ButtonText = #120d26
Base = #f9f7ff
Window = #e3dcf7
Shadow = #554c85
Highlight = #d6ccf3
HighlightedText = #000000
Link = #0f8fb5
LinkVisited = #6a45d6
AlternateBase = #ece8fa
ToolTipBase = #f5f3ff
ToolTipText = #120d26
PlaceholderText = #7a1d1830
Accent = #5a3ee2
[AppColors]
AccentStrong = #5b3ee0
AccentSoft = #a28bf7

View file

@ -0,0 +1,5 @@
[Appearance]
ColorScheme = System
[Style]
Name = Fusion

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

@ -2,4 +2,4 @@
ColorScheme = Light
[Style]
Name = Default
Name = System

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View file

@ -0,0 +1,74 @@
# VelvetMarble — dark identity palette (charcoal-velvet chrome with slate marble)
[Palette]
WindowText = #e8eaee
Button = #262a31
Light = #3a3f47
Midlight = #31363d
Dark = #0e1013
Mid = #1f2329
Text = #e8eaee
BrightText = #c3d8f2
ButtonText = #f2f3f6
Base = #1b1e23
Window = #131519
Shadow = #08090b
Highlight = #8fa6c0
HighlightedText = #101318
Link = #a9c6e8
LinkVisited = #b9a8d9
AlternateBase = #171a1f
ToolTipBase = #2d323a
ToolTipText = #f2f3f6
PlaceholderText = #6ee8eaee
Accent = #8fa6c0
[Palette.Disabled]
WindowText = #9d9d9d
Button = #131519
Light = #3a3f47
Midlight = #31363d
Dark = #0e1013
Mid = #1f2329
Text = #9d9d9d
BrightText = #c3d8f2
ButtonText = #787878
Base = #131519
Window = #131519
Shadow = #08090b
Highlight = #1f2229
HighlightedText = #9d9d9d
Link = #308cc6
LinkVisited = #b450ff
AlternateBase = #171a1f
ToolTipBase = #2d323a
ToolTipText = #f2f3f6
PlaceholderText = #6ee8eaee
Accent = #9d9d9d
[Palette.Inactive]
WindowText = #e8eaee
Button = #262a31
Light = #3a3f47
Midlight = #31363d
Dark = #0e1013
Mid = #1f2329
Text = #e8eaee
BrightText = #c3d8f2
ButtonText = #f2f3f6
Base = #1b1e23
Window = #131519
Shadow = #08090b
Highlight = #1f2229
HighlightedText = #ffffff
Link = #a9c6e8
LinkVisited = #b9a8d9
AlternateBase = #171a1f
ToolTipBase = #2d323a
ToolTipText = #f2f3f6
PlaceholderText = #6ee8eaee
Accent = #8fa6c0
[AppColors]
AccentStrong = #54687e
AccentSoft = #9db0c4

View file

@ -0,0 +1,74 @@
# VelvetMarble — light identity palette (charcoal-velvet chrome with slate marble)
[Palette]
WindowText = #1b1d21
Button = #c7d1dd
Light = #ffffff
Midlight = #b3becb
Dark = #93a0b1
Mid = #a1adbc
Text = #1b1d21
BrightText = #26465f
ButtonText = #121418
Base = #f7f9fb
Window = #d9e0ea
Shadow = #57626f
Highlight = #54687e
HighlightedText = #ffffff
Link = #2e4d6b
LinkVisited = #5d4a78
AlternateBase = #e6ebf2
ToolTipBase = #f2f4f6
ToolTipText = #121418
PlaceholderText = #7a1b1d21
Accent = #54687e
[Palette.Disabled]
WindowText = #787878
Button = #d9e0ea
Light = #ffffff
Midlight = #b3becb
Dark = #93a0b1
Mid = #a1adbc
Text = #787878
BrightText = #26465f
ButtonText = #969696
Base = #d9e0ea
Window = #d9e0ea
Shadow = #57626f
Highlight = #ccd5e3
HighlightedText = #787878
Link = #0000ff
LinkVisited = #ff00ff
AlternateBase = #e6ebf2
ToolTipBase = #f2f4f6
ToolTipText = #121418
PlaceholderText = #7a1b1d21
Accent = #787878
[Palette.Inactive]
WindowText = #1b1d21
Button = #c7d1dd
Light = #ffffff
Midlight = #b3becb
Dark = #93a0b1
Mid = #a1adbc
Text = #1b1d21
BrightText = #26465f
ButtonText = #121418
Base = #f7f9fb
Window = #d9e0ea
Shadow = #57626f
Highlight = #ccd5e3
HighlightedText = #000000
Link = #2e4d6b
LinkVisited = #5d4a78
AlternateBase = #e6ebf2
ToolTipBase = #f2f4f6
ToolTipText = #121418
PlaceholderText = #7a1b1d21
Accent = #54687e
[AppColors]
AccentStrong = #54687e
AccentSoft = #9db0c4

View file

@ -0,0 +1,5 @@
[Appearance]
ColorScheme = System
[Style]
Name = Fusion

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View file

@ -70,6 +70,17 @@ void AppearanceSettings::setHomeTabDisplayCardName(bool _displayCardName)
emit homeTabDisplayCardNameChanged();
}
bool AppearanceSettings::getHomeTabBackgroundDim() const
{
return getValue("homeTabBackgroundDim", QString(), QString(), true).toBool();
}
void AppearanceSettings::setHomeTabBackgroundDim(bool _dimBackground)
{
setValue(_dimBackground, "homeTabBackgroundDim");
emit homeTabBackgroundDimChanged();
}
int AppearanceSettings::getHomeTabButtonColorSourceIndex() const
{
return getValue("homeTabButtonColorSource", "", "", 0).toInt();

View file

@ -27,6 +27,8 @@ public:
void setHomeTabBackgroundShuffleFrequency(int _frequency);
[[nodiscard]] bool getHomeTabDisplayCardName() const;
void setHomeTabDisplayCardName(bool _displayCardName);
[[nodiscard]] bool getHomeTabBackgroundDim() const;
void setHomeTabBackgroundDim(bool _dimBackground);
[[nodiscard]] int getHomeTabButtonColorSourceIndex() const;
void setHomeTabButtonColorSourceIndex(int index);
@ -36,6 +38,7 @@ signals:
void homeTabBackgroundSourceChanged();
void homeTabBackgroundShuffleFrequencyChanged();
void homeTabDisplayCardNameChanged();
void homeTabBackgroundDimChanged();
void homeTabButtonColorChanged();
public:

View file

@ -374,7 +374,11 @@ static void migrateAppearanceSettings(const QString &settingsPath, QSettings &gl
QSettings appearanceIni(settingsPath + "appearance.ini", QSettings::IniFormat);
for (auto it = appearanceKeyMap.constBegin(); it != appearanceKeyMap.constEnd(); ++it) {
if (globalIni.contains(it.key())) {
appearanceIni.setValue(it.value(), globalIni.value(it.key()));
QVariant value = globalIni.value(it.key());
if (it.key() == "theme/name" && value.toString() == "Default") {
value = "System";
}
appearanceIni.setValue(it.value(), value);
}
}
}

View file

@ -386,6 +386,19 @@ TEST_F(SettingsDefaultsTest, Appearance_HomeTabDisplayCardName_Default)
ASSERT_EQ(s.getHomeTabDisplayCardName(), true);
}
TEST_F(SettingsDefaultsTest, Appearance_HomeTabBackgroundDim_Default)
{
AppearanceSettings s(settingsPath, nullptr);
ASSERT_EQ(s.getHomeTabBackgroundDim(), true);
}
TEST_F(SettingsDefaultsTest, Appearance_HomeTabBackgroundDim_SetAndGet)
{
AppearanceSettings s(settingsPath, nullptr);
s.setHomeTabBackgroundDim(false);
ASSERT_EQ(s.getHomeTabBackgroundDim(), false);
}
// --- InterfaceSettings ---
TEST_F(SettingsDefaultsTest, Interface_ShowStatusBar_Default)