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