theme.rs

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