1use gpui::{App, Global};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 PlotMotion, ScrollbarMode, ScrollbarMotion, ScrollbarStyles, SemanticThemeTokens,
7 input::CursorBlink,
8};
9
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
12#[serde(rename_all = "lowercase")]
13pub enum ThemeAppearance {
14 #[default]
15 Light,
16 Dark,
17}
18
19#[derive(Clone, Default)]
22#[non_exhaustive]
23pub struct Theme {
24 pub appearance: ThemeAppearance,
25 pub tokens: SemanticThemeTokens,
26 pub scrollbar: ScrollbarTheme,
27 pub resizable: ResizableTheme,
28 pub plot: PlotTheme,
29 pub cursor_blink: CursorBlink,
31}
32
33impl Global for Theme {}
34
35impl Theme {
36 pub fn global(cx: &App) -> Self {
37 cx.try_global::<Self>().cloned().unwrap_or_default()
38 }
39
40 pub fn global_mut(cx: &mut App) -> &mut Self {
41 if !cx.has_global::<Self>() {
42 cx.set_global(Self::default());
43 }
44 cx.global_mut::<Self>()
45 }
46}
47
48pub(crate) trait ActiveTheme {
50 fn theme(&self) -> Theme;
51}
52
53impl ActiveTheme for App {
54 #[inline(always)]
55 fn theme(&self) -> Theme {
56 Theme::global(self)
57 }
58}
59
60#[derive(Clone, Default)]
65pub struct ScrollbarTheme {
66 mode: ScrollbarMode,
67 motion: ScrollbarMotion,
68 styles: ScrollbarStyles,
69}
70
71impl ScrollbarTheme {
72 pub fn new() -> Self {
73 Self::default()
74 }
75
76 pub fn with_mode(mut self, mode: ScrollbarMode) -> Self {
77 self.mode = mode;
78 self
79 }
80
81 pub fn with_motion(mut self, motion: ScrollbarMotion) -> Self {
82 self.motion = motion;
83 self
84 }
85
86 pub fn with_styles(mut self, styles: ScrollbarStyles) -> Self {
87 self.styles = styles;
88 self
89 }
90
91 pub fn mode(&self) -> ScrollbarMode {
92 self.mode
93 }
94
95 pub fn motion(&self) -> ScrollbarMotion {
96 self.motion
97 }
98
99 pub fn styles(&self) -> &ScrollbarStyles {
100 &self.styles
101 }
102}
103
104#[derive(Clone, Default)]
109pub struct PlotTheme {
110 motion: PlotMotion,
111}
112
113impl PlotTheme {
114 pub fn new() -> Self {
115 Self::default()
116 }
117
118 pub fn with_motion(mut self, motion: PlotMotion) -> Self {
119 self.motion = motion;
120 self
121 }
122
123 pub fn motion(&self) -> &PlotMotion {
124 &self.motion
125 }
126}
127
128#[derive(Clone, Copy, Default)]
142#[non_exhaustive]
143pub struct ResizableTheme {
144 pub handle: Option<gpui::Hsla>,
145 pub active_handle: Option<gpui::Hsla>,
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn scrollbar_theme_supports_builder_construction() {
154 let mode = ScrollbarMode::Hover;
155 let motion = ScrollbarMotion::default().with_enter(std::time::Duration::from_millis(120));
156 let styles = ScrollbarStyles::default();
157 let theme = ScrollbarTheme::new()
158 .with_mode(mode)
159 .with_motion(motion)
160 .with_styles(styles);
161
162 assert_eq!(theme.mode(), mode);
163 assert_eq!(theme.motion(), motion);
164 let _ = theme.styles();
165 }
166}