1use gpui::{AlignContent, AlignItems, Div, Hsla, Styled};
6
7use crate::theme::{ColorName, Size, Theme};
8
9pub trait StyleExt: Sized {
12 fn apply(self, f: impl FnOnce(Self) -> Self) -> Self {
15 f(self)
16 }
17}
18
19impl<T: Styled> StyleExt for T {}
20
21pub(crate) trait FlexExt: Sized {
26 fn grow(self, factor: f32) -> Self;
28 fn shrink(self, factor: f32) -> Self;
30 fn items_stretch(self) -> Self;
32 fn justify_evenly(self) -> Self;
34}
35
36impl FlexExt for Div {
37 fn grow(mut self, factor: f32) -> Self {
38 self.style().flex_grow = Some(factor);
39 self
40 }
41
42 fn shrink(mut self, factor: f32) -> Self {
43 self.style().flex_shrink = Some(factor);
44 self
45 }
46
47 fn items_stretch(mut self) -> Self {
48 self.style().align_items = Some(AlignItems::Stretch);
49 self
50 }
51
52 fn justify_evenly(mut self) -> Self {
53 self.style().justify_content = Some(AlignContent::SpaceEvenly);
54 self
55 }
56}
57
58pub(crate) fn icon_size(size: Size) -> f32 {
61 match size {
62 Size::Xs => 18.0,
63 Size::Sm => 22.0,
64 Size::Md => 28.0,
65 Size::Lg => 34.0,
66 Size::Xl => 44.0,
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
72pub enum Variant {
73 #[default]
75 Filled,
76 Light,
78 Outline,
80 Subtle,
82 Default,
84 Transparent,
86 White,
88}
89
90
91#[derive(Debug, Clone, Copy)]
93pub struct Surface {
94 pub bg: Hsla,
95 pub bg_hover: Hsla,
96 pub fg: Hsla,
97 pub border: Option<Hsla>,
98}
99
100#[derive(Debug, Clone, Copy, PartialEq)]
107pub enum ColorValue {
108 Named(ColorName),
110 Custom(Hsla),
112}
113
114impl Default for ColorValue {
115 fn default() -> Self {
116 ColorValue::Named(ColorName::Blue)
117 }
118}
119
120impl From<ColorName> for ColorValue {
121 fn from(name: ColorName) -> Self {
122 ColorValue::Named(name)
123 }
124}
125
126impl From<Hsla> for ColorValue {
127 fn from(color: Hsla) -> Self {
128 ColorValue::Custom(color)
129 }
130}
131
132impl ColorValue {
133 pub fn accent(self, theme: &Theme) -> Hsla {
135 match self {
136 ColorValue::Named(name) => theme.color(name, theme.primary_shade()).hsla(),
137 ColorValue::Custom(c) => c,
138 }
139 }
140
141 pub fn soft(self, theme: &Theme) -> Hsla {
143 let dark = theme.scheme.is_dark();
144 match self {
145 ColorValue::Named(name) if dark => theme.color(name, 5).alpha(0.20),
146 ColorValue::Named(name) => theme.color(name, 0).hsla(),
147 ColorValue::Custom(c) => with_alpha(c, if dark { 0.22 } else { 0.12 }),
148 }
149 }
150}
151
152fn shift_l(c: Hsla, delta: f32) -> Hsla {
153 Hsla {
154 l: (c.l + delta).clamp(0.0, 1.0),
155 ..c
156 }
157}
158
159fn with_alpha(c: Hsla, a: f32) -> Hsla {
160 Hsla { a, ..c }
161}
162
163fn readable_on(c: Hsla) -> Hsla {
165 let v = if c.l > 0.6 { 0.0 } else { 1.0 };
166 Hsla {
167 h: 0.0,
168 s: 0.0,
169 l: v,
170 a: 1.0,
171 }
172}
173
174pub fn surface(theme: &Theme, color: impl Into<ColorValue>, variant: Variant) -> Surface {
177 match color.into() {
178 ColorValue::Named(name) => surface_named(theme, name, variant),
179 ColorValue::Custom(c) => surface_custom(theme, c, variant),
180 }
181}
182
183fn surface_custom(theme: &Theme, c: Hsla, variant: Variant) -> Surface {
186 let dark = theme.scheme.is_dark();
187 let transparent = gpui::transparent_black();
188 let hover_fill = if dark { shift_l(c, 0.06) } else { shift_l(c, -0.06) };
189 match variant {
190 Variant::Filled => Surface {
191 bg: c,
192 bg_hover: hover_fill,
193 fg: readable_on(c),
194 border: None,
195 },
196 Variant::Light => Surface {
197 bg: with_alpha(c, if dark { 0.20 } else { 0.12 }),
198 bg_hover: with_alpha(c, if dark { 0.28 } else { 0.20 }),
199 fg: c,
200 border: None,
201 },
202 Variant::Outline => Surface {
203 bg: transparent,
204 bg_hover: with_alpha(c, 0.08),
205 fg: c,
206 border: Some(c),
207 },
208 Variant::Subtle => Surface {
209 bg: transparent,
210 bg_hover: with_alpha(c, if dark { 0.15 } else { 0.08 }),
211 fg: c,
212 border: None,
213 },
214 Variant::Default => Surface {
215 bg: theme.surface().hsla(),
216 bg_hover: theme.surface_hover().hsla(),
217 fg: theme.text().hsla(),
218 border: Some(theme.border().hsla()),
219 },
220 Variant::Transparent => Surface {
221 bg: transparent,
222 bg_hover: transparent,
223 fg: c,
224 border: None,
225 },
226 Variant::White => Surface {
227 bg: theme.white.hsla(),
228 bg_hover: theme.color(ColorName::Gray, 0).hsla(),
229 fg: c,
230 border: None,
231 },
232 }
233}
234
235fn surface_named(theme: &Theme, name: ColorName, variant: Variant) -> Surface {
237 let dark = theme.scheme.is_dark();
238 let transparent = gpui::transparent_black();
239 let shade = theme.primary_shade();
240 let filled = theme.color(name, shade);
241 let filled_hover = theme.color(name, (shade + 1).min(9));
242 let accent = theme.color(name, if dark { 4 } else { 6 });
244
245 match variant {
246 Variant::Filled => Surface {
247 bg: filled.hsla(),
248 bg_hover: filled_hover.hsla(),
249 fg: filled.contrasting().hsla(),
250 border: None,
251 },
252 Variant::Light => {
253 let (bg, bg_hover, fg) = if dark {
254 (
255 theme.color(name, 5).alpha(0.20),
256 theme.color(name, 5).alpha(0.30),
257 theme.color(name, 2).hsla(),
258 )
259 } else {
260 (
261 theme.color(name, 0).hsla(),
262 theme.color(name, 1).hsla(),
263 theme.color(name, 8).hsla(),
264 )
265 };
266 Surface {
267 bg,
268 bg_hover,
269 fg,
270 border: None,
271 }
272 }
273 Variant::Outline => Surface {
274 bg: transparent,
275 bg_hover: accent.alpha(0.08),
276 fg: accent.hsla(),
277 border: Some(accent.hsla()),
278 },
279 Variant::Subtle => Surface {
280 bg: transparent,
281 bg_hover: accent.alpha(if dark { 0.15 } else { 0.08 }),
282 fg: accent.hsla(),
283 border: None,
284 },
285 Variant::Default => Surface {
286 bg: theme.surface().hsla(),
287 bg_hover: theme.surface_hover().hsla(),
288 fg: theme.text().hsla(),
289 border: Some(theme.border().hsla()),
290 },
291 Variant::Transparent => Surface {
292 bg: transparent,
293 bg_hover: transparent,
294 fg: accent.hsla(),
295 border: None,
296 },
297 Variant::White => Surface {
298 bg: theme.white.hsla(),
299 bg_hover: theme.color(ColorName::Gray, 0).hsla(),
300 fg: theme.color(name, shade).hsla(),
301 border: None,
302 },
303 }
304}