use cleansys_core::AppTheme;
use iced::{Color, Theme};
#[derive(Debug, Clone, Copy)]
pub struct ThemeColors {
pub accent: Color,
pub text_primary: Color,
pub text_secondary: Color,
pub muted: Color,
pub bg: Color,
pub surface: Color,
pub surface_highlight: Color,
pub header_bg: Color,
pub border: Color,
pub selection: Color,
pub green: Color,
pub red: Color,
pub yellow: Color,
}
fn clamp(v: f32) -> f32 {
v.clamp(0.0, 1.0)
}
fn shift(base: Color, delta: f32) -> Color {
Color {
r: clamp(base.r + delta),
g: clamp(base.g + delta),
b: clamp(base.b + delta),
a: base.a,
}
}
fn rgb_to_iced(rgb: cleansys_core::Rgb) -> Color {
Color::from_rgb8(rgb.r, rgb.g, rgb.b)
}
impl ThemeColors {
pub fn from_core(t: &AppTheme) -> Self {
let bg = rgb_to_iced(t.background);
let surface = rgb_to_iced(t.surface);
let sign: f32 = if t.is_dark { 1.0 } else { -1.0 };
let surface_highlight = shift(surface, sign * 0.04);
let header_bg = shift(bg, sign * 0.02);
Self {
accent: rgb_to_iced(t.accent),
text_primary: rgb_to_iced(t.text_primary),
text_secondary: rgb_to_iced(t.text_secondary),
muted: rgb_to_iced(t.text_muted),
bg,
surface,
surface_highlight,
header_bg,
border: rgb_to_iced(t.border),
selection: rgb_to_iced(t.selection),
green: rgb_to_iced(t.success),
red: rgb_to_iced(t.error),
yellow: rgb_to_iced(t.warning),
}
}
}
pub fn iced_theme_for(index: usize) -> Theme {
let core = cleansys_core::theme_by_index(index);
let name = cleansys_core::THEME_NAMES
.get(index)
.copied()
.unwrap_or("Default")
.to_string();
let palette = iced::theme::Palette {
background: rgb_to_iced(core.background),
text: rgb_to_iced(core.text_primary),
primary: rgb_to_iced(core.accent),
success: rgb_to_iced(core.success),
warning: rgb_to_iced(core.warning),
danger: rgb_to_iced(core.error),
};
Theme::custom(name, palette)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_core_produces_valid_colors() {
for i in 0..cleansys_core::THEME_COUNT {
let core = cleansys_core::theme_by_index(i);
let colors = ThemeColors::from_core(&core);
assert!(colors.bg.a > 0.0);
assert!(colors.text_primary.a > 0.0);
}
}
#[test]
fn iced_theme_for_all_indices_does_not_panic() {
for i in 0..cleansys_core::THEME_COUNT {
let _ = iced_theme_for(i);
}
}
#[test]
fn iced_theme_for_out_of_range_falls_back_to_default_name() {
let theme = iced_theme_for(9999);
assert_eq!(format!("{theme}"), "Default");
}
}