diff --git a/Cargo.lock b/Cargo.lock index 7d17b48cc7..19b1ca6c0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9005,6 +9005,7 @@ dependencies = [ "fs2", "gpui2", "indexmap 1.9.3", + "itertools 0.11.0", "parking_lot 0.11.2", "refineable", "schemars", diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index f59208ccb8..47807def25 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -38,6 +38,7 @@ pub enum ComponentStory { Palette, Panel, ProjectPanel, + Players, RecentProjects, Scroll, Tab, @@ -79,6 +80,7 @@ impl ComponentStory { Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into(), Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into(), Self::Palette => cx.build_view(|cx| ui::PaletteStory).into(), + Self::Players => cx.build_view(|_| theme2::PlayerStory).into(), Self::Panel => cx.build_view(|cx| ui::PanelStory).into(), Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into(), Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into(), diff --git a/crates/theme2/Cargo.toml b/crates/theme2/Cargo.toml index d57c22ede7..45ba4587ba 100644 --- a/crates/theme2/Cargo.toml +++ b/crates/theme2/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" publish = false [features] +default = ["stories"] +stories = ["dep:itertools"] test-support = [ "gpui/test-support", "fs/test-support", @@ -30,6 +32,7 @@ settings = { package = "settings2", path = "../settings2" } toml.workspace = true uuid.workspace = true util = { path = "../util" } +itertools = { version = "0.11.0", optional = true } [dev-dependencies] gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] } diff --git a/crates/theme2/src/colors.rs b/crates/theme2/src/colors.rs index 8ee4b5fd47..8e3db63537 100644 --- a/crates/theme2/src/colors.rs +++ b/crates/theme2/src/colors.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use gpui::Hsla; use refineable::Refineable; -use crate::SyntaxTheme; +use crate::{PlayerColors, SyntaxTheme}; #[derive(Clone)] pub struct SystemColors { @@ -13,33 +13,6 @@ pub struct SystemColors { pub mac_os_traffic_light_green: Hsla, } -#[derive(Debug, Clone, Copy)] -pub struct PlayerColor { - pub cursor: Hsla, - pub background: Hsla, - pub selection: Hsla, -} - -#[derive(Clone)] -pub struct PlayerColors(pub Vec); - -impl PlayerColors { - pub fn local(&self) -> PlayerColor { - // todo!("use a valid color"); - *self.0.first().unwrap() - } - - pub fn absent(&self) -> PlayerColor { - // todo!("use a valid color"); - *self.0.last().unwrap() - } - - pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor { - let len = self.0.len() - 1; - self.0[(participant_index as usize % len) + 1] - } -} - #[derive(Refineable, Clone, Debug)] #[refineable(debug)] pub struct StatusColors { diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 7252e82972..3a626205f9 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -3,12 +3,106 @@ use std::num::ParseIntError; use gpui::{hsla, Hsla, Rgba}; use crate::{ - colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors}, + colors::{GitStatusColors, StatusColors, SystemColors, ThemeColors}, scale::{ColorScaleSet, ColorScales}, syntax::SyntaxTheme, - ColorScale, + ColorScale, PlayerColor, PlayerColors, }; +impl Default for PlayerColors { + fn default() -> Self { + Self(vec![ + PlayerColor { + cursor: blue().dark().step_9(), + background: blue().dark().step_5(), + selection: blue().dark().step_3(), + }, + PlayerColor { + cursor: orange().dark().step_9(), + background: orange().dark().step_5(), + selection: orange().dark().step_3(), + }, + PlayerColor { + cursor: pink().dark().step_9(), + background: pink().dark().step_5(), + selection: pink().dark().step_3(), + }, + PlayerColor { + cursor: lime().dark().step_9(), + background: lime().dark().step_5(), + selection: lime().dark().step_3(), + }, + PlayerColor { + cursor: purple().dark().step_9(), + background: purple().dark().step_5(), + selection: purple().dark().step_3(), + }, + PlayerColor { + cursor: amber().dark().step_9(), + background: amber().dark().step_5(), + selection: amber().dark().step_3(), + }, + PlayerColor { + cursor: jade().dark().step_9(), + background: jade().dark().step_5(), + selection: jade().dark().step_3(), + }, + PlayerColor { + cursor: red().dark().step_9(), + background: red().dark().step_5(), + selection: red().dark().step_3(), + }, + ]) + } +} + +impl PlayerColors { + pub fn default_light() -> Self { + Self(vec![ + PlayerColor { + cursor: blue().light().step_9(), + background: blue().light().step_4(), + selection: blue().light().step_3(), + }, + PlayerColor { + cursor: orange().light().step_9(), + background: orange().light().step_4(), + selection: orange().light().step_3(), + }, + PlayerColor { + cursor: pink().light().step_9(), + background: pink().light().step_4(), + selection: pink().light().step_3(), + }, + PlayerColor { + cursor: lime().light().step_9(), + background: lime().light().step_4(), + selection: lime().light().step_3(), + }, + PlayerColor { + cursor: purple().light().step_9(), + background: purple().light().step_4(), + selection: purple().light().step_3(), + }, + PlayerColor { + cursor: amber().light().step_9(), + background: amber().light().step_4(), + selection: amber().light().step_3(), + }, + PlayerColor { + cursor: jade().light().step_9(), + background: jade().light().step_4(), + selection: jade().light().step_3(), + }, + PlayerColor { + cursor: red().light().step_9(), + background: red().light().step_4(), + selection: red().light().step_3(), + }, + ]) + } +} + fn neutral() -> ColorScaleSet { slate() } @@ -27,17 +121,17 @@ impl Default for SystemColors { impl Default for StatusColors { fn default() -> Self { Self { - conflict: red().dark().step_11(), - created: grass().dark().step_11(), - deleted: red().dark().step_11(), - error: red().dark().step_11(), - hidden: neutral().dark().step_11(), - ignored: neutral().dark().step_11(), - info: blue().dark().step_11(), - modified: yellow().dark().step_11(), - renamed: blue().dark().step_11(), - success: grass().dark().step_11(), - warning: yellow().dark().step_11(), + conflict: red().dark().step_9(), + created: grass().dark().step_9(), + deleted: red().dark().step_9(), + error: red().dark().step_9(), + hidden: neutral().dark().step_9(), + ignored: neutral().dark().step_9(), + info: blue().dark().step_9(), + modified: yellow().dark().step_9(), + renamed: blue().dark().step_9(), + success: grass().dark().step_9(), + warning: yellow().dark().step_9(), } } } @@ -45,43 +139,16 @@ impl Default for StatusColors { impl Default for GitStatusColors { fn default() -> Self { Self { - conflict: orange().dark().step_11(), - created: grass().dark().step_11(), - deleted: red().dark().step_11(), - ignored: neutral().dark().step_11(), - modified: yellow().dark().step_11(), - renamed: blue().dark().step_11(), + conflict: orange().dark().step_9(), + created: grass().dark().step_9(), + deleted: red().dark().step_9(), + ignored: neutral().dark().step_9(), + modified: yellow().dark().step_9(), + renamed: blue().dark().step_9(), } } } -impl Default for PlayerColors { - fn default() -> Self { - Self(vec![ - PlayerColor { - cursor: hsla(0.0, 0.0, 0.0, 1.0), - background: hsla(0.0, 0.0, 0.0, 1.0), - selection: hsla(0.0, 0.0, 0.0, 1.0), - }, - PlayerColor { - cursor: hsla(0.0, 0.0, 0.0, 1.0), - background: hsla(0.0, 0.0, 0.0, 1.0), - selection: hsla(0.0, 0.0, 0.0, 1.0), - }, - PlayerColor { - cursor: hsla(0.0, 0.0, 0.0, 1.0), - background: hsla(0.0, 0.0, 0.0, 1.0), - selection: hsla(0.0, 0.0, 0.0, 1.0), - }, - PlayerColor { - cursor: hsla(0.0, 0.0, 0.0, 1.0), - background: hsla(0.0, 0.0, 0.0, 1.0), - selection: hsla(0.0, 0.0, 0.0, 1.0), - }, - ]) - } -} - impl SyntaxTheme { pub fn default_light() -> Self { Self { diff --git a/crates/theme2/src/default_theme.rs b/crates/theme2/src/default_theme.rs index 3c9634c989..a0947e47c3 100644 --- a/crates/theme2/src/default_theme.rs +++ b/crates/theme2/src/default_theme.rs @@ -1,8 +1,8 @@ use std::sync::Arc; use crate::{ - colors::{GitStatusColors, PlayerColors, StatusColors, SystemColors, ThemeColors, ThemeStyles}, - default_color_scales, Appearance, SyntaxTheme, Theme, ThemeFamily, + colors::{GitStatusColors, StatusColors, SystemColors, ThemeColors, ThemeStyles}, + default_color_scales, Appearance, PlayerColors, SyntaxTheme, Theme, ThemeFamily, }; fn zed_pro_daylight() -> Theme { @@ -15,7 +15,7 @@ fn zed_pro_daylight() -> Theme { colors: ThemeColors::default_light(), status: StatusColors::default(), git: GitStatusColors::default(), - player: PlayerColors::default(), + player: PlayerColors::default_light(), syntax: Arc::new(SyntaxTheme::default_light()), }, } diff --git a/crates/theme2/src/players.rs b/crates/theme2/src/players.rs new file mode 100644 index 0000000000..0e36ff5947 --- /dev/null +++ b/crates/theme2/src/players.rs @@ -0,0 +1,170 @@ +use gpui::Hsla; + +#[derive(Debug, Clone, Copy)] +pub struct PlayerColor { + pub cursor: Hsla, + pub background: Hsla, + pub selection: Hsla, +} + +/// A collection of colors that are used to color players in the editor. +/// +/// The first color is always the local player's color, usually a blue. +/// +/// The rest of the default colors crisscross back and forth on the +/// color wheel so that the colors are as distinct as possible. +#[derive(Clone)] +pub struct PlayerColors(pub Vec); + +impl PlayerColors { + pub fn local(&self) -> PlayerColor { + // todo!("use a valid color"); + *self.0.first().unwrap() + } + + pub fn absent(&self) -> PlayerColor { + // todo!("use a valid color"); + *self.0.last().unwrap() + } + + pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor { + let len = self.0.len() - 1; + self.0[(participant_index as usize % len) + 1] + } +} + +#[cfg(feature = "stories")] +pub use stories::*; + +#[cfg(feature = "stories")] +mod stories { + use super::*; + use crate::{ActiveTheme, Story}; + use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext}; + + pub struct PlayerStory; + + impl Render for PlayerStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + Story::container(cx).child( + div() + .flex() + .flex_col() + .gap_4() + .child(Story::title_for::<_, PlayerColors>(cx)) + .child(Story::label(cx, "Player Colors")) + .child( + div() + .flex() + .flex_col() + .gap_1() + .child( + div().flex().gap_1().children( + cx.theme().players().0.clone().iter_mut().map(|player| { + div().w_8().h_8().rounded_md().bg(player.cursor) + }), + ), + ) + .child(div().flex().gap_1().children( + cx.theme().players().0.clone().iter_mut().map(|player| { + div().w_8().h_8().rounded_md().bg(player.background) + }), + )) + .child(div().flex().gap_1().children( + cx.theme().players().0.clone().iter_mut().map(|player| { + div().w_8().h_8().rounded_md().bg(player.selection) + }), + )), + ) + .child(Story::label(cx, "Avatar Rings")) + .child(div().flex().gap_1().children( + cx.theme().players().0.clone().iter_mut().map(|player| { + div() + .my_1() + .rounded_full() + .border_2() + .border_color(player.cursor) + .child( + img() + .rounded_full() + .uri("https://avatars.githubusercontent.com/u/1714999?v=4") + .size_6() + .bg(gpui::red()), + ) + }), + )) + .child(Story::label(cx, "Player Backgrounds")) + .child(div().flex().gap_1().children( + cx.theme().players().0.clone().iter_mut().map(|player| { + div() + .my_1() + .rounded_xl() + .flex() + .items_center() + .h_8() + .py_0p5() + .px_1p5() + .bg(player.background) + .child( + div().relative().neg_mx_1().rounded_full().z_index(3) + .border_2() + .border_color(player.background) + .size(px(28.)) + .child( + img() + .rounded_full() + .uri("https://avatars.githubusercontent.com/u/1714999?v=4") + .size(px(24.)) + .bg(gpui::red()), + ), + ).child( + div().relative().neg_mx_1().rounded_full().z_index(2) + .border_2() + .border_color(player.background) + .size(px(28.)) + .child( + img() + .rounded_full() + .uri("https://avatars.githubusercontent.com/u/1714999?v=4") + .size(px(24.)) + .bg(gpui::red()), + ), + ).child( + div().relative().neg_mx_1().rounded_full().z_index(1) + .border_2() + .border_color(player.background) + .size(px(28.)) + .child( + img() + .rounded_full() + .uri("https://avatars.githubusercontent.com/u/1714999?v=4") + .size(px(24.)) + .bg(gpui::red()), + ), + ) + }), + )) + .child(Story::label(cx, "Player Selections")) + .child(div().flex().flex_col().gap_px().children( + cx.theme().players().0.clone().iter_mut().map(|player| { + div() + .flex() + .child( + div() + .flex() + .flex_none() + .rounded_sm() + .px_0p5() + .text_color(cx.theme().colors().text) + .bg(player.selection) + .child("The brown fox jumped over the lazy dog."), + ) + .child(div().flex_1()) + }), + )), + ) + } + } +} diff --git a/crates/theme2/src/story.rs b/crates/theme2/src/story.rs new file mode 100644 index 0000000000..8b3754b59e --- /dev/null +++ b/crates/theme2/src/story.rs @@ -0,0 +1,38 @@ +use gpui::{div, Component, Div, ParentElement, Styled, ViewContext}; + +use crate::ActiveTheme; + +pub struct Story {} + +impl Story { + pub fn container(cx: &mut ViewContext) -> Div { + div() + .size_full() + .flex() + .flex_col() + .pt_2() + .px_4() + .font("Zed Mono") + .bg(cx.theme().colors().background) + } + + pub fn title(cx: &mut ViewContext, title: &str) -> impl Component { + div() + .text_xl() + .text_color(cx.theme().colors().text) + .child(title.to_owned()) + } + + pub fn title_for(cx: &mut ViewContext) -> impl Component { + Self::title(cx, std::any::type_name::()) + } + + pub fn label(cx: &mut ViewContext, label: &str) -> impl Component { + div() + .mt_4() + .mb_2() + .text_xs() + .text_color(cx.theme().colors().text) + .child(label.to_owned()) + } +} diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 88db3c55f4..9019eba07a 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -1,6 +1,7 @@ mod colors; mod default_colors; mod default_theme; +mod players; mod registry; mod scale; mod settings; @@ -14,6 +15,7 @@ use ::settings::Settings; pub use colors::*; pub use default_colors::*; pub use default_theme::*; +pub use players::*; pub use registry::*; pub use scale::*; pub use settings::*; @@ -120,3 +122,8 @@ pub struct DiagnosticStyle { pub hint: Hsla, pub ignored: Hsla, } + +#[cfg(feature = "stories")] +mod story; +#[cfg(feature = "stories")] +pub use story::*;