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_container: ContainerStyle,
 72    pub input_editor: InputEditorStyle,
 73    pub sign_in_prompt: TextStyle,
 74    pub hovered_sign_in_prompt: TextStyle,
 75}
 76
 77#[derive(Deserialize)]
 78pub struct ChatMessage {
 79    #[serde(flatten)]
 80    pub container: ContainerStyle,
 81    pub body: TextStyle,
 82    pub sender: ContainedText,
 83    pub timestamp: ContainedText,
 84}
 85
 86#[derive(Deserialize)]
 87pub struct ChannelSelect {
 88    #[serde(flatten)]
 89    pub container: ContainerStyle,
 90    pub header: ChannelName,
 91    pub item: ChannelName,
 92    pub active_item: ChannelName,
 93    pub hovered_item: ChannelName,
 94    pub hovered_active_item: ChannelName,
 95    pub menu: ContainerStyle,
 96}
 97
 98#[derive(Deserialize)]
 99pub struct ChannelName {
100    #[serde(flatten)]
101    pub container: ContainerStyle,
102    pub hash: ContainedText,
103    pub name: TextStyle,
104}
105
106#[derive(Deserialize)]
107pub struct Selector {
108    #[serde(flatten)]
109    pub container: ContainerStyle,
110    #[serde(flatten)]
111    pub label: LabelStyle,
112
113    pub input_editor: InputEditorStyle,
114    pub item: ContainedLabel,
115    pub active_item: ContainedLabel,
116}
117
118#[derive(Deserialize)]
119pub struct ContainedText {
120    #[serde(flatten)]
121    pub container: ContainerStyle,
122    #[serde(flatten)]
123    pub text: TextStyle,
124}
125
126#[derive(Deserialize)]
127pub struct ContainedLabel {
128    #[serde(flatten)]
129    pub container: ContainerStyle,
130    #[serde(flatten)]
131    pub label: LabelStyle,
132}
133
134#[derive(Clone, Deserialize)]
135pub struct EditorStyle {
136    pub text: HighlightStyle,
137    #[serde(default)]
138    pub placeholder_text: HighlightStyle,
139    pub background: Color,
140    pub selection: SelectionStyle,
141    pub gutter_background: Color,
142    pub active_line_background: Color,
143    pub line_number: Color,
144    pub line_number_active: Color,
145    pub guest_selections: Vec<SelectionStyle>,
146}
147
148#[derive(Clone, Deserialize)]
149pub struct InputEditorStyle {
150    pub text: HighlightStyle,
151    pub placeholder_text: HighlightStyle,
152    pub background: Color,
153    pub selection: SelectionStyle,
154}
155
156#[derive(Clone, Copy, Default, Deserialize)]
157pub struct SelectionStyle {
158    pub cursor: Color,
159    pub selection: Color,
160}
161
162impl SyntaxTheme {
163    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
164        Self { highlights }
165    }
166
167    pub fn highlight_style(&self, id: HighlightId) -> Option<HighlightStyle> {
168        self.highlights
169            .get(id.0 as usize)
170            .map(|entry| entry.1.clone())
171    }
172
173    #[cfg(test)]
174    pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
175        self.highlights.get(id.0 as usize).map(|e| e.0.as_str())
176    }
177}
178
179impl Default for EditorStyle {
180    fn default() -> Self {
181        Self {
182            text: HighlightStyle {
183                color: Color::from_u32(0xff0000ff),
184                font_properties: Default::default(),
185                underline: false,
186            },
187            placeholder_text: HighlightStyle {
188                color: Color::from_u32(0x00ff00ff),
189                font_properties: Default::default(),
190                underline: false,
191            },
192            background: Default::default(),
193            gutter_background: Default::default(),
194            active_line_background: Default::default(),
195            line_number: Default::default(),
196            line_number_active: Default::default(),
197            selection: Default::default(),
198            guest_selections: Default::default(),
199        }
200    }
201}
202
203impl InputEditorStyle {
204    pub fn as_editor(&self) -> EditorStyle {
205        EditorStyle {
206            text: self.text.clone(),
207            placeholder_text: self.placeholder_text.clone(),
208            background: self.background,
209            selection: self.selection,
210            ..Default::default()
211        }
212    }
213}
214
215impl<'de> Deserialize<'de> for SyntaxTheme {
216    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
217    where
218        D: serde::Deserializer<'de>,
219    {
220        let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
221
222        let mut result = Self::new(Vec::new());
223        for (key, style) in syntax_data {
224            match result
225                .highlights
226                .binary_search_by(|(needle, _)| needle.cmp(&key))
227            {
228                Ok(i) | Err(i) => {
229                    result.highlights.insert(i, (key, style));
230                }
231            }
232        }
233
234        Ok(result)
235    }
236}