zed/crates/theme/src/theme.rs

671 lines
18 KiB
Rust
Raw Normal View History

mod theme_registry;
use gpui::{
color::Color,
elements::{ContainerStyle, ImageStyle, LabelStyle, TooltipStyle},
fonts::{HighlightStyle, TextStyle},
Border, MouseState,
};
use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value;
use std::{collections::HashMap, sync::Arc};
pub use theme_registry::*;
pub const DEFAULT_THEME_NAME: &'static str = "cave-dark";
#[derive(Deserialize, Default)]
pub struct Theme {
#[serde(default)]
pub name: String,
pub workspace: Workspace,
pub context_menu: ContextMenu,
pub chat_panel: ChatPanel,
pub contacts_panel: ContactsPanel,
pub contact_finder: ContactFinder,
2021-09-24 01:14:15 +00:00
pub project_panel: ProjectPanel,
pub command_palette: CommandPalette,
2022-04-28 21:45:32 +00:00
pub picker: Picker,
pub editor: Editor,
pub search: Search,
pub project_diagnostics: ProjectDiagnostics,
pub breadcrumbs: ContainedText,
pub contact_notification: ContactNotification,
pub update_notification: UpdateNotification,
pub tooltip: TooltipStyle,
2022-06-29 00:07:18 +00:00
pub terminal: TerminalStyle,
}
#[derive(Deserialize, Default)]
pub struct Workspace {
pub background: Color,
pub titlebar: Titlebar,
pub tab: Tab,
pub active_tab: Tab,
pub pane_divider: Border,
pub leader_border_opacity: f32,
pub leader_border_width: f32,
pub sidebar_resize_handle: ContainerStyle,
pub status_bar: StatusBar,
2022-01-31 10:00:29 +00:00
pub toolbar: Toolbar,
pub disconnected_overlay: ContainedText,
pub modal: ContainerStyle,
2022-05-10 23:46:46 +00:00
pub notification: ContainerStyle,
pub notifications: Notifications,
pub joining_project_avatar: ImageStyle,
pub joining_project_message: ContainedText,
}
#[derive(Clone, Deserialize, Default)]
pub struct Titlebar {
#[serde(flatten)]
pub container: ContainerStyle,
2021-11-28 19:41:46 +00:00
pub height: f32,
pub title: TextStyle,
pub avatar_width: f32,
pub avatar_margin: f32,
pub avatar_ribbon: AvatarRibbon,
pub offline_icon: OfflineIcon,
pub avatar: ImageStyle,
pub sign_in_prompt: Interactive<ContainedText>,
pub outdated_warning: ContainedText,
}
#[derive(Clone, Deserialize, Default)]
pub struct AvatarRibbon {
#[serde(flatten)]
pub container: ContainerStyle,
pub width: f32,
pub height: f32,
}
#[derive(Clone, Deserialize, Default)]
pub struct OfflineIcon {
#[serde(flatten)]
pub container: ContainerStyle,
pub width: f32,
2022-01-10 19:26:07 +00:00
pub color: Color,
}
#[derive(Clone, Deserialize, Default)]
pub struct Tab {
pub height: f32,
#[serde(flatten)]
pub container: ContainerStyle,
#[serde(flatten)]
pub label: LabelStyle,
pub spacing: f32,
pub icon_width: f32,
pub icon_close: Color,
pub icon_close_active: Color,
pub icon_dirty: Color,
pub icon_conflict: Color,
}
2022-01-31 10:00:29 +00:00
#[derive(Clone, Deserialize, Default)]
pub struct Toolbar {
#[serde(flatten)]
pub container: ContainerStyle,
2022-01-31 10:00:29 +00:00
pub height: f32,
pub item_spacing: f32,
pub nav_button: Interactive<IconButton>,
2022-01-31 10:00:29 +00:00
}
2022-05-10 23:46:46 +00:00
#[derive(Clone, Deserialize, Default)]
pub struct Notifications {
#[serde(flatten)]
pub container: ContainerStyle,
pub width: f32,
}
#[derive(Clone, Deserialize, Default)]
pub struct Search {
#[serde(flatten)]
pub container: ContainerStyle,
pub editor: FindEditor,
pub invalid_editor: ContainerStyle,
pub option_button_group: ContainerStyle,
pub option_button: Interactive<ContainedText>,
pub match_background: Color,
pub match_index: ContainedText,
pub results_status: TextStyle,
pub tab_icon_width: f32,
pub tab_icon_spacing: f32,
}
#[derive(Clone, Deserialize, Default)]
pub struct FindEditor {
#[serde(flatten)]
pub input: FieldEditor,
pub min_width: f32,
pub max_width: f32,
}
#[derive(Deserialize, Default)]
pub struct StatusBar {
#[serde(flatten)]
pub container: ContainerStyle,
pub height: f32,
pub item_spacing: f32,
pub cursor_position: TextStyle,
pub auto_update_progress_message: TextStyle,
pub auto_update_done_message: TextStyle,
pub lsp_status: Interactive<StatusBarLspStatus>,
pub feedback: Interactive<TextStyle>,
pub sidebar_buttons: StatusBarSidebarButtons,
pub diagnostic_summary: Interactive<StatusBarDiagnosticSummary>,
pub diagnostic_message: Interactive<ContainedText>,
}
#[derive(Deserialize, Default)]
pub struct StatusBarSidebarButtons {
pub group_left: ContainerStyle,
pub group_right: ContainerStyle,
pub item: Interactive<SidebarItem>,
pub badge: ContainerStyle,
}
#[derive(Deserialize, Default)]
pub struct StatusBarDiagnosticSummary {
pub container_ok: ContainerStyle,
pub container_warning: ContainerStyle,
pub container_error: ContainerStyle,
pub text: TextStyle,
pub icon_color_ok: Color,
pub icon_color_warning: Color,
pub icon_color_error: Color,
pub height: f32,
pub icon_width: f32,
pub icon_spacing: f32,
pub summary_spacing: f32,
}
#[derive(Deserialize, Default)]
pub struct StatusBarLspStatus {
#[serde(flatten)]
pub container: ContainerStyle,
pub height: f32,
pub icon_spacing: f32,
pub icon_color: Color,
pub icon_width: f32,
pub message: TextStyle,
}
#[derive(Deserialize, Default)]
pub struct Sidebar {
pub resize_handle: ContainerStyle,
}
#[derive(Clone, Copy, Deserialize, Default)]
pub struct SidebarItem {
#[serde(flatten)]
pub container: ContainerStyle,
pub icon_color: Color,
pub icon_size: f32,
}
#[derive(Deserialize, Default)]
pub struct ChatPanel {
2021-08-25 22:22:14 +00:00
#[serde(flatten)]
pub container: ContainerStyle,
pub message: ChatMessage,
2021-09-15 13:12:16 +00:00
pub pending_message: ChatMessage,
pub channel_select: ChannelSelect,
pub input_editor: FieldEditor,
pub sign_in_prompt: TextStyle,
pub hovered_sign_in_prompt: TextStyle,
}
#[derive(Deserialize, Default)]
2021-09-24 01:14:15 +00:00
pub struct ProjectPanel {
#[serde(flatten)]
pub container: ContainerStyle,
2022-04-29 18:08:25 +00:00
pub entry: Interactive<ProjectPanelEntry>,
2022-05-30 10:23:21 +00:00
pub cut_entry_fade: f32,
2022-05-24 07:12:57 +00:00
pub ignored_entry_fade: f32,
pub filename_editor: FieldEditor,
2022-04-29 18:08:25 +00:00
pub indent_width: f32,
}
2022-05-23 14:37:57 +00:00
#[derive(Clone, Debug, Deserialize, Default)]
pub struct ProjectPanelEntry {
pub height: f32,
#[serde(flatten)]
pub container: ContainerStyle,
pub text: TextStyle,
pub icon_color: Color,
pub icon_size: f32,
pub icon_spacing: f32,
2021-09-24 01:14:15 +00:00
}
#[derive(Clone, Debug, Deserialize, Default)]
pub struct ContextMenu {
2022-05-25 01:59:08 +00:00
#[serde(flatten)]
pub container: ContainerStyle,
2022-05-25 08:23:43 +00:00
pub item: Interactive<ContextMenuItem>,
pub separator: ContainerStyle,
2022-05-25 01:59:08 +00:00
}
#[derive(Clone, Debug, Deserialize, Default)]
pub struct ContextMenuItem {
#[serde(flatten)]
pub container: ContainerStyle,
pub label: TextStyle,
2022-05-25 13:24:44 +00:00
pub keystroke: ContainedText,
}
#[derive(Debug, Deserialize, Default)]
pub struct CommandPalette {
pub key: Interactive<ContainedLabel>,
pub keystroke_spacing: f32,
}
#[derive(Deserialize, Default)]
pub struct ContactsPanel {
#[serde(flatten)]
pub container: ContainerStyle,
pub user_query_editor: FieldEditor,
pub user_query_editor_height: f32,
2022-05-10 16:25:47 +00:00
pub add_contact_button: IconButton,
pub header_row: Interactive<ContainedText>,
pub contact_row: Interactive<ContainerStyle>,
pub project_row: Interactive<ProjectRow>,
pub row_height: f32,
pub contact_avatar: ImageStyle,
pub contact_username: ContainedText,
pub contact_button: Interactive<IconButton>,
pub contact_button_spacing: f32,
pub disabled_button: IconButton,
pub tree_branch: Interactive<TreeBranch>,
pub private_button: Interactive<IconButton>,
pub section_icon_size: f32,
pub invite_row: Interactive<ContainedLabel>,
}
#[derive(Deserialize, Default)]
pub struct InviteLink {
#[serde(flatten)]
pub container: ContainerStyle,
#[serde(flatten)]
pub label: LabelStyle,
pub icon: Icon,
}
#[derive(Deserialize, Default, Clone, Copy)]
pub struct TreeBranch {
pub width: f32,
pub color: Color,
}
#[derive(Deserialize, Default)]
pub struct ContactFinder {
pub row_height: f32,
pub contact_avatar: ImageStyle,
pub contact_username: ContainerStyle,
2022-05-10 16:25:47 +00:00
pub contact_button: IconButton,
pub disabled_contact_button: IconButton,
}
#[derive(Deserialize, Default)]
pub struct Icon {
#[serde(flatten)]
pub container: ContainerStyle,
pub color: Color,
pub width: f32,
pub path: String,
}
2022-05-31 23:47:06 +00:00
#[derive(Deserialize, Clone, Copy, Default)]
2022-05-10 16:25:47 +00:00
pub struct IconButton {
#[serde(flatten)]
pub container: ContainerStyle,
pub color: Color,
2022-05-10 16:25:47 +00:00
pub icon_width: f32,
pub button_width: f32,
}
#[derive(Deserialize, Default)]
pub struct ProjectRow {
#[serde(flatten)]
pub container: ContainerStyle,
pub name: ContainedText,
pub guests: ContainerStyle,
pub guest_avatar: ImageStyle,
pub guest_avatar_spacing: f32,
}
#[derive(Deserialize, Default)]
pub struct ChatMessage {
#[serde(flatten)]
pub container: ContainerStyle,
pub body: TextStyle,
pub sender: ContainedText,
pub timestamp: ContainedText,
}
#[derive(Deserialize, Default)]
pub struct ChannelSelect {
#[serde(flatten)]
pub container: ContainerStyle,
pub header: ChannelName,
pub item: ChannelName,
pub active_item: ChannelName,
pub hovered_item: ChannelName,
pub hovered_active_item: ChannelName,
pub menu: ContainerStyle,
}
#[derive(Deserialize, Default)]
pub struct ChannelName {
#[serde(flatten)]
pub container: ContainerStyle,
pub hash: ContainedText,
pub name: TextStyle,
}
#[derive(Deserialize, Default)]
2022-04-28 21:45:32 +00:00
pub struct Picker {
#[serde(flatten)]
pub container: ContainerStyle,
pub empty: ContainedLabel,
pub input_editor: FieldEditor,
pub item: Interactive<ContainedLabel>,
}
#[derive(Clone, Debug, Deserialize, Default)]
pub struct ContainedText {
#[serde(flatten)]
pub container: ContainerStyle,
#[serde(flatten)]
pub text: TextStyle,
}
#[derive(Clone, Debug, Deserialize, Default)]
2021-08-25 22:22:14 +00:00
pub struct ContainedLabel {
#[serde(flatten)]
pub container: ContainerStyle,
#[serde(flatten)]
pub label: LabelStyle,
}
#[derive(Clone, Deserialize, Default)]
pub struct ProjectDiagnostics {
#[serde(flatten)]
pub container: ContainerStyle,
pub empty_message: TextStyle,
pub tab_icon_width: f32,
pub tab_icon_spacing: f32,
pub tab_summary_spacing: f32,
}
#[derive(Deserialize, Default)]
pub struct ContactNotification {
pub header_avatar: ImageStyle,
pub header_message: ContainedText,
pub header_height: f32,
pub body_message: ContainedText,
pub button: Interactive<ContainedText>,
pub dismiss_button: Interactive<IconButton>,
}
#[derive(Deserialize, Default)]
pub struct UpdateNotification {
pub message: ContainedText,
pub action_message: Interactive<ContainedText>,
pub dismiss_button: Interactive<IconButton>,
}
#[derive(Clone, Deserialize, Default)]
pub struct Editor {
pub text_color: Color,
#[serde(default)]
pub background: Color,
pub selection: SelectionStyle,
pub gutter_background: Color,
pub gutter_padding_factor: f32,
pub active_line_background: Color,
pub highlighted_line_background: Color,
pub rename_fade: f32,
pub document_highlight_read_background: Color,
pub document_highlight_write_background: Color,
pub diff_background_deleted: Color,
pub diff_background_inserted: Color,
pub line_number: Color,
pub line_number_active: Color,
pub guest_selections: Vec<SelectionStyle>,
pub syntax: Arc<SyntaxTheme>,
2022-01-25 11:23:24 +00:00
pub diagnostic_path_header: DiagnosticPathHeader,
pub diagnostic_header: DiagnosticHeader,
pub error_diagnostic: DiagnosticStyle,
pub invalid_error_diagnostic: DiagnosticStyle,
pub warning_diagnostic: DiagnosticStyle,
pub invalid_warning_diagnostic: DiagnosticStyle,
pub information_diagnostic: DiagnosticStyle,
pub invalid_information_diagnostic: DiagnosticStyle,
pub hint_diagnostic: DiagnosticStyle,
pub invalid_hint_diagnostic: DiagnosticStyle,
pub autocomplete: AutocompleteStyle,
pub code_actions_indicator: Color,
pub unnecessary_code_fade: f32,
pub hover_popover: HoverPopover,
2022-06-24 22:02:18 +00:00
pub link_definition: HighlightStyle,
pub jump_icon: Interactive<IconButton>,
}
2022-01-25 11:23:24 +00:00
#[derive(Clone, Deserialize, Default)]
pub struct DiagnosticPathHeader {
#[serde(flatten)]
pub container: ContainerStyle,
pub filename: ContainedText,
pub path: ContainedText,
pub text_scale_factor: f32,
2022-01-25 11:23:24 +00:00
}
#[derive(Clone, Deserialize, Default)]
pub struct DiagnosticHeader {
#[serde(flatten)]
pub container: ContainerStyle,
pub message: ContainedLabel,
2022-01-25 14:23:58 +00:00
pub code: ContainedText,
pub text_scale_factor: f32,
pub icon_width_factor: f32,
2022-01-25 11:23:24 +00:00
}
#[derive(Clone, Deserialize, Default)]
pub struct DiagnosticStyle {
pub message: LabelStyle,
#[serde(default)]
pub header: ContainerStyle,
pub text_scale_factor: f32,
}
#[derive(Clone, Deserialize, Default)]
pub struct AutocompleteStyle {
#[serde(flatten)]
pub container: ContainerStyle,
pub item: ContainerStyle,
pub selected_item: ContainerStyle,
pub hovered_item: ContainerStyle,
pub match_highlight: HighlightStyle,
}
#[derive(Clone, Copy, Default, Deserialize)]
pub struct SelectionStyle {
pub cursor: Color,
pub selection: Color,
}
#[derive(Clone, Deserialize, Default)]
pub struct FieldEditor {
#[serde(flatten)]
pub container: ContainerStyle,
2021-09-16 20:12:38 +00:00
pub text: TextStyle,
2021-09-16 19:23:42 +00:00
#[serde(default)]
2021-09-16 20:12:38 +00:00
pub placeholder_text: Option<TextStyle>,
pub selection: SelectionStyle,
}
2022-04-28 21:45:32 +00:00
#[derive(Debug, Default, Clone, Copy)]
pub struct Interactive<T> {
pub default: T,
pub hover: Option<T>,
pub active: Option<T>,
pub active_hover: Option<T>,
}
impl<T> Interactive<T> {
pub fn style_for(&self, state: MouseState, active: bool) -> &T {
if active {
if state.hovered {
self.active_hover
.as_ref()
.or(self.active.as_ref())
.unwrap_or(&self.default)
} else {
self.active.as_ref().unwrap_or(&self.default)
}
} else {
if state.hovered {
self.hover.as_ref().unwrap_or(&self.default)
} else {
&self.default
}
}
}
}
impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
struct Helper {
#[serde(flatten)]
default: Value,
hover: Option<Value>,
active: Option<Value>,
active_hover: Option<Value>,
}
let json = Helper::deserialize(deserializer)?;
let deserialize_state = |state_json: Option<Value>| -> Result<Option<T>, D::Error> {
if let Some(mut state_json) = state_json {
if let Value::Object(state_json) = &mut state_json {
if let Value::Object(default) = &json.default {
for (key, value) in default {
if !state_json.contains_key(key) {
state_json.insert(key.clone(), value.clone());
}
}
}
}
Ok(Some(
serde_json::from_value::<T>(state_json).map_err(serde::de::Error::custom)?,
))
} else {
Ok(None)
}
};
let hover = deserialize_state(json.hover)?;
let active = deserialize_state(json.active)?;
let active_hover = deserialize_state(json.active_hover)?;
let default = serde_json::from_value(json.default).map_err(serde::de::Error::custom)?;
Ok(Interactive {
default,
hover,
active,
active_hover,
})
}
}
impl Editor {
pub fn replica_selection_style(&self, replica_id: u16) -> &SelectionStyle {
let style_ix = replica_id as usize % (self.guest_selections.len() + 1);
if style_ix == 0 {
&self.selection
} else {
&self.guest_selections[style_ix - 1]
}
}
}
#[derive(Default)]
pub struct SyntaxTheme {
pub highlights: Vec<(String, HighlightStyle)>,
}
impl SyntaxTheme {
pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
Self { highlights }
}
}
impl<'de> Deserialize<'de> for SyntaxTheme {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
let mut result = Self::new(Vec::new());
for (key, style) in syntax_data {
match result
.highlights
.binary_search_by(|(needle, _)| needle.cmp(&key))
{
Ok(i) | Err(i) => {
result.highlights.insert(i, (key, style));
}
}
}
Ok(result)
}
}
#[derive(Clone, Deserialize, Default)]
pub struct HoverPopover {
pub container: ContainerStyle,
pub block_style: ContainerStyle,
pub prose: TextStyle,
pub highlight: Color,
}
2022-06-29 00:07:18 +00:00
#[derive(Clone, Deserialize, Default)]
pub struct TerminalStyle {
pub black: Color,
pub red: Color,
pub green: Color,
pub yellow: Color,
pub blue: Color,
pub magenta: Color,
pub cyan: Color,
pub white: Color,
pub bright_black: Color,
pub bright_red: Color,
pub bright_green: Color,
pub bright_yellow: Color,
pub bright_blue: Color,
pub bright_magenta: Color,
pub bright_cyan: Color,
pub bright_white: Color,
pub foreground: Color,
pub background: Color,
pub cursor: Color,
pub dim_black: Color,
pub dim_red: Color,
pub dim_green: Color,
pub dim_yellow: Color,
pub dim_blue: Color,
pub dim_magenta: Color,
pub dim_cyan: Color,
pub dim_white: Color,
pub bright_foreground: Color,
pub dim_foreground: Color,
}