use gpui::{Hsla, WindowAppearance};
#[derive(Clone, Debug)]
pub struct GridTheme {
pub bg: Hsla,
pub header_bg: Hsla,
pub filter_bg: Hsla,
pub filter_active_bg: Hsla,
pub row_header_bg: Hsla,
pub selection_bg: Hsla,
pub alt_row_bg: Hsla,
pub grid_line: Hsla,
pub header_fg: Hsla,
pub text_fg: Hsla,
pub negative_fg: Hsla,
pub sort_indicator: Hsla,
pub filter_cursor: Hsla,
pub menu_bg: Hsla,
pub menu_hover_bg: Hsla,
pub menu_fg: Hsla,
}
impl Default for GridTheme {
fn default() -> Self {
Self {
bg: hsla(0.0, 0.0, 1.0, 1.0),
header_bg: hsla(0.0, 0.0, 0.93, 1.0),
filter_bg: hsla(0.0, 0.0, 0.96, 1.0),
filter_active_bg: hsla(0.58, 0.30, 0.85, 1.0),
row_header_bg: hsla(0.0, 0.0, 0.90, 1.0),
selection_bg: hsla(0.58, 0.50, 0.80, 0.50),
alt_row_bg: hsla(0.0, 0.0, 0.95, 1.0),
grid_line: hsla(0.0, 0.0, 0.85, 1.0),
header_fg: hsla(0.0, 0.0, 0.15, 1.0),
text_fg: hsla(0.0, 0.0, 0.1, 1.0),
negative_fg: hsla(0.0, 0.75, 0.45, 1.0),
sort_indicator: hsla(0.58, 0.50, 0.40, 1.0),
filter_cursor: hsla(0.0, 0.0, 0.1, 1.0),
menu_bg: hsla(0.0, 0.0, 1.0, 1.0),
menu_hover_bg: hsla(0.58, 0.45, 0.85, 1.0),
menu_fg: hsla(0.0, 0.0, 0.1, 1.0),
}
}
}
fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
Hsla { h, s, l, a }
}
impl GridTheme {
#[must_use]
pub fn light() -> Self {
Self::default()
}
#[must_use]
pub fn dark() -> Self {
Self {
bg: hsla(0.0, 0.0, 0.12, 1.0),
header_bg: hsla(0.0, 0.0, 0.18, 1.0),
filter_bg: hsla(0.0, 0.0, 0.15, 1.0),
filter_active_bg: hsla(0.58, 0.40, 0.30, 1.0),
row_header_bg: hsla(0.0, 0.0, 0.16, 1.0),
selection_bg: hsla(0.58, 0.50, 0.45, 0.50),
alt_row_bg: hsla(0.0, 0.0, 0.15, 1.0),
grid_line: hsla(0.0, 0.0, 0.28, 1.0),
header_fg: hsla(0.0, 0.0, 0.80, 1.0),
text_fg: hsla(0.0, 0.0, 0.90, 1.0),
negative_fg: hsla(0.0, 0.70, 0.62, 1.0),
sort_indicator: hsla(0.58, 0.60, 0.68, 1.0),
filter_cursor: hsla(0.0, 0.0, 0.90, 1.0),
menu_bg: hsla(0.0, 0.0, 0.16, 1.0),
menu_hover_bg: hsla(0.58, 0.45, 0.38, 1.0),
menu_fg: hsla(0.0, 0.0, 0.90, 1.0),
}
}
#[must_use]
pub fn for_appearance(appearance: WindowAppearance) -> Self {
match appearance {
WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::dark(),
WindowAppearance::Light | WindowAppearance::VibrantLight => Self::light(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_theme_exposes_distinct_menu_colors() {
let t = GridTheme::default();
assert_eq!(t.menu_bg.a, 1.0, "menu background must be opaque");
assert_ne!(
t.menu_hover_bg, t.menu_bg,
"menu hover fill must differ from the menu background"
);
assert_ne!(
t.menu_fg, t.menu_bg,
"menu label color must contrast with the menu background"
);
}
#[test]
fn light_matches_default_and_dark_differs() {
assert_eq!(
GridTheme::light().bg,
GridTheme::default().bg,
"light() must alias the default palette"
);
let dark = GridTheme::dark();
assert_ne!(dark.bg, GridTheme::light().bg, "dark bg must differ");
assert!(
dark.bg.l < dark.text_fg.l,
"dark theme must be light text on a dark surface"
);
assert_eq!(dark.menu_bg.a, 1.0, "dark menu background must be opaque");
assert_ne!(
dark.menu_hover_bg, dark.menu_bg,
"dark menu hover fill must differ from the menu background"
);
}
#[test]
fn for_appearance_maps_dark_and_light_variants() {
assert_eq!(
GridTheme::for_appearance(WindowAppearance::Dark).bg,
GridTheme::dark().bg
);
assert_eq!(
GridTheme::for_appearance(WindowAppearance::VibrantDark).bg,
GridTheme::dark().bg
);
assert_eq!(
GridTheme::for_appearance(WindowAppearance::Light).bg,
GridTheme::light().bg
);
assert_eq!(
GridTheme::for_appearance(WindowAppearance::VibrantLight).bg,
GridTheme::light().bg
);
}
}