use super::{
color::{Color, dim_color, lighten, parse_color},
theme_set::ThemeColors,
};
const ACCENT_DIM_FACTOR: f32 = 0.75;
const ACCENT_DARK_FACTOR: f32 = 0.25;
const INPUT_BG_FACTOR: f32 = 0.22;
const INPUT_BG_ACTIVE_FACTOR: f32 = 0.30;
#[derive(Debug, Default, Clone)]
pub struct ColorOverrides<'a> {
pub accent: &'a str,
pub selection_bg: &'a str,
pub cursor: &'a str,
pub background: &'a str,
pub surface: &'a str,
pub surface_alt: &'a str,
pub surface_bar: &'a str,
pub input_bg: &'a str,
pub input_bg_active: &'a str,
}
#[derive(Debug, Clone, Copy)]
pub struct Palette {
pub accent: Color,
pub accent_dim: Color,
pub accent_dark: Color,
pub selection_bg: Color,
pub cursor: Color,
pub background: Color,
pub surface: Color,
pub surface_alt: Color,
pub surface_bar: Color,
pub input_bg: Color,
pub input_bg_active: Color,
pub error: Color,
pub warning: Color,
pub success: Color,
pub info: Color,
}
impl Palette {
pub fn resolve(base: ThemeColors, overrides: &ColorOverrides<'_>) -> Self {
let accent = override_or(overrides.accent, base.accent);
let background = override_or(overrides.background, base.background);
Self {
accent,
accent_dim: dim_color(accent, ACCENT_DIM_FACTOR),
accent_dark: dim_color(accent, ACCENT_DARK_FACTOR),
selection_bg: override_or(
overrides.selection_bg,
base.selection_bg,
),
cursor: override_or(overrides.cursor, base.cursor),
background,
surface: override_or(overrides.surface, base.surface),
surface_alt: override_or(overrides.surface_alt, base.surface_alt),
surface_bar: override_or(overrides.surface_bar, base.surface_bar),
input_bg: override_or(
overrides.input_bg,
lighten(background, INPUT_BG_FACTOR),
),
input_bg_active: override_or(
overrides.input_bg_active,
lighten(background, INPUT_BG_ACTIVE_FACTOR),
),
error: base.error,
warning: base.warning,
success: base.success,
info: base.info,
}
}
}
fn override_or(value: &str, fallback: Color) -> Color {
parse_color(value).unwrap_or(fallback)
}
#[cfg(test)]
mod tests {
use super::{super::theme_set::ThemeRegistry, *};
fn nord() -> ThemeColors {
ThemeRegistry::builtin().resolve("nord")
}
#[test]
fn resolve_without_overrides_uses_theme_colors() {
let palette = Palette::resolve(nord(), &ColorOverrides::default());
assert_eq!(palette.accent, nord().accent);
assert_eq!(palette.background, nord().background);
}
#[test]
fn non_empty_override_wins_over_theme() {
let overrides = ColorOverrides {
accent: "#010203",
..ColorOverrides::default()
};
let palette = Palette::resolve(nord(), &overrides);
assert_eq!(palette.accent, Color::Rgb(1, 2, 3));
assert_eq!(palette.cursor, nord().cursor);
}
#[test]
fn empty_or_invalid_override_keeps_theme_color() {
let overrides = ColorOverrides {
accent: "",
cursor: "nope",
..ColorOverrides::default()
};
let palette = Palette::resolve(nord(), &overrides);
assert_eq!(palette.accent, nord().accent);
assert_eq!(palette.cursor, nord().cursor);
}
#[test]
fn semantic_colors_carry_through_from_the_theme() {
let palette = Palette::resolve(nord(), &ColorOverrides::default());
assert_eq!(palette.error, nord().error);
assert_eq!(palette.success, nord().success);
}
#[test]
fn surfaces_carry_through_and_can_be_overridden() {
let palette = Palette::resolve(nord(), &ColorOverrides::default());
assert_eq!(palette.surface, nord().surface);
assert_eq!(palette.surface_bar, nord().surface_bar);
let overrides = ColorOverrides {
surface_alt: "#010203",
..ColorOverrides::default()
};
let palette = Palette::resolve(nord(), &overrides);
assert_eq!(palette.surface_alt, Color::Rgb(1, 2, 3));
assert_eq!(palette.surface, nord().surface);
}
#[test]
fn input_backgrounds_are_derived_from_the_background() {
let palette = Palette::resolve(nord(), &ColorOverrides::default());
assert_eq!(
palette.input_bg,
lighten(nord().background, INPUT_BG_FACTOR),
);
assert_eq!(
palette.input_bg_active,
lighten(nord().background, INPUT_BG_ACTIVE_FACTOR),
);
assert_ne!(palette.input_bg, palette.input_bg_active);
}
#[test]
fn input_backgrounds_honor_overrides() {
let overrides = ColorOverrides {
input_bg: "#010203",
input_bg_active: "#040506",
..ColorOverrides::default()
};
let palette = Palette::resolve(nord(), &overrides);
assert_eq!(palette.input_bg, Color::Rgb(1, 2, 3));
assert_eq!(palette.input_bg_active, Color::Rgb(4, 5, 6));
}
#[test]
fn accent_dim_and_dark_are_derived_from_accent() {
let overrides = ColorOverrides {
accent: "#646464",
..ColorOverrides::default()
};
let palette = Palette::resolve(nord(), &overrides);
assert_eq!(
palette.accent_dim,
dim_color(palette.accent, ACCENT_DIM_FACTOR),
);
assert_eq!(
palette.accent_dark,
dim_color(palette.accent, ACCENT_DARK_FACTOR),
);
}
}