theme.rs

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