1use gpui::{Hsla, WindowAppearance};
6
7#[derive(Clone, Debug)]
8pub struct GridTheme {
9 pub bg: Hsla,
10 pub header_bg: Hsla,
11 pub filter_bg: Hsla,
12 pub filter_active_bg: Hsla,
13 pub row_header_bg: Hsla,
14 pub selection_bg: Hsla,
15 pub alt_row_bg: Hsla,
16 pub grid_line: Hsla,
17 pub header_fg: Hsla,
18 pub text_fg: Hsla,
19 pub negative_fg: Hsla,
20 pub sort_indicator: Hsla,
21 pub filter_cursor: Hsla,
22 pub menu_bg: Hsla,
24 pub menu_hover_bg: Hsla,
26 pub menu_fg: Hsla,
28}
29
30impl Default for GridTheme {
31 fn default() -> Self {
32 Self {
33 bg: hsla(0.0, 0.0, 1.0, 1.0),
34 header_bg: hsla(0.0, 0.0, 0.93, 1.0),
35 filter_bg: hsla(0.0, 0.0, 0.96, 1.0),
36 filter_active_bg: hsla(0.58, 0.30, 0.85, 1.0),
37 row_header_bg: hsla(0.0, 0.0, 0.90, 1.0),
38 selection_bg: hsla(0.58, 0.50, 0.80, 0.50),
39 alt_row_bg: hsla(0.0, 0.0, 0.95, 1.0),
40 grid_line: hsla(0.0, 0.0, 0.85, 1.0),
41 header_fg: hsla(0.0, 0.0, 0.15, 1.0),
42 text_fg: hsla(0.0, 0.0, 0.1, 1.0),
43 negative_fg: hsla(0.0, 0.75, 0.45, 1.0),
44 sort_indicator: hsla(0.58, 0.50, 0.40, 1.0),
45 filter_cursor: hsla(0.0, 0.0, 0.1, 1.0),
46 menu_bg: hsla(0.0, 0.0, 1.0, 1.0),
47 menu_hover_bg: hsla(0.58, 0.45, 0.85, 1.0),
48 menu_fg: hsla(0.0, 0.0, 0.1, 1.0),
49 }
50 }
51}
52
53fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
54 Hsla { h, s, l, a }
55}
56
57impl GridTheme {
58 #[must_use]
61 pub fn light() -> Self {
62 Self::default()
63 }
64
65 #[must_use]
68 pub fn dark() -> Self {
69 Self {
70 bg: hsla(0.0, 0.0, 0.12, 1.0),
71 header_bg: hsla(0.0, 0.0, 0.18, 1.0),
72 filter_bg: hsla(0.0, 0.0, 0.15, 1.0),
73 filter_active_bg: hsla(0.58, 0.40, 0.30, 1.0),
74 row_header_bg: hsla(0.0, 0.0, 0.16, 1.0),
75 selection_bg: hsla(0.58, 0.50, 0.45, 0.50),
76 alt_row_bg: hsla(0.0, 0.0, 0.15, 1.0),
77 grid_line: hsla(0.0, 0.0, 0.28, 1.0),
78 header_fg: hsla(0.0, 0.0, 0.80, 1.0),
79 text_fg: hsla(0.0, 0.0, 0.90, 1.0),
80 negative_fg: hsla(0.0, 0.70, 0.62, 1.0),
81 sort_indicator: hsla(0.58, 0.60, 0.68, 1.0),
82 filter_cursor: hsla(0.0, 0.0, 0.90, 1.0),
83 menu_bg: hsla(0.0, 0.0, 0.16, 1.0),
84 menu_hover_bg: hsla(0.58, 0.45, 0.38, 1.0),
85 menu_fg: hsla(0.0, 0.0, 0.90, 1.0),
86 }
87 }
88
89 #[must_use]
93 pub fn for_appearance(appearance: WindowAppearance) -> Self {
94 match appearance {
95 WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::dark(),
96 WindowAppearance::Light | WindowAppearance::VibrantLight => Self::light(),
97 }
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
111 fn default_theme_exposes_distinct_menu_colors() {
112 let t = GridTheme::default();
113 assert_eq!(t.menu_bg.a, 1.0, "menu background must be opaque");
115 assert_ne!(
117 t.menu_hover_bg, t.menu_bg,
118 "menu hover fill must differ from the menu background"
119 );
120 assert_ne!(
122 t.menu_fg, t.menu_bg,
123 "menu label color must contrast with the menu background"
124 );
125 }
126
127 #[test]
131 fn light_matches_default_and_dark_differs() {
132 assert_eq!(
133 GridTheme::light().bg,
134 GridTheme::default().bg,
135 "light() must alias the default palette"
136 );
137 let dark = GridTheme::dark();
138 assert_ne!(dark.bg, GridTheme::light().bg, "dark bg must differ");
139 assert!(
141 dark.bg.l < dark.text_fg.l,
142 "dark theme must be light text on a dark surface"
143 );
144 assert_eq!(dark.menu_bg.a, 1.0, "dark menu background must be opaque");
145 assert_ne!(
146 dark.menu_hover_bg, dark.menu_bg,
147 "dark menu hover fill must differ from the menu background"
148 );
149 }
150
151 #[test]
154 fn for_appearance_maps_dark_and_light_variants() {
155 assert_eq!(
156 GridTheme::for_appearance(WindowAppearance::Dark).bg,
157 GridTheme::dark().bg
158 );
159 assert_eq!(
160 GridTheme::for_appearance(WindowAppearance::VibrantDark).bg,
161 GridTheme::dark().bg
162 );
163 assert_eq!(
164 GridTheme::for_appearance(WindowAppearance::Light).bg,
165 GridTheme::light().bg
166 );
167 assert_eq!(
168 GridTheme::for_appearance(WindowAppearance::VibrantLight).bg,
169 GridTheme::light().bg
170 );
171 }
172}