use gpui::Hsla; use crate::model::{AccentRoles, Color, ThemeColors, ThemeVariant}; fn hsla(color: Color) -> Hsla { let packed = ((color.r as u32) << 24) | ((color.g as u32) << 16) | ((color.b as u32) << 8) | color.a as u32; Hsla::from(gpui::rgba(packed)) } /// Accent roles resolved to [`Hsla`], with derived interaction states precomputed. #[derive(Debug, Clone, Copy)] pub struct ResolvedAccentRoles { pub primary: Hsla, /// Accent for strokes, icons, and text on the background; meets 3:1 contrast. pub strong: Hsla, pub wash: Hsla, pub on: Hsla, pub selection: Hsla, pub hover: Hsla, pub active: Hsla, pub selected: Hsla, /// Text guaranteed 4.5:1 readable on `wash`. pub text_on_wash: Hsla, /// Text guaranteed readable on the flat `danger` color. pub on_danger: Hsla, } impl ResolvedAccentRoles { pub fn resolve(accent: &AccentRoles, colors: &ThemeColors) -> Self { let background = colors.background; let wash_background = accent.wash.blend_over(background); Self { primary: hsla(accent.primary), strong: hsla(accent.strong), wash: hsla(accent.wash), on: hsla(accent.on), selection: hsla(accent.selection), hover: hsla(accent.primary.mix(background, 0.12)), active: hsla(accent.primary.mix(background, 0.2)), selected: hsla(accent.primary.mix(background, 0.28)), text_on_wash: hsla(accent.primary.ensure_contrast(wash_background, 4.5)), on_danger: hsla(colors.danger.best_on_color()), } } } /// The `Hsla` snapshot components render from, rebuilt on every theme change. #[derive(Debug, Clone, Copy)] pub struct ResolvedColors { pub background: Hsla, pub shell: Hsla, pub raised: Hsla, pub card: Hsla, pub dialog: Hsla, pub overlay: Hsla, pub hover: Hsla, pub active: Hsla, pub border: Hsla, pub border_strong: Hsla, pub text: Hsla, pub text_muted: Hsla, pub text_faint: Hsla, pub solid: Hsla, pub on_solid: Hsla, pub danger: Hsla, pub danger_muted: Hsla, pub warning: Hsla, pub warning_muted: Hsla, pub success: Hsla, pub success_muted: Hsla, pub input: Hsla, pub cursor: Hsla, pub diff_add: Hsla, pub diff_delete: Hsla, pub diff_hunk: Hsla, } impl ResolvedColors { pub fn resolve(variant: &ThemeVariant) -> Self { let colors = &variant.colors; Self { background: hsla(colors.background), shell: hsla(colors.shell), raised: hsla(colors.raised), card: hsla(colors.card), dialog: hsla(colors.dialog), overlay: hsla(colors.overlay), hover: hsla(colors.hover), active: hsla(colors.active), border: hsla(colors.border), border_strong: hsla(colors.border_strong), text: hsla(colors.text), text_muted: hsla(colors.text_muted), text_faint: hsla(colors.text_faint), solid: hsla(colors.solid), on_solid: hsla(colors.on_solid), danger: hsla(colors.danger), danger_muted: hsla(colors.danger_muted), warning: hsla(colors.warning), warning_muted: hsla(colors.warning_muted), success: hsla(colors.success), success_muted: hsla(colors.success_muted), input: hsla(colors.input), cursor: hsla(colors.cursor), diff_add: hsla(colors.diff_add), diff_delete: hsla(colors.diff_delete), diff_hunk: hsla(colors.diff_hunk), } } }