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: HighlightStyle,
167    pub placeholder_text: HighlightStyle,
168    pub selection: SelectionStyle,
169}
170
171impl SyntaxTheme {
172    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
173        Self { highlights }
174    }
175
176    pub fn highlight_style(&self, id: HighlightId) -> Option<HighlightStyle> {
177        self.highlights
178            .get(id.0 as usize)
179            .map(|entry| entry.1.clone())
180    }
181
182    #[cfg(test)]
183    pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
184        self.highlights.get(id.0 as usize).map(|e| e.0.as_str())
185    }
186}
187
188impl InputEditorStyle {
189    pub fn as_editor(&self) -> EditorStyle {
190        EditorStyle {
191            text: self.text.clone(),
192            placeholder_text: self.placeholder_text.clone(),
193            background: self
194                .container
195                .background_color
196                .unwrap_or(Color::transparent_black()),
197            selection: self.selection,
198            ..Default::default()
199        }
200    }
201}
202
203impl<'de> Deserialize<'de> for SyntaxTheme {
204    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
205    where
206        D: serde::Deserializer<'de>,
207    {
208        let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
209
210        let mut result = Self::new(Vec::new());
211        for (key, style) in syntax_data {
212            match result
213                .highlights
214                .binary_search_by(|(needle, _)| needle.cmp(&key))
215            {
216                Ok(i) | Err(i) => {
217                    result.highlights.insert(i, (key, style));
218                }
219            }
220        }
221
222        Ok(result)
223    }
224}