1use gpui::{App, Global};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use crate::{ScrollbarMode, ScrollbarMotion, ScrollbarStyles, SemanticThemeTokens};
6
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
9#[serde(rename_all = "lowercase")]
10pub enum ThemeAppearance {
11 #[default]
12 Light,
13 Dark,
14}
15
16#[derive(Clone, Default)]
17pub struct Theme {
18 pub appearance: ThemeAppearance,
19 pub tokens: SemanticThemeTokens,
20 pub scrollbar: ScrollbarTheme,
21 pub resizable: ResizableTheme,
22}
23
24impl Global for Theme {}
25
26impl Theme {
27 pub fn global(cx: &App) -> Self {
28 cx.try_global::<Self>().cloned().unwrap_or_default()
29 }
30
31 pub fn global_mut(cx: &mut App) -> &mut Self {
32 if !cx.has_global::<Self>() {
33 cx.set_global(Self::default());
34 }
35 cx.global_mut::<Self>()
36 }
37}
38
39pub(crate) trait ActiveTheme {
41 fn theme(&self) -> Theme;
42}
43
44impl ActiveTheme for App {
45 #[inline(always)]
46 fn theme(&self) -> Theme {
47 Theme::global(self)
48 }
49}
50
51#[derive(Clone, Default)]
56pub struct ScrollbarTheme {
57 mode: ScrollbarMode,
58 motion: ScrollbarMotion,
59 styles: ScrollbarStyles,
60}
61
62impl ScrollbarTheme {
63 pub fn new() -> Self {
64 Self::default()
65 }
66
67 pub fn with_mode(mut self, mode: ScrollbarMode) -> Self {
68 self.mode = mode;
69 self
70 }
71
72 pub fn with_motion(mut self, motion: ScrollbarMotion) -> Self {
73 self.motion = motion;
74 self
75 }
76
77 pub fn with_styles(mut self, styles: ScrollbarStyles) -> Self {
78 self.styles = styles;
79 self
80 }
81
82 pub fn mode(&self) -> ScrollbarMode {
83 self.mode
84 }
85
86 pub fn motion(&self) -> ScrollbarMotion {
87 self.motion
88 }
89
90 pub fn styles(&self) -> &ScrollbarStyles {
91 &self.styles
92 }
93}
94
95#[derive(Clone, Copy, Default)]
109pub struct ResizableTheme {
110 pub handle: Option<gpui::Hsla>,
111 pub active_handle: Option<gpui::Hsla>,
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn scrollbar_theme_supports_builder_construction() {
120 let mode = ScrollbarMode::Hover;
121 let motion = ScrollbarMotion::default().with_enter(std::time::Duration::from_millis(120));
122 let styles = ScrollbarStyles::default();
123 let theme = ScrollbarTheme::new()
124 .with_mode(mode)
125 .with_motion(motion)
126 .with_styles(styles);
127
128 assert_eq!(theme.mode(), mode);
129 assert_eq!(theme.motion(), motion);
130 let _ = theme.styles();
131 }
132}