mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-11 12:54:10 -07:00
[App/Theme] Palette Editor (#6877)
* [App/Theme] Palette Editor Took 1 minute Took 1 hour 47 minutes Took 6 seconds Took 3 minutes Took 5 minutes Took 3 minutes * Add oracle, add palette files and configs. Took 10 minutes * Fix a stupid include mistake, thanks IDE Took 3 minutes Took 20 seconds * Includes. Took 4 minutes * Fix ampersand not displaying correctly. Took 14 minutes * Longer variable names. Took 10 minutes Took 5 seconds * Change ampersand everywhere Took 23 seconds * Doxygen properly. Took 1 minute * Remove namespace, fold I/O into structs. Took 12 minutes * Remove namespace, fold I/O into structs. Took 33 seconds * Alphabetize. Took 35 seconds * Lint. Took 49 seconds * Add a combo box to quick switch settings. Took 19 minutes --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
989a5be23b
commit
117ea543c5
23 changed files with 1874 additions and 232 deletions
200
cockatrice/src/interface/palette_editor/palette_generator.cpp
Normal file
200
cockatrice/src/interface/palette_editor/palette_generator.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
#include "palette_generator.h"
|
||||
|
||||
#include <QColor>
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// PaletteGenerator::fromAccent
|
||||
//
|
||||
// Three intensity bands:
|
||||
// 0–30 Subtle — Highlight / links / BrightText take on the hue;
|
||||
// backgrounds stay neutral grey.
|
||||
// 30–70 Accented — Button, ToolTipBase, AlternateBase, shading tint in;
|
||||
// backgrounds get a faint hue wash.
|
||||
// 70–100 Chromatic— Window and Base pick up real saturation; the whole
|
||||
// application reads as that colour, text stays readable.
|
||||
//
|
||||
// Key principles:
|
||||
// • Quadratic saturation curve: low end feels truly subtle, top is vivid.
|
||||
// • Each role has its own saturation ceiling; buttons always pop above window.
|
||||
// • Base stays near-white / near-black regardless of intensity for legibility.
|
||||
// • Button text contrast is computed from the real button color, not assumed.
|
||||
// • Tooltip blends from classic yellow to hue-tinted above intensity ~25.
|
||||
// • 3D shading ladder (Light→Shadow) carries the same hue for coherence.
|
||||
// • Disabled: text → mid-gray, backgrounds → match Window.
|
||||
// • Inactive Highlight fades to a near-bg tone so it doesn't compete.
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
namespace PaletteGenerator
|
||||
{
|
||||
|
||||
PaletteConfig fromAccent(const QColor &accent, int intensity, const QString &scheme)
|
||||
{
|
||||
PaletteConfig cfg;
|
||||
const bool dark = scheme.compare("Dark", Qt::CaseInsensitive) == 0;
|
||||
const double t = intensity / 100.0; // 0.0 – 1.0
|
||||
|
||||
int h = accent.hslHue();
|
||||
const bool achromatic = (h < 0);
|
||||
if (achromatic) {
|
||||
h = 0;
|
||||
}
|
||||
|
||||
// Saturation budgets
|
||||
// Quadratic ease-in means the subtle end is genuinely subtle and the
|
||||
// full end is bold without being garish.
|
||||
auto sat = [&](double maxSat) -> int {
|
||||
if (achromatic) {
|
||||
return 0;
|
||||
}
|
||||
return qRound(maxSat * t * t);
|
||||
};
|
||||
|
||||
const int satWindow = sat(dark ? 80.0 : 90.0);
|
||||
const int satBase = sat(dark ? 25.0 : 20.0); // text areas stay near-white/black
|
||||
const int satAlt = sat(dark ? 90.0 : 100.0);
|
||||
const int satButton = sat(dark ? 120.0 : 130.0); // buttons pop above the bg
|
||||
const int satTooltip = sat(dark ? 90.0 : 80.0);
|
||||
const int satHighlight = achromatic ? 0 : qRound(accent.hslSaturation() * (0.45 + 0.55 * t));
|
||||
const int satShadeHi = sat(dark ? 60.0 : 50.0); // Light / Midlight
|
||||
const int satShadeLo = sat(dark ? 90.0 : 70.0); // Mid / Dark
|
||||
|
||||
// Per-role lightness
|
||||
// Nudge lightness slightly as saturation rises to compensate for the
|
||||
// Helmholtz-Kohlrausch effect (saturated colors look lighter/heavier).
|
||||
const int winL = dark ? (28 + qRound(t * 8)) : (242 - qRound(t * 6));
|
||||
const int baseL = dark ? (43 + qRound(t * 6)) : 252;
|
||||
const int altL = dark ? (36 + qRound(t * 9)) : (234 - qRound(t * 5));
|
||||
const int btnL = dark ? (56 + qRound(t * 10)) : (230 - qRound(t * 10));
|
||||
const int tipL = dark ? (52 + qRound(t * 8)) : (248 - qRound(t * 6));
|
||||
|
||||
// Highlight color
|
||||
QColor hl;
|
||||
if (achromatic) {
|
||||
hl = dark ? QColor(105, 105, 105) : QColor(95, 95, 95);
|
||||
} else if (dark) {
|
||||
int L = qBound(105, accent.lightness() + qRound(45.0 * (1.0 - t)), 215);
|
||||
hl = QColor::fromHsl(h, qMin(255, satHighlight), L);
|
||||
} else {
|
||||
int L = qBound(50, accent.lightness() - qRound(25.0 * t), 180);
|
||||
hl = QColor::fromHsl(h, qMin(255, satHighlight), L);
|
||||
}
|
||||
const double hlLuma = 0.299 * hl.red() + 0.587 * hl.green() + 0.114 * hl.blue();
|
||||
const QColor hlText = (hlLuma > 135) ? Qt::black : Qt::white;
|
||||
|
||||
// Local helpers
|
||||
using CR = QPalette::ColorRole;
|
||||
using CG = QPalette::ColorGroup;
|
||||
|
||||
auto hsl = [&](int lightness, int s) -> QColor {
|
||||
return QColor::fromHsl(h, qBound(0, s, 255), qBound(0, lightness, 255));
|
||||
};
|
||||
|
||||
auto textOn = [](const QColor &bg) -> QColor {
|
||||
double luma = 0.299 * bg.red() + 0.587 * bg.green() + 0.114 * bg.blue();
|
||||
return (luma > 135) ? Qt::black : Qt::white;
|
||||
};
|
||||
|
||||
auto set3 = [&](CR role, QColor active, QColor disabled, QColor inactive) {
|
||||
cfg.colors[CG::Active][role] = active;
|
||||
cfg.colors[CG::Disabled][role] = disabled;
|
||||
cfg.colors[CG::Inactive][role] = inactive;
|
||||
};
|
||||
|
||||
auto setAll = [&](CR role, QColor c) { set3(role, c, c, c); };
|
||||
|
||||
// Tooltip: blend classic yellow → hue-tinted above t≈0.20
|
||||
QColor bg_tip;
|
||||
if (achromatic || t < 0.20) {
|
||||
bg_tip = QColor(255, 255, 220);
|
||||
} else {
|
||||
QColor tinted = hsl(tipL, satTooltip);
|
||||
double blend = qMin(1.0, (t - 0.20) / 0.55);
|
||||
QColor yellow(255, 255, 220);
|
||||
bg_tip = QColor(qRound(yellow.red() * (1.0 - blend) + tinted.red() * blend),
|
||||
qRound(yellow.green() * (1.0 - blend) + tinted.green() * blend),
|
||||
qRound(yellow.blue() * (1.0 - blend) + tinted.blue() * blend));
|
||||
}
|
||||
|
||||
// Backgrounds
|
||||
const QColor bg_win = hsl(winL, satWindow);
|
||||
const QColor bg_base = hsl(baseL, satBase);
|
||||
const QColor bg_alt = hsl(altL, satAlt);
|
||||
const QColor bg_btn = hsl(btnL, satButton);
|
||||
|
||||
set3(CR::Window, bg_win, bg_win, bg_win);
|
||||
set3(CR::Base, bg_base, bg_win, bg_base);
|
||||
set3(CR::AlternateBase, bg_alt, bg_alt, bg_alt);
|
||||
set3(CR::Button, bg_btn, bg_win, bg_btn);
|
||||
set3(CR::ToolTipBase, bg_tip, bg_tip, bg_tip);
|
||||
|
||||
// Foreground text
|
||||
const QColor winText = dark ? Qt::white : Qt::black;
|
||||
const QColor disText = dark ? QColor(157, 157, 157) : QColor(120, 120, 120);
|
||||
const QColor disBtnText = dark ? QColor(120, 120, 120) : QColor(150, 150, 150);
|
||||
|
||||
set3(CR::WindowText, winText, disText, winText);
|
||||
set3(CR::Text, winText, disText, winText);
|
||||
set3(CR::ButtonText, textOn(bg_btn), disBtnText, textOn(bg_btn));
|
||||
setAll(CR::ToolTipText, textOn(bg_tip));
|
||||
|
||||
const QColor phAlpha = dark ? QColor(255, 255, 255, 110) : QColor(0, 0, 0, 110);
|
||||
const QColor phDis = dark ? QColor(255, 255, 255, 70) : QColor(0, 0, 0, 70);
|
||||
set3(CR::PlaceholderText, phAlpha, phDis, phAlpha);
|
||||
|
||||
// Highlight / selection
|
||||
const QColor inactiveHl = hsl(winL + (dark ? 14 : -10), satWindow);
|
||||
cfg.colors[CG::Active][CR::Highlight] = hl;
|
||||
cfg.colors[CG::Disabled][CR::Highlight] = inactiveHl;
|
||||
cfg.colors[CG::Inactive][CR::Highlight] = inactiveHl;
|
||||
cfg.colors[CG::Active][CR::HighlightedText] = hlText;
|
||||
cfg.colors[CG::Disabled][CR::HighlightedText] = disText;
|
||||
cfg.colors[CG::Inactive][CR::HighlightedText] = dark ? Qt::white : Qt::black;
|
||||
|
||||
// BrightText
|
||||
QColor bright;
|
||||
if (achromatic) {
|
||||
bright = dark ? Qt::white : Qt::black;
|
||||
} else if (dark) {
|
||||
bright = QColor::fromHsl(h, qMin(255, satHighlight + 25), qMin(235, hl.lightness() + 50));
|
||||
} else {
|
||||
bright = Qt::white;
|
||||
}
|
||||
setAll(CR::BrightText, bright);
|
||||
|
||||
// Links
|
||||
QColor link, linkV;
|
||||
if (achromatic) {
|
||||
link = dark ? QColor(100, 200, 255) : QColor(0, 0, 210);
|
||||
linkV = dark ? QColor(200, 100, 255) : QColor(128, 0, 180);
|
||||
} else if (dark) {
|
||||
link = QColor::fromHsl(h, qMin(255, satHighlight), qMin(230, hl.lightness() + 75));
|
||||
linkV = QColor::fromHsl((h + 30) % 360, qMin(255, satHighlight), qMin(215, hl.lightness() + 55));
|
||||
} else {
|
||||
link = QColor::fromHsl(h, qMin(255, satHighlight), qMax(40, hl.lightness() - 75));
|
||||
linkV = QColor::fromHsl((h + 30) % 360, qMin(255, satHighlight), qMax(30, hl.lightness() - 95));
|
||||
}
|
||||
set3(CR::Link, link, dark ? QColor(48, 140, 198) : QColor(0, 0, 255), link);
|
||||
set3(CR::LinkVisited, linkV, dark ? QColor(180, 80, 255) : QColor(255, 0, 255), linkV);
|
||||
|
||||
// 3D / frame shading
|
||||
if (dark) {
|
||||
setAll(CR::Light, hsl(115, qMin(255, satShadeHi)));
|
||||
setAll(CR::Midlight, hsl(82, qMin(255, satShadeHi)));
|
||||
setAll(CR::Mid, hsl(37, satShadeLo));
|
||||
setAll(CR::Dark, hsl(22, satShadeLo));
|
||||
setAll(CR::Shadow, Qt::black);
|
||||
} else {
|
||||
setAll(CR::Light, Qt::white);
|
||||
setAll(CR::Midlight, hsl(226, qMin(255, satShadeHi)));
|
||||
setAll(CR::Mid, hsl(158, satShadeLo));
|
||||
setAll(CR::Dark, hsl(148, satShadeLo));
|
||||
// Shadow stays neutral — tinting it makes the UI look bruised
|
||||
cfg.colors[CG::Active][CR::Shadow] = QColor(105, 105, 105);
|
||||
cfg.colors[CG::Disabled][CR::Shadow] = Qt::black;
|
||||
cfg.colors[CG::Inactive][CR::Shadow] = QColor(105, 105, 105);
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
} // namespace PaletteGenerator
|
||||
Loading…
Add table
Add a link
Reference in a new issue