[AppColors] Address review comments

- PaletteEditorDialog::onSave(): compare whole PaletteConfig (colors and
  appColors) so a change to only AccentStrong/AccentSoft writes the file;
  add PaletteConfig::operator==.
- appColor(): derive both roles from QPalette::Highlight unconditionally.
  The Fusion palettes pin Accent to near-Window values, and QPalette::Accent
  only exists on Qt 6.6+, so keying on it made identical themes render very
  differently across Qt versions.
- themeChangedSlot(): merge the theme default's [AppColors] into a custom
  palette that predates the section instead of all-or-nothing per file;
  hasPalette() now counts an appColors-only file as a palette.
- Add Default/palette-default-light.toml so the Default theme's Light scheme
  keeps the classic greens instead of falling back to the OS accent.
- home_widget: restore the isBuiltInTheme() half of the Automatic condition;
  non-built-in themes extract button colors from their own background art.
- palette_grid_widget: use appEnum.value(i) for the role cast (3 sites),
  append appHeader to headerLabels, fix the 'Lighted' typo.
This commit is contained in:
Lukas Brübach 2026-09-10 21:03:42 +02:00 committed by GitHub
parent 03684bf41d
commit 7549f9c2cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 107 additions and 19 deletions

View file

@ -297,7 +297,7 @@ void PaletteEditorDialog::onSave()
if (it.key() == loadedScheme) {
continue;
}
if (it.value().colors == savedConfig.value(it.key()).colors) {
if (it.value() == savedConfig.value(it.key())) {
continue;
}
if (!ThemeManager::commitPalette(saveDir, it.key(), it.value())) {
@ -308,7 +308,7 @@ void PaletteEditorDialog::onSave()
}
// Commit the active scheme last so the global colour scheme matches.
if (workingConfig[loadedScheme].colors != savedConfig.value(loadedScheme).colors) {
if (workingConfig[loadedScheme] != savedConfig.value(loadedScheme)) {
if (!ThemeManager::commitPalette(saveDir, loadedScheme, workingConfig[loadedScheme])) {
QMessageBox::warning(this, tr("Save failed"),
tr("Could not write %1 to:\n%2").arg(PaletteConfig::fileName(loadedScheme), saveDir));

View file

@ -49,7 +49,7 @@ static const QMap<QPalette::ColorRole, const char *> ROLE_DESCRIPTIONS = {
static const QMap<AppColor::Role, const char *> APP_ROLE_DESCRIPTIONS = {
{AppColor::AccentStrong, QT_TR_NOOP("Vivid primary accent (e.g. home-tab button gradient start)")},
{AppColor::AccentSoft, QT_TR_NOOP("Lighted, desaturated accent (e.g. home-tab button gradient end)")},
{AppColor::AccentSoft, QT_TR_NOOP("Lightened, desaturated accent (e.g. home-tab button gradient end)")},
};
PaletteGridWidget::PaletteGridWidget(QWidget *parent) : QWidget(parent)
@ -144,9 +144,10 @@ void PaletteGridWidget::buildGrid(QWidget *host)
appHeader->setAutoFillBackground(true);
appHeader->setContentsMargins(4, 4, 4, 4);
grid->addWidget(appHeader, appHeaderRow, 0, 1, 4);
headerLabels.append(appHeader);
for (int i = 0; i < appEnum.keyCount(); ++i) {
auto role = static_cast<AppColor::Role>(i);
auto role = static_cast<AppColor::Role>(appEnum.value(i));
const int row = appHeaderRow + 1 + i;
if (i % 2 == 0) {
@ -215,7 +216,7 @@ void PaletteGridWidget::loadPalette(const PaletteConfig &cfg)
QMetaEnum appEnum = QMetaEnum::fromType<AppColor::Role>();
for (int i = 0; i < appEnum.keyCount(); ++i) {
auto role = static_cast<AppColor::Role>(i);
auto role = static_cast<AppColor::Role>(appEnum.value(i));
QColor color = cfg.appColors.value(role);
if (!color.isValid()) {
color = themeManager->appColor(role);
@ -235,7 +236,7 @@ PaletteConfig PaletteGridWidget::currentPaletteConfig() const
QMetaEnum appEnum = QMetaEnum::fromType<AppColor::Role>();
for (int i = 0; i < appEnum.keyCount(); ++i) {
auto role = static_cast<AppColor::Role>(i);
auto role = static_cast<AppColor::Role>(appEnum.value(i));
cfg.appColors[role] = appColorButtons[role]->getColor();
}

View file

@ -96,7 +96,7 @@ bool ThemeConfig::save(const QString &themeDirPath) const
bool PaletteConfig::hasPalette() const
{
return !colors.isEmpty();
return !colors.isEmpty() || !appColors.isEmpty();
}
QString PaletteConfig::toToml() const

View file

@ -39,6 +39,14 @@ struct PaletteConfig
QMap<QPalette::ColorGroup, QMap<QPalette::ColorRole, QColor>> colors;
QMap<AppColor::Role, QColor> appColors;
bool operator==(const PaletteConfig &rhs) const
{
return colors == rhs.colors && appColors == rhs.appColors;
}
bool operator!=(const PaletteConfig &rhs) const
{
return !(*this == rhs);
}
bool hasPalette() const;
QString toToml() const;

View file

@ -441,14 +441,12 @@ QColor ThemeManager::appColor(AppColor::Role role) const
return it.value();
}
// QPalette::Accent was introduced in Qt 6.6; before that the nearest
// accent is the selection highlight, which Accent defaults to when unset.
const QColor accent = qApp->palette().color(QPalette::Active,
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
QPalette::Accent);
#else
QPalette::Highlight);
#endif
// QPalette::Accent was introduced in Qt 6.6 and several shipped palettes
// set it to a value barely distinguishable from Window, so it is not a
// reliable accent source. The selection highlight is the stable accent
// (Accent defaults to Highlight when unset), and deriving from it
// unconditionally keeps every Qt version rendering identically.
const QColor accent = qApp->palette().color(QPalette::Active, QPalette::Highlight);
if (role == AppColor::AccentSoft) {
constexpr int SOFT_SATURATION_PERCENT = 70;
@ -497,8 +495,19 @@ void ThemeManager::themeChangedSlot()
// ── Load palette: custom first, then theme default ────────────────────
PaletteConfig palette = PaletteConfig::fromScheme(dirPath, activeScheme);
if (!palette.hasPalette()) {
palette = ThemeManager::loadDefaultPaletteConfig(dirPath, themeName, activeScheme);
const PaletteConfig themeDefault = ThemeManager::loadDefaultPaletteConfig(dirPath, themeName, activeScheme);
if (palette.hasPalette()) {
// A custom palette written before [AppColors] existed carries no app
// colors; merge the theme's shipped defaults so the identity colors
// survive (hasPalette() counts an app-colors-only file as a palette,
// so those are kept wholesale and never reach here empty).
for (auto it = themeDefault.appColors.cbegin(); it != themeDefault.appColors.cend(); ++it) {
if (!palette.appColors.contains(it.key())) {
palette.appColors.insert(it.key(), it.value());
}
}
} else {
palette = themeDefault;
}
applyStyleAndPalette(themeName, themeCfg, palette, activeScheme);

View file

@ -124,10 +124,13 @@ QPair<QColor, QColor> HomeWidget::determineButtonColor() const
switch (colorSource) {
case HomeTabButtonColor::Automatic: {
if (usesThemeBackground()) {
// Static theme background: follow the theme's accent colors.
if (usesThemeBackground() && themeManager->isBuiltInTheme()) {
// Built-in themes paint a static theme background; follow the
// theme's identity accent colors rather than sampling the image.
return paletteDerivedButtonColors();
} else {
// Non-built-in themes may ship their own background art, so
// extract the button colors from the image actually painted.
return extractDominantColors(background);
}
}

View file

@ -0,0 +1,67 @@
[Palette]
WindowText = #ff000000
Button = #fff0f0f0
Light = #ffffffff
Midlight = #ffe3e3e3
Dark = #ffa0a0a0
Mid = #ffa0a0a0
Text = #ff000000
BrightText = #ffffffff
ButtonText = #ff000000
Base = #ffffffff
Window = #fff0f0f0
Shadow = #ff696969
HighlightedText = #ffffffff
Link = #ff0d5f28
LinkVisited = #ff08401b
AlternateBase = #ffe9e7e3
ToolTipBase = #ffffffdc
ToolTipText = #ff000000
PlaceholderText = #80000000
[Palette.Disabled]
WindowText = #ff787878
Button = #fff0f0f0
Light = #ffffffff
Midlight = #fff7f7f7
Dark = #ffa0a0a0
Mid = #ffa0a0a0
Text = #ff787878
BrightText = #ffffffff
ButtonText = #ff787878
Base = #fff0f0f0
Window = #fff0f0f0
Shadow = #ff000000
HighlightedText = #ffffffff
Link = #ff0000ff
LinkVisited = #ffff00ff
AlternateBase = #fff7f7f7
ToolTipBase = #ffffffdc
ToolTipText = #ff000000
PlaceholderText = #80000000
[Palette.Inactive]
WindowText = #ff000000
Button = #fff0f0f0
Light = #ffffffff
Midlight = #ffe3e3e3
Dark = #ffa0a0a0
Mid = #ffa0a0a0
Text = #ff000000
BrightText = #ffffffff
ButtonText = #ff000000
Base = #ffffffff
Window = #fff0f0f0
Shadow = #ff696969
HighlightedText = #ff000000
Link = #ff0d5f28
LinkVisited = #ff08401b
AlternateBase = #ffe9e7e3
ToolTipBase = #ffffffdc
ToolTipText = #ff000000
PlaceholderText = #80000000
[AppColors]
AccentStrong = #ff148c3c
AccentSoft = #ff78c850