From 1e13e273d2af0198eb5d6001def16399784f1f6a Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Mon, 23 Oct 2023 11:01:04 -0400 Subject: [PATCH] Add ThemeColor interface for UI coloring and remove redundancy --- crates/ui2/src/color.rs | 253 +++++++++++++++++++++++++++++++++++ crates/ui2/src/lib.rs | 5 + crates/ui2/src/prelude.rs | 273 +------------------------------------- 3 files changed, 260 insertions(+), 271 deletions(-) create mode 100644 crates/ui2/src/color.rs diff --git a/crates/ui2/src/color.rs b/crates/ui2/src/color.rs new file mode 100644 index 0000000000..7e7453dc72 --- /dev/null +++ b/crates/ui2/src/color.rs @@ -0,0 +1,253 @@ +pub use crate::{theme, ButtonVariant, ElementExt, Theme}; +use gpui2::{hsla, rgb, Hsla, WindowContext}; +use strum::EnumIter; + +#[derive(Clone, Copy)] +pub struct PlayerThemeColors { + pub cursor: Hsla, + pub selection: Hsla, +} + +impl PlayerThemeColors { + pub fn new(cx: &WindowContext, ix: usize) -> Self { + let theme = theme(cx); + + if ix < theme.players.len() { + Self { + cursor: theme.players[ix].cursor, + selection: theme.players[ix].selection, + } + } else { + Self { + cursor: rgb::(0xff00ff), + selection: rgb::(0xff00ff), + } + } + } +} + +#[derive(Clone, Copy)] +pub struct SyntaxColor { + pub comment: Hsla, + pub string: Hsla, + pub function: Hsla, + pub keyword: Hsla, +} + +impl SyntaxColor { + pub fn new(cx: &WindowContext) -> Self { + let theme = theme(cx); + + Self { + comment: theme + .syntax + .get("comment") + .cloned() + .unwrap_or_else(|| rgb::(0xff00ff)), + string: theme + .syntax + .get("string") + .cloned() + .unwrap_or_else(|| rgb::(0xff00ff)), + function: theme + .syntax + .get("function") + .cloned() + .unwrap_or_else(|| rgb::(0xff00ff)), + keyword: theme + .syntax + .get("keyword") + .cloned() + .unwrap_or_else(|| rgb::(0xff00ff)), + } + } +} + +/// ThemeColor is the primary interface for coloring elements in the UI. +/// +/// It is a mapping layer between semantic theme colors and colors from the reference library. +/// +/// While we are between zed and zed2 we use this to map semantic colors to the old theme. +#[derive(Clone, Copy)] +pub struct ThemeColor { + pub transparent: Hsla, + pub mac_os_traffic_light_red: Hsla, + pub mac_os_traffic_light_yellow: Hsla, + pub mac_os_traffic_light_green: Hsla, + pub border: Hsla, + pub border_variant: Hsla, + pub border_focused: Hsla, + pub border_transparent: Hsla, + /// The background color of an elevated surface, like a modal, tooltip or toast. + pub elevated_surface: Hsla, + pub surface: Hsla, + /// Window background color of the base app + pub background: Hsla, + /// Default background for elements like filled buttons, + /// text fields, checkboxes, radio buttons, etc. + /// - TODO: Map to step 3. + pub filled_element: Hsla, + /// The background color of a hovered element, like a button being hovered + /// with a mouse, or hovered on a touch screen. + /// - TODO: Map to step 4. + pub filled_element_hover: Hsla, + /// The background color of an active element, like a button being pressed, + /// or tapped on a touch screen. + /// - TODO: Map to step 5. + pub filled_element_active: Hsla, + /// The background color of a selected element, like a selected tab, + /// a button toggled on, or a checkbox that is checked. + pub filled_element_selected: Hsla, + pub filled_element_disabled: Hsla, + pub ghost_element: Hsla, + /// The background color of a hovered element with no default background, + /// like a ghost-style button or an interactable list item. + /// - TODO: Map to step 3. + pub ghost_element_hover: Hsla, + /// - TODO: Map to step 4. + pub ghost_element_active: Hsla, + pub ghost_element_selected: Hsla, + pub ghost_element_disabled: Hsla, + pub text: Hsla, + pub text_muted: Hsla, + pub text_placeholder: Hsla, + pub text_disabled: Hsla, + pub text_accent: Hsla, + pub icon_muted: Hsla, + pub syntax: SyntaxColor, + + pub status_bar: Hsla, + pub title_bar: Hsla, + pub toolbar: Hsla, + pub tab_bar: Hsla, + /// The background of the editor + pub editor: Hsla, + pub editor_subheader: Hsla, + pub editor_active_line: Hsla, + pub terminal: Hsla, + pub image_fallback_background: Hsla, + + pub git_created: Hsla, + pub git_modified: Hsla, + pub git_deleted: Hsla, + pub git_conflict: Hsla, + pub git_ignored: Hsla, + pub git_renamed: Hsla, + + pub player: [PlayerThemeColors; 8], +} + +impl ThemeColor { + pub fn new(cx: &WindowContext) -> Self { + let theme = theme(cx); + let transparent = hsla(0.0, 0.0, 0.0, 0.0); + + let players = [ + PlayerThemeColors::new(cx, 0), + PlayerThemeColors::new(cx, 1), + PlayerThemeColors::new(cx, 2), + PlayerThemeColors::new(cx, 3), + PlayerThemeColors::new(cx, 4), + PlayerThemeColors::new(cx, 5), + PlayerThemeColors::new(cx, 6), + PlayerThemeColors::new(cx, 7), + ]; + + Self { + transparent, + mac_os_traffic_light_red: rgb::(0xEC695E), + mac_os_traffic_light_yellow: rgb::(0xF4BF4F), + mac_os_traffic_light_green: rgb::(0x62C554), + border: theme.lowest.base.default.border, + border_variant: theme.lowest.variant.default.border, + border_focused: theme.lowest.accent.default.border, + border_transparent: transparent, + elevated_surface: theme.lowest.base.default.background, + surface: theme.middle.base.default.background, + background: theme.lowest.base.default.background, + filled_element: theme.lowest.base.default.background, + filled_element_hover: theme.lowest.base.hovered.background, + filled_element_active: theme.lowest.base.active.background, + filled_element_selected: theme.lowest.accent.default.background, + filled_element_disabled: transparent, + ghost_element: transparent, + ghost_element_hover: theme.lowest.base.default.background, + ghost_element_active: theme.lowest.base.hovered.background, + ghost_element_selected: theme.lowest.accent.default.background, + ghost_element_disabled: transparent, + text: theme.lowest.base.default.foreground, + text_muted: theme.lowest.variant.default.foreground, + /// TODO: map this to a real value + text_placeholder: theme.lowest.negative.default.foreground, + text_disabled: theme.lowest.base.disabled.foreground, + text_accent: theme.lowest.accent.default.foreground, + icon_muted: theme.lowest.variant.default.foreground, + syntax: SyntaxColor::new(cx), + + status_bar: theme.lowest.base.default.background, + title_bar: theme.lowest.base.default.background, + toolbar: theme.highest.base.default.background, + tab_bar: theme.middle.base.default.background, + editor: theme.highest.base.default.background, + editor_subheader: theme.middle.base.default.background, + terminal: theme.highest.base.default.background, + editor_active_line: theme.highest.on.default.background, + image_fallback_background: theme.lowest.base.default.background, + + git_created: theme.lowest.positive.default.foreground, + git_modified: theme.lowest.accent.default.foreground, + git_deleted: theme.lowest.negative.default.foreground, + git_conflict: theme.lowest.warning.default.foreground, + git_ignored: theme.lowest.base.disabled.foreground, + git_renamed: theme.lowest.warning.default.foreground, + + player: players, + } + } +} + +/// Colors used exclusively for syntax highlighting. +/// +/// For now we deserialize these from a theme. +/// These will be defined statically in the new theme. +#[derive(Default, PartialEq, EnumIter, Clone, Copy)] +pub enum HighlightColor { + #[default] + Default, + Comment, + String, + Function, + Keyword, +} + +impl HighlightColor { + pub fn hsla(&self, theme: &Theme) -> Hsla { + match self { + Self::Default => theme + .syntax + .get("primary") + .cloned() + .expect("Couldn't find `primary` in theme.syntax"), + Self::Comment => theme + .syntax + .get("comment") + .cloned() + .expect("Couldn't find `comment` in theme.syntax"), + Self::String => theme + .syntax + .get("string") + .cloned() + .expect("Couldn't find `string` in theme.syntax"), + Self::Function => theme + .syntax + .get("function") + .cloned() + .expect("Couldn't find `function` in theme.syntax"), + Self::Keyword => theme + .syntax + .get("keyword") + .cloned() + .expect("Couldn't find `keyword` in theme.syntax"), + } + } +} diff --git a/crates/ui2/src/lib.rs b/crates/ui2/src/lib.rs index 20c47f88f0..841599c4b4 100644 --- a/crates/ui2/src/lib.rs +++ b/crates/ui2/src/lib.rs @@ -9,6 +9,10 @@ //! //! Expect some inconsistencies from component to component as we work out the best way to build these components. //! +//! ## Getting Started +//! +//! - [ThemeColor](crate::color::ThemeColor) is your one stop shop for all colors in the UI. +//! //! ## Design Philosophy //! //! Work in Progress! @@ -16,6 +20,7 @@ #![allow(dead_code, unused_variables)] +mod color; mod components; mod element_ext; mod elements; diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index 2b28240034..59d09c76a2 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -3,282 +3,13 @@ pub use gpui2::{ StatelessInteractive, Styled, ViewContext, WindowContext, }; +pub use crate::color::*; use crate::settings::user_settings; pub use crate::{theme, ButtonVariant, ElementExt, Theme}; -use gpui2::{hsla, rems, rgb, Hsla, Rems}; +use gpui2::{rems, Hsla, Rems}; use strum::EnumIter; -// TODO Remove uses in favor of ThemeColor -#[derive(Default)] -pub struct SystemColor { - pub transparent: Hsla, - pub mac_os_traffic_light_red: Hsla, - pub mac_os_traffic_light_yellow: Hsla, - pub mac_os_traffic_light_green: Hsla, - pub state_hover_background: Hsla, - pub state_active_background: Hsla, -} - -impl SystemColor { - pub fn new() -> SystemColor { - SystemColor { - transparent: hsla(0.0, 0.0, 0.0, 0.0), - mac_os_traffic_light_red: rgb::(0xEC695E), - mac_os_traffic_light_yellow: rgb::(0xF4BF4F), - mac_os_traffic_light_green: rgb::(0x62C554), - state_hover_background: hsla(0.0, 0.0, 0.0, 0.08), - state_active_background: hsla(0.0, 0.0, 0.0, 0.16), - } - } - pub fn color(&self) -> Hsla { - self.transparent - } -} - -#[derive(Clone, Copy)] -pub struct PlayerThemeColors { - pub cursor: Hsla, - pub selection: Hsla, -} - -impl PlayerThemeColors { - pub fn new(cx: &WindowContext, ix: usize) -> Self { - let theme = theme(cx); - - if ix < theme.players.len() { - Self { - cursor: theme.players[ix].cursor, - selection: theme.players[ix].selection, - } - } else { - Self { - cursor: rgb::(0xff00ff), - selection: rgb::(0xff00ff), - } - } - } -} - -#[derive(Clone, Copy)] -pub struct SyntaxColor { - pub comment: Hsla, - pub string: Hsla, - pub function: Hsla, - pub keyword: Hsla, -} - -impl SyntaxColor { - pub fn new(cx: &WindowContext) -> Self { - let theme = theme(cx); - - Self { - comment: theme - .syntax - .get("comment") - .cloned() - .unwrap_or_else(|| rgb::(0xff00ff)), - string: theme - .syntax - .get("string") - .cloned() - .unwrap_or_else(|| rgb::(0xff00ff)), - function: theme - .syntax - .get("function") - .cloned() - .unwrap_or_else(|| rgb::(0xff00ff)), - keyword: theme - .syntax - .get("keyword") - .cloned() - .unwrap_or_else(|| rgb::(0xff00ff)), - } - } -} - -#[derive(Clone, Copy)] -pub struct ThemeColor { - pub transparent: Hsla, - pub mac_os_traffic_light_red: Hsla, - pub mac_os_traffic_light_yellow: Hsla, - pub mac_os_traffic_light_green: Hsla, - pub border: Hsla, - pub border_variant: Hsla, - pub border_focused: Hsla, - pub border_transparent: Hsla, - /// The background color of an elevated surface, like a modal, tooltip or toast. - pub elevated_surface: Hsla, - pub surface: Hsla, - /// Window background color of the base app - pub background: Hsla, - /// Default background for elements like filled buttons, - /// text fields, checkboxes, radio buttons, etc. - /// - TODO: Map to step 3. - pub filled_element: Hsla, - /// The background color of a hovered element, like a button being hovered - /// with a mouse, or hovered on a touch screen. - /// - TODO: Map to step 4. - pub filled_element_hover: Hsla, - /// The background color of an active element, like a button being pressed, - /// or tapped on a touch screen. - /// - TODO: Map to step 5. - pub filled_element_active: Hsla, - /// The background color of a selected element, like a selected tab, - /// a button toggled on, or a checkbox that is checked. - pub filled_element_selected: Hsla, - pub filled_element_disabled: Hsla, - pub ghost_element: Hsla, - /// The background color of a hovered element with no default background, - /// like a ghost-style button or an interactable list item. - /// - TODO: Map to step 3. - pub ghost_element_hover: Hsla, - /// - TODO: Map to step 4. - pub ghost_element_active: Hsla, - pub ghost_element_selected: Hsla, - pub ghost_element_disabled: Hsla, - pub text: Hsla, - pub text_muted: Hsla, - pub text_placeholder: Hsla, - pub text_disabled: Hsla, - pub text_accent: Hsla, - pub icon_muted: Hsla, - pub syntax: SyntaxColor, - - pub status_bar: Hsla, - pub title_bar: Hsla, - pub toolbar: Hsla, - pub tab_bar: Hsla, - /// The background of the editor - pub editor: Hsla, - pub editor_subheader: Hsla, - pub editor_active_line: Hsla, - pub terminal: Hsla, - pub image_fallback_background: Hsla, - - pub git_created: Hsla, - pub git_modified: Hsla, - pub git_deleted: Hsla, - pub git_conflict: Hsla, - pub git_ignored: Hsla, - pub git_renamed: Hsla, - - pub player: [PlayerThemeColors; 8], -} - -impl ThemeColor { - pub fn new(cx: &WindowContext) -> Self { - let theme = theme(cx); - let system_color = SystemColor::new(); - - let players = [ - PlayerThemeColors::new(cx, 0), - PlayerThemeColors::new(cx, 1), - PlayerThemeColors::new(cx, 2), - PlayerThemeColors::new(cx, 3), - PlayerThemeColors::new(cx, 4), - PlayerThemeColors::new(cx, 5), - PlayerThemeColors::new(cx, 6), - PlayerThemeColors::new(cx, 7), - ]; - - Self { - transparent: hsla(0.0, 0.0, 0.0, 0.0), - mac_os_traffic_light_red: rgb::(0xEC695E), - mac_os_traffic_light_yellow: rgb::(0xF4BF4F), - mac_os_traffic_light_green: rgb::(0x62C554), - border: theme.lowest.base.default.border, - border_variant: theme.lowest.variant.default.border, - border_focused: theme.lowest.accent.default.border, - border_transparent: system_color.transparent, - elevated_surface: theme.lowest.base.default.background, - surface: theme.middle.base.default.background, - background: theme.lowest.base.default.background, - filled_element: theme.lowest.base.default.background, - filled_element_hover: theme.lowest.base.hovered.background, - filled_element_active: theme.lowest.base.active.background, - filled_element_selected: theme.lowest.accent.default.background, - filled_element_disabled: system_color.transparent, - ghost_element: system_color.transparent, - ghost_element_hover: theme.lowest.base.default.background, - ghost_element_active: theme.lowest.base.hovered.background, - ghost_element_selected: theme.lowest.accent.default.background, - ghost_element_disabled: system_color.transparent, - text: theme.lowest.base.default.foreground, - text_muted: theme.lowest.variant.default.foreground, - /// TODO: map this to a real value - text_placeholder: theme.lowest.negative.default.foreground, - text_disabled: theme.lowest.base.disabled.foreground, - text_accent: theme.lowest.accent.default.foreground, - icon_muted: theme.lowest.variant.default.foreground, - syntax: SyntaxColor::new(cx), - - status_bar: theme.lowest.base.default.background, - title_bar: theme.lowest.base.default.background, - toolbar: theme.highest.base.default.background, - tab_bar: theme.middle.base.default.background, - editor: theme.highest.base.default.background, - editor_subheader: theme.middle.base.default.background, - terminal: theme.highest.base.default.background, - editor_active_line: theme.highest.on.default.background, - image_fallback_background: theme.lowest.base.default.background, - - git_created: theme.lowest.positive.default.foreground, - git_modified: theme.lowest.accent.default.foreground, - git_deleted: theme.lowest.negative.default.foreground, - git_conflict: theme.lowest.warning.default.foreground, - git_ignored: theme.lowest.base.disabled.foreground, - git_renamed: theme.lowest.warning.default.foreground, - - player: players, - } - } -} - -#[derive(Default, PartialEq, EnumIter, Clone, Copy)] -pub enum HighlightColor { - #[default] - Default, - Comment, - String, - Function, - Keyword, -} - -impl HighlightColor { - pub fn hsla(&self, theme: &Theme) -> Hsla { - let system_color = SystemColor::new(); - - match self { - Self::Default => theme - .syntax - .get("primary") - .cloned() - .unwrap_or_else(|| rgb::(0xff00ff)), - Self::Comment => theme - .syntax - .get("comment") - .cloned() - .unwrap_or_else(|| rgb::(0xff00ff)), - Self::String => theme - .syntax - .get("string") - .cloned() - .unwrap_or_else(|| rgb::(0xff00ff)), - Self::Function => theme - .syntax - .get("function") - .cloned() - .unwrap_or_else(|| rgb::(0xff00ff)), - Self::Keyword => theme - .syntax - .get("keyword") - .cloned() - .unwrap_or_else(|| rgb::(0xff00ff)), - } - } -} - pub fn ui_size(cx: &mut WindowContext, size: f32) -> Rems { const UI_SCALE_RATIO: f32 = 0.875;