mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-11 20:43:25 -08:00
[Icons] Paint neutral glyphs with icon ink instead of body-text ink
Neutral icons resolved `currentColor` straight to `QPalette::WindowText`. A glyph is not a word: it is a 16-24px mark drawn with a 2-unit stroke, so antialiasing thins it until it reads lighter than the text beside it, in the one color the palette reserves for reading. The result is that icons recede into the chrome exactly where they should anchor it. Add `theme_icon_ink`, one rule with one job: mix a share of the theme accent into the neutral text color, then hold the result to a contrast floor. The accent share gives the glyph chroma the text does not have, so it reads as an icon rather than as leftover text, and it is derived from `QPalette::Highlight` rather than a new token. The floor keeps a theme with a weak text color from producing an illegible glyph. Only the neutral label roles (`WindowText`, `Text`) are inked. Semantic tints are hues the call site chose on purpose, disabled uses the disabled group, and the selected-row swap to `HighlightedText` is unchanged. The WCAG contrast math moves from `theme_icon_badge` to the new unit, so badge ink decisions and the floor share one implementation. Tests: the ink rule against every shipped palette, the weak-text floor, and the palette-following render tests now ask the rule instead of restating its output. A preview sheet sweeps the accent share at 0-35% over window and button surfaces for both schemes.
This commit is contained in:
parent
4a0c956062
commit
86c8790b48
10 changed files with 467 additions and 36 deletions
98
cockatrice/src/interface/theme_icon_ink.h
Normal file
98
cockatrice/src/interface/theme_icon_ink.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* @file theme_icon_ink.h
|
||||
* @ingroup Interface
|
||||
*/
|
||||
|
||||
#ifndef THEME_ICON_INK_H
|
||||
#define THEME_ICON_INK_H
|
||||
|
||||
#include <QColor>
|
||||
|
||||
/**
|
||||
* @brief Turns a text color into the ink a glyph is painted with.
|
||||
*
|
||||
* A glyph is not a word. Body text is read in sentences, at a size where a 2px
|
||||
* stroke would be unusually heavy; a glyph is a 16px mark of the same stroke,
|
||||
* and antialiasing eats its edges, so the same color that reads as solid type
|
||||
* reads as a thin gray line beside it. Painting icons with `WindowText` verbatim
|
||||
* is what makes them recede.
|
||||
*
|
||||
* The rule here is deliberately small: give the glyph a little of the theme's
|
||||
* accent chroma, then hold it to a contrast floor against the surface it sits
|
||||
* on. The chroma is what makes it read as an icon rather than as leftover text;
|
||||
* the floor keeps a theme with a weak text color from producing a glyph that is
|
||||
* not legible. Neither step invents a color: both are derived from the palette
|
||||
* the theme already ships.
|
||||
*
|
||||
* The rule only applies to the neutral label roles. A semantic tint
|
||||
* (`AppColor::Positive`, `QPalette::Highlight`, `QPalette::Link`) is a hue the
|
||||
* call site chose on purpose, so it is left alone.
|
||||
*/
|
||||
namespace ThemeIconInk
|
||||
{
|
||||
/**
|
||||
* @brief WCAG relative luminance of an opaque color.
|
||||
*/
|
||||
double relativeLuminance(const QColor &color);
|
||||
|
||||
/**
|
||||
* @brief WCAG contrast ratio between two opaque colors, from 1.0 to 21.0.
|
||||
*/
|
||||
double contrastRatio(const QColor &first, const QColor &second);
|
||||
|
||||
/**
|
||||
* @brief Mixes two colors, @p amount = 0 returning @p color and 1 returning @p target.
|
||||
*
|
||||
* RGB interpolation, with no gamma or hue-space correction: the inputs are
|
||||
* palette colors that already sit in sRGB, and the result has to be predictable
|
||||
* enough for a theme author to reason about it.
|
||||
*/
|
||||
QColor blend(const QColor &color, const QColor &target, double amount);
|
||||
|
||||
/**
|
||||
* @brief Pushes @p color away from @p background until it reaches @p minRatio.
|
||||
*
|
||||
* Moves @p color toward black on a light background and toward white on a dark
|
||||
* one, so the hue survives the adjustment. Returns @p color unchanged when it
|
||||
* already meets the floor, which is the common case for a shipped palette.
|
||||
*
|
||||
* @param color Color to adjust.
|
||||
* @param background Surface the color is drawn on.
|
||||
* @param minRatio Contrast floor, as a WCAG ratio.
|
||||
* @return The adjusted color; the nearer pole when even that cannot reach it.
|
||||
*/
|
||||
QColor reachContrast(const QColor &color, const QColor &background, double minRatio);
|
||||
|
||||
/**
|
||||
* @brief Accent share used by production callers of glyphInk().
|
||||
*
|
||||
* Exposed so the preview sheet and the tests name the value they pin instead of
|
||||
* repeating the literal, and so the default argument below has exactly one
|
||||
* source: the constant in theme_icon_ink.cpp.
|
||||
*/
|
||||
double defaultAccentMix();
|
||||
|
||||
/**
|
||||
* @brief Icon ink for a neutral glyph, given the palette it is drawn from.
|
||||
*
|
||||
* @param text Neutral text color, normally `QPalette::WindowText`.
|
||||
* @param surface Surface the glyph is drawn on, normally `QPalette::Window`.
|
||||
* @param accent The theme accent, normally `QPalette::Highlight`.
|
||||
* @param accentMix How much of the accent color to borrow. Defaults to
|
||||
* defaultAccentMix(); previews and tests sweep it to compare
|
||||
* strengths, production callers do not pass it.
|
||||
* @return The ink to resolve `currentColor` to, as an RGB color.
|
||||
*/
|
||||
QColor glyphInk(const QColor &text, const QColor &surface, const QColor &accent, double accentMix = defaultAccentMix());
|
||||
|
||||
/**
|
||||
* @brief Contrast floor a glyph that labels a control has to clear.
|
||||
*
|
||||
* 4.5:1, the Text-level WCAG ratio: an icon that is the only label for a button
|
||||
* is carrying text, and the icon guidelines in `.opencode/docs/ui-assets.md`
|
||||
* already ask for that when there is no visible label.
|
||||
*/
|
||||
double labelContrastFloor();
|
||||
} // namespace ThemeIconInk
|
||||
|
||||
#endif // THEME_ICON_INK_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue