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