1use gpui::{App, Global};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use crate::{PlotMotion, 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 pub plot: PlotTheme,
23}
24
25impl Global for Theme {}
26
27impl Theme {
28 pub fn global(cx: &App) -> Self {
29 cx.try_global::<Self>().cloned().unwrap_or_default()
30 }
31
32 pub fn global_mut(cx: &mut App) -> &mut Self {
33 if !cx.has_global::<Self>() {
34 cx.set_global(Self::default());
35 }
36 cx.global_mut::<Self>()
37 }
38}
39
40pub(crate) trait ActiveTheme {
42 fn theme(&self) -> Theme;
43}
44
45impl ActiveTheme for App {
46 #[inline(always)]
47 fn theme(&self) -> Theme {
48 Theme::global(self)
49 }
50}
51
52#[derive(Clone, Default)]
57pub struct ScrollbarTheme {
58 mode: ScrollbarMode,
59 motion: ScrollbarMotion,
60 styles: ScrollbarStyles,
61}
62
63impl ScrollbarTheme {
64 pub fn new() -> Self {
65 Self::default()
66 }
67
68 pub fn with_mode(mut self, mode: ScrollbarMode) -> Self {
69 self.mode = mode;
70 self
71 }
72
73 pub fn with_motion(mut self, motion: ScrollbarMotion) -> Self {
74 self.motion = motion;
75 self
76 }
77
78 pub fn with_styles(mut self, styles: ScrollbarStyles) -> Self {
79 self.styles = styles;
80 self
81 }
82
83 pub fn mode(&self) -> ScrollbarMode {
84 self.mode
85 }
86
87 pub fn motion(&self) -> ScrollbarMotion {
88 self.motion
89 }
90
91 pub fn styles(&self) -> &ScrollbarStyles {
92 &self.styles
93 }
94}
95
96#[derive(Clone, Default)]
101pub struct PlotTheme {
102 motion: PlotMotion,
103}
104
105impl PlotTheme {
106 pub fn new() -> Self {
107 Self::default()
108 }
109
110 pub fn with_motion(mut self, motion: PlotMotion) -> Self {
111 self.motion = motion;
112 self
113 }
114
115 pub fn motion(&self) -> &PlotMotion {
116 &self.motion
117 }
118}
119
120#[derive(Clone, Copy, Default)]
134pub struct ResizableTheme {
135 pub handle: Option<gpui::Hsla>,
136 pub active_handle: Option<gpui::Hsla>,
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn scrollbar_theme_supports_builder_construction() {
145 let mode = ScrollbarMode::Hover;
146 let motion = ScrollbarMotion::default().with_enter(std::time::Duration::from_millis(120));
147 let styles = ScrollbarStyles::default();
148 let theme = ScrollbarTheme::new()
149 .with_mode(mode)
150 .with_motion(motion)
151 .with_styles(styles);
152
153 assert_eq!(theme.mode(), mode);
154 assert_eq!(theme.motion(), motion);
155 let _ = theme.styles();
156 }
157}