theme.rs

  1mod highlight_map;
  2mod theme_registry;
  3
  4use anyhow::Result;
  5use gpui::{
  6    color::Color,
  7    elements::{ContainerStyle, ImageStyle, LabelStyle},
  8    fonts::{HighlightStyle, TextStyle},
  9    Border,
 10};
 11use serde::Deserialize;
 12use std::collections::HashMap;
 13
 14pub use highlight_map::*;
 15pub use theme_registry::*;
 16
 17pub const DEFAULT_THEME_NAME: &'static str = "black";
 18
 19#[derive(Deserialize)]
 20pub struct Theme {
 21    #[serde(default)]
 22    pub name: String,
 23    pub workspace: Workspace,
 24    pub chat_panel: ChatPanel,
 25    pub selector: Selector,
 26    pub editor: EditorStyle,
 27    pub syntax: SyntaxTheme,
 28}
 29
 30pub struct SyntaxTheme {
 31    highlights: Vec<(String, HighlightStyle)>,
 32}
 33
 34#[derive(Deserialize)]
 35pub struct Workspace {
 36    pub background: Color,
 37    pub titlebar: Titlebar,
 38    pub tab: Tab,
 39    pub active_tab: Tab,
 40    pub pane_divider: Border,
 41    pub left_sidebar: Sidebar,
 42    pub right_sidebar: Sidebar,
 43}
 44
 45#[derive(Clone, Deserialize)]
 46pub struct Titlebar {
 47    #[serde(flatten)]
 48    pub container: ContainerStyle,
 49    pub title: TextStyle,
 50    pub avatar_width: f32,
 51    pub offline_icon: OfflineIcon,
 52    pub icon_color: Color,
 53    pub avatar: ImageStyle,
 54}
 55
 56#[derive(Clone, Deserialize)]
 57pub struct OfflineIcon {
 58    #[serde(flatten)]
 59    pub container: ContainerStyle,
 60    pub width: f32,
 61}
 62
 63#[derive(Clone, Deserialize)]
 64pub struct Tab {
 65    #[serde(flatten)]
 66    pub container: ContainerStyle,
 67    #[serde(flatten)]
 68    pub label: LabelStyle,
 69    pub spacing: f32,
 70    pub icon_width: f32,
 71    pub icon_close: Color,
 72    pub icon_close_active: Color,
 73    pub icon_dirty: Color,
 74    pub icon_conflict: Color,
 75}
 76
 77#[derive(Deserialize)]
 78pub struct Sidebar {
 79    #[serde(flatten)]
 80    pub container: ContainerStyle,
 81    pub width: f32,
 82    pub icon: SidebarIcon,
 83    pub active_icon: SidebarIcon,
 84    pub resize_handle: ContainerStyle,
 85}
 86
 87#[derive(Deserialize)]
 88pub struct SidebarIcon {
 89    pub color: Color,
 90    pub height: f32,
 91}
 92
 93#[derive(Deserialize)]
 94pub struct ChatPanel {
 95    #[serde(flatten)]
 96    pub container: ContainerStyle,
 97    pub message: ChatMessage,
 98    pub channel_select: ChannelSelect,
 99    pub input_editor: InputEditorStyle,
100    pub sign_in_prompt: TextStyle,
101    pub hovered_sign_in_prompt: TextStyle,
102}
103
104#[derive(Deserialize)]
105pub struct ChatMessage {
106    #[serde(flatten)]
107    pub container: ContainerStyle,
108    pub body: TextStyle,
109    pub sender: ContainedText,
110    pub timestamp: ContainedText,
111}
112
113#[derive(Deserialize)]
114pub struct ChannelSelect {
115    #[serde(flatten)]
116    pub container: ContainerStyle,
117    pub header: ChannelName,
118    pub item: ChannelName,
119    pub active_item: ChannelName,
120    pub hovered_item: ChannelName,
121    pub hovered_active_item: ChannelName,
122    pub menu: ContainerStyle,
123}
124
125#[derive(Deserialize)]
126pub struct ChannelName {
127    #[serde(flatten)]
128    pub container: ContainerStyle,
129    pub hash: ContainedText,
130    pub name: TextStyle,
131}
132
133#[derive(Deserialize)]
134pub struct Selector {
135    #[serde(flatten)]
136    pub container: ContainerStyle,
137    pub empty: ContainedLabel,
138    pub input_editor: InputEditorStyle,
139    pub item: ContainedLabel,
140    pub active_item: ContainedLabel,
141}
142
143#[derive(Deserialize)]
144pub struct ContainedText {
145    #[serde(flatten)]
146    pub container: ContainerStyle,
147    #[serde(flatten)]
148    pub text: TextStyle,
149}
150
151#[derive(Deserialize)]
152pub struct ContainedLabel {
153    #[serde(flatten)]
154    pub container: ContainerStyle,
155    #[serde(flatten)]
156    pub label: LabelStyle,
157}
158
159#[derive(Clone, Deserialize)]
160pub struct EditorStyle {
161    pub text: HighlightStyle,
162    #[serde(default)]
163    pub placeholder_text: HighlightStyle,
164    pub background: Color,
165    pub selection: SelectionStyle,
166    pub gutter_background: Color,
167    pub active_line_background: Color,
168    pub line_number: Color,
169    pub line_number_active: Color,
170    pub guest_selections: Vec<SelectionStyle>,
171}
172
173#[derive(Clone, Deserialize)]
174pub struct InputEditorStyle {
175    #[serde(flatten)]
176    pub container: ContainerStyle,
177    pub text: HighlightStyle,
178    pub placeholder_text: HighlightStyle,
179    pub selection: SelectionStyle,
180}
181
182#[derive(Clone, Copy, Default, Deserialize)]
183pub struct SelectionStyle {
184    pub cursor: Color,
185    pub selection: Color,
186}
187
188impl SyntaxTheme {
189    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
190        Self { highlights }
191    }
192
193    pub fn highlight_style(&self, id: HighlightId) -> Option<HighlightStyle> {
194        self.highlights
195            .get(id.0 as usize)
196            .map(|entry| entry.1.clone())
197    }
198
199    #[cfg(test)]
200    pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
201        self.highlights.get(id.0 as usize).map(|e| e.0.as_str())
202    }
203}
204
205impl Default for EditorStyle {
206    fn default() -> Self {
207        Self {
208            text: HighlightStyle {
209                color: Color::from_u32(0xff0000ff),
210                font_properties: Default::default(),
211                underline: false,
212            },
213            placeholder_text: HighlightStyle {
214                color: Color::from_u32(0x00ff00ff),
215                font_properties: Default::default(),
216                underline: false,
217            },
218            background: Default::default(),
219            gutter_background: Default::default(),
220            active_line_background: Default::default(),
221            line_number: Default::default(),
222            line_number_active: Default::default(),
223            selection: Default::default(),
224            guest_selections: Default::default(),
225        }
226    }
227}
228
229impl InputEditorStyle {
230    pub fn as_editor(&self) -> EditorStyle {
231        EditorStyle {
232            text: self.text.clone(),
233            placeholder_text: self.placeholder_text.clone(),
234            background: self
235                .container
236                .background_color
237                .unwrap_or(Color::transparent_black()),
238            selection: self.selection,
239            ..Default::default()
240        }
241    }
242}
243
244impl<'de> Deserialize<'de> for SyntaxTheme {
245    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
246    where
247        D: serde::Deserializer<'de>,
248    {
249        let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
250
251        let mut result = Self::new(Vec::new());
252        for (key, style) in syntax_data {
253            match result
254                .highlights
255                .binary_search_by(|(needle, _)| needle.cmp(&key))
256            {
257                Ok(i) | Err(i) => {
258                    result.highlights.insert(i, (key, style));
259                }
260            }
261        }
262
263        Ok(result)
264    }
265}