diff --git a/Cargo.lock b/Cargo.lock index 76de671620..f146c4b8d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2790,7 +2790,6 @@ dependencies = [ "lazy_static", "libc", "log", - "lsp", "parking_lot 0.11.2", "regex", "rope", @@ -7403,6 +7402,8 @@ dependencies = [ "anyhow", "chrono", "clap 4.4.4", + "fs", + "futures 0.3.28", "gpui2", "itertools 0.11.0", "log", @@ -8638,6 +8639,7 @@ dependencies = [ "anyhow", "chrono", "gpui2", + "rand 0.8.5", "serde", "settings", "smallvec", diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index 78146c3a9d..441ce6f9c7 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -9,8 +9,6 @@ path = "src/fs.rs" [dependencies] collections = { path = "../collections" } -gpui = { path = "../gpui" } -lsp = { path = "../lsp" } rope = { path = "../rope" } text = { path = "../text" } util = { path = "../util" } @@ -34,8 +32,10 @@ log.workspace = true libc = "0.2" time.workspace = true +gpui = { path = "../gpui", optional = true} + [dev-dependencies] gpui = { path = "../gpui", features = ["test-support"] } [features] -test-support = [] +test-support = ["gpui/test-support"] diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 97175cb55e..1d95db9b6c 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -93,33 +93,6 @@ pub struct Metadata { pub is_dir: bool, } -impl From for CreateOptions { - fn from(options: lsp::CreateFileOptions) -> Self { - Self { - overwrite: options.overwrite.unwrap_or(false), - ignore_if_exists: options.ignore_if_exists.unwrap_or(false), - } - } -} - -impl From for RenameOptions { - fn from(options: lsp::RenameFileOptions) -> Self { - Self { - overwrite: options.overwrite.unwrap_or(false), - ignore_if_exists: options.ignore_if_exists.unwrap_or(false), - } - } -} - -impl From for RemoveOptions { - fn from(options: lsp::DeleteFileOptions) -> Self { - Self { - recursive: options.recursive.unwrap_or(false), - ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false), - } - } -} - pub struct RealFs; #[async_trait::async_trait] diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 95b7ccb559..6aeef558c0 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -11,7 +11,7 @@ path = "src/gpui.rs" doctest = false [features] -test-support = ["backtrace", "dhat", "env_logger", "collections/test-support"] +test-support = ["backtrace", "dhat", "env_logger", "collections/test-support", "util/test-support"] [dependencies] collections = { path = "../collections" } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index ee8690ea70..1194593157 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4957,8 +4957,16 @@ impl Project { if abs_path.ends_with("/") { fs.create_dir(&abs_path).await?; } else { - fs.create_file(&abs_path, op.options.map(Into::into).unwrap_or_default()) - .await?; + fs.create_file( + &abs_path, + op.options + .map(|options| fs::CreateOptions { + overwrite: options.overwrite.unwrap_or(false), + ignore_if_exists: options.ignore_if_exists.unwrap_or(false), + }) + .unwrap_or_default(), + ) + .await?; } } @@ -4974,7 +4982,12 @@ impl Project { fs.rename( &source_abs_path, &target_abs_path, - op.options.map(Into::into).unwrap_or_default(), + op.options + .map(|options| fs::RenameOptions { + overwrite: options.overwrite.unwrap_or(false), + ignore_if_exists: options.ignore_if_exists.unwrap_or(false), + }) + .unwrap_or_default(), ) .await?; } @@ -4984,7 +4997,13 @@ impl Project { .uri .to_file_path() .map_err(|_| anyhow!("can't convert URI to path"))?; - let options = op.options.map(Into::into).unwrap_or_default(); + let options = op + .options + .map(|options| fs::RemoveOptions { + recursive: options.recursive.unwrap_or(false), + ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false), + }) + .unwrap_or_default(); if abs_path.ends_with("/") { fs.remove_dir(&abs_path, options).await?; } else { diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml index 73c8361613..43890dd01a 100644 --- a/crates/storybook/Cargo.toml +++ b/crates/storybook/Cargo.toml @@ -12,6 +12,8 @@ path = "src/storybook.rs" anyhow.workspace = true clap = { version = "4.4", features = ["derive", "string"] } chrono = "0.4" +fs = { path = "../fs" } +futures.workspace = true gpui2 = { path = "../gpui2" } itertools = "0.11.0" log.workspace = true diff --git a/crates/storybook/src/stories/components/breadcrumb.rs b/crates/storybook/src/stories/components/breadcrumb.rs index 8d144c0174..002b6140e1 100644 --- a/crates/storybook/src/stories/components/breadcrumb.rs +++ b/crates/storybook/src/stories/components/breadcrumb.rs @@ -1,5 +1,8 @@ +use std::path::PathBuf; +use std::str::FromStr; + use ui::prelude::*; -use ui::Breadcrumb; +use ui::{Breadcrumb, HighlightedText, Symbol}; use crate::story::Story; @@ -8,9 +11,35 @@ pub struct BreadcrumbStory {} impl BreadcrumbStory { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + Story::container(cx) .child(Story::title_for::<_, Breadcrumb>(cx)) .child(Story::label(cx, "Default")) - .child(Breadcrumb::new()) + .child(Breadcrumb::new( + PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(), + vec![ + Symbol(vec![ + HighlightedText { + text: "impl ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + HighlightedText { + text: "BreadcrumbStory".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + ]), + Symbol(vec![ + HighlightedText { + text: "fn ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + HighlightedText { + text: "render".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + ]), + ], + )) } } diff --git a/crates/storybook/src/stories/components/buffer.rs b/crates/storybook/src/stories/components/buffer.rs index 8d9e70a282..0b3268421b 100644 --- a/crates/storybook/src/stories/components/buffer.rs +++ b/crates/storybook/src/stories/components/buffer.rs @@ -12,8 +12,10 @@ pub struct BufferStory {} impl BufferStory { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + Story::container(cx) - .child(Story::title_for::<_, Buffer>(cx)) + .child(Story::title_for::<_, Buffer>(cx)) .child(Story::label(cx, "Default")) .child(div().w(rems(64.)).h_96().child(empty_buffer_example())) .child(Story::label(cx, "Hello World (Rust)")) @@ -21,14 +23,14 @@ impl BufferStory { div() .w(rems(64.)) .h_96() - .child(hello_world_rust_buffer_example(cx)), + .child(hello_world_rust_buffer_example(&theme)), ) .child(Story::label(cx, "Hello World (Rust) with Status")) .child( div() .w(rems(64.)) .h_96() - .child(hello_world_rust_buffer_with_status_example(cx)), + .child(hello_world_rust_buffer_with_status_example(&theme)), ) } } diff --git a/crates/storybook/src/stories/components/chat_panel.rs b/crates/storybook/src/stories/components/chat_panel.rs index 804290b7ca..e87ac0afa2 100644 --- a/crates/storybook/src/stories/components/chat_panel.rs +++ b/crates/storybook/src/stories/components/chat_panel.rs @@ -1,6 +1,6 @@ use chrono::DateTime; use ui::prelude::*; -use ui::{ChatMessage, ChatPanel}; +use ui::{ChatMessage, ChatPanel, Panel}; use crate::story::Story; @@ -12,23 +12,35 @@ impl ChatPanelStory { Story::container(cx) .child(Story::title_for::<_, ChatPanel>(cx)) .child(Story::label(cx, "Default")) - .child(ChatPanel::new(ScrollState::default())) + .child(Panel::new( + ScrollState::default(), + |_, _| vec![ChatPanel::new(ScrollState::default()).into_any()], + Box::new(()), + )) .child(Story::label(cx, "With Mesages")) - .child(ChatPanel::new(ScrollState::default()).with_messages(vec![ - ChatMessage::new( - "osiewicz".to_string(), - "is this thing on?".to_string(), - DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z") - .unwrap() - .naive_local(), - ), - ChatMessage::new( - "maxdeviant".to_string(), - "Reading you loud and clear!".to_string(), - DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z") - .unwrap() - .naive_local(), - ), - ])) + .child(Panel::new( + ScrollState::default(), + |_, _| { + vec![ChatPanel::new(ScrollState::default()) + .with_messages(vec![ + ChatMessage::new( + "osiewicz".to_string(), + "is this thing on?".to_string(), + DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z") + .unwrap() + .naive_local(), + ), + ChatMessage::new( + "maxdeviant".to_string(), + "Reading you loud and clear!".to_string(), + DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z") + .unwrap() + .naive_local(), + ), + ]) + .into_any()] + }, + Box::new(()), + )) } } diff --git a/crates/storybook/src/stories/components/facepile.rs b/crates/storybook/src/stories/components/facepile.rs index a32ffa3693..bbd08ae984 100644 --- a/crates/storybook/src/stories/components/facepile.rs +++ b/crates/storybook/src/stories/components/facepile.rs @@ -11,7 +11,7 @@ impl FacepileStory { let players = static_players(); Story::container(cx) - .child(Story::title_for::<_, ui::Facepile>(cx)) + .child(Story::title_for::<_, Facepile>(cx)) .child(Story::label(cx, "Default")) .child( div() diff --git a/crates/storybook/src/stories/components/panel.rs b/crates/storybook/src/stories/components/panel.rs index 38e7033d44..39a5ceafa2 100644 --- a/crates/storybook/src/stories/components/panel.rs +++ b/crates/storybook/src/stories/components/panel.rs @@ -14,9 +14,10 @@ impl PanelStory { .child(Panel::new( ScrollState::default(), |_, _| { - (0..100) - .map(|ix| Label::new(format!("Item {}", ix + 1)).into_any()) - .collect() + vec![div() + .overflow_y_scroll(ScrollState::default()) + .children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1)))) + .into_any()] }, Box::new(()), )) diff --git a/crates/storybook/src/stories/components/project_panel.rs b/crates/storybook/src/stories/components/project_panel.rs index ff4eb6099b..cba71cd21a 100644 --- a/crates/storybook/src/stories/components/project_panel.rs +++ b/crates/storybook/src/stories/components/project_panel.rs @@ -1,5 +1,5 @@ use ui::prelude::*; -use ui::ProjectPanel; +use ui::{Panel, ProjectPanel}; use crate::story::Story; @@ -11,6 +11,10 @@ impl ProjectPanelStory { Story::container(cx) .child(Story::title_for::<_, ProjectPanel>(cx)) .child(Story::label(cx, "Default")) - .child(ProjectPanel::new(ScrollState::default())) + .child(Panel::new( + ScrollState::default(), + |_, _| vec![ProjectPanel::new(ScrollState::default()).into_any()], + Box::new(()), + )) } } diff --git a/crates/storybook/src/stories/components/tab_bar.rs b/crates/storybook/src/stories/components/tab_bar.rs index 4c116caf7b..b5fa45dfd6 100644 --- a/crates/storybook/src/stories/components/tab_bar.rs +++ b/crates/storybook/src/stories/components/tab_bar.rs @@ -1,5 +1,5 @@ use ui::prelude::*; -use ui::TabBar; +use ui::{Tab, TabBar}; use crate::story::Story; @@ -11,6 +11,36 @@ impl TabBarStory { Story::container(cx) .child(Story::title_for::<_, TabBar>(cx)) .child(Story::label(cx, "Default")) - .child(TabBar::new(ScrollState::default())) + .child(TabBar::new(vec![ + Tab::new() + .title("Cargo.toml".to_string()) + .current(false) + .git_status(GitStatus::Modified), + Tab::new() + .title("Channels Panel".to_string()) + .current(false), + Tab::new() + .title("channels_panel.rs".to_string()) + .current(true) + .git_status(GitStatus::Modified), + Tab::new() + .title("workspace.rs".to_string()) + .current(false) + .git_status(GitStatus::Modified), + Tab::new() + .title("icon_button.rs".to_string()) + .current(false), + Tab::new() + .title("storybook.rs".to_string()) + .current(false) + .git_status(GitStatus::Created), + Tab::new().title("theme.rs".to_string()).current(false), + Tab::new() + .title("theme_registry.rs".to_string()) + .current(false), + Tab::new() + .title("styleable_helpers.rs".to_string()) + .current(false), + ])) } } diff --git a/crates/storybook/src/stories/components/toolbar.rs b/crates/storybook/src/stories/components/toolbar.rs index cfe3c97840..1413c463b4 100644 --- a/crates/storybook/src/stories/components/toolbar.rs +++ b/crates/storybook/src/stories/components/toolbar.rs @@ -1,5 +1,9 @@ +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; + use ui::prelude::*; -use ui::Toolbar; +use ui::{theme, Breadcrumb, HighlightColor, HighlightedText, Icon, IconButton, Symbol, Toolbar}; use crate::story::Story; @@ -8,9 +12,59 @@ pub struct ToolbarStory {} impl ToolbarStory { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + + struct LeftItemsPayload { + pub theme: Arc, + } + Story::container(cx) - .child(Story::title_for::<_, Toolbar>(cx)) + .child(Story::title_for::<_, Toolbar>(cx)) .child(Story::label(cx, "Default")) - .child(Toolbar::new()) + .child(Toolbar::new( + |_, payload| { + let payload = payload.downcast_ref::().unwrap(); + + let theme = payload.theme.clone(); + + vec![Breadcrumb::new( + PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(), + vec![ + Symbol(vec![ + HighlightedText { + text: "impl ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + HighlightedText { + text: "ToolbarStory".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + ]), + Symbol(vec![ + HighlightedText { + text: "fn ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + HighlightedText { + text: "render".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + ]), + ], + ) + .into_any()] + }, + Box::new(LeftItemsPayload { + theme: theme.clone(), + }), + |_, _| { + vec![ + IconButton::new(Icon::InlayHint).into_any(), + IconButton::new(Icon::MagnifyingGlass).into_any(), + IconButton::new(Icon::MagicWand).into_any(), + ] + }, + Box::new(()), + )) } } diff --git a/crates/storybook/src/stories/elements/avatar.rs b/crates/storybook/src/stories/elements/avatar.rs index d47c667f61..a277fa6a1e 100644 --- a/crates/storybook/src/stories/elements/avatar.rs +++ b/crates/storybook/src/stories/elements/avatar.rs @@ -9,7 +9,7 @@ pub struct AvatarStory {} impl AvatarStory { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { Story::container(cx) - .child(Story::title_for::<_, ui::Avatar>(cx)) + .child(Story::title_for::<_, Avatar>(cx)) .child(Story::label(cx, "Default")) .child(Avatar::new( "https://avatars.githubusercontent.com/u/1714999?v=4", diff --git a/crates/storybook/src/stories/elements/icon.rs b/crates/storybook/src/stories/elements/icon.rs index 21838bd839..66d3abc0b3 100644 --- a/crates/storybook/src/stories/elements/icon.rs +++ b/crates/storybook/src/stories/elements/icon.rs @@ -12,7 +12,7 @@ impl IconStory { let icons = Icon::iter(); Story::container(cx) - .child(Story::title_for::<_, ui::IconElement>(cx)) + .child(Story::title_for::<_, IconElement>(cx)) .child(Story::label(cx, "All Icons")) .child(div().flex().gap_3().children(icons.map(IconElement::new))) } diff --git a/crates/storybook/src/stories/kitchen_sink.rs b/crates/storybook/src/stories/kitchen_sink.rs index 3bb902b0db..ae826f934e 100644 --- a/crates/storybook/src/stories/kitchen_sink.rs +++ b/crates/storybook/src/stories/kitchen_sink.rs @@ -19,5 +19,8 @@ impl KitchenSinkStory { .child(div().flex().flex_col().children_any(element_stories)) .child(Story::label(cx, "Components")) .child(div().flex().flex_col().children_any(component_stories)) + // Add a bit of space at the bottom of the kitchen sink so elements + // don't end up squished right up against the bottom of the screen. + .child(div().p_4()) } } diff --git a/crates/storybook/src/storybook.rs b/crates/storybook/src/storybook.rs index f4a2f69704..afae0d5ebe 100644 --- a/crates/storybook/src/storybook.rs +++ b/crates/storybook/src/storybook.rs @@ -4,7 +4,7 @@ mod stories; mod story; mod story_selector; -use std::sync::Arc; +use std::{process::Command, sync::Arc}; use ::theme as legacy_theme; use clap::Parser; @@ -38,11 +38,44 @@ struct Args { theme: Option, } +async fn watch_zed_changes(fs: Arc) -> Option<()> { + if std::env::var("ZED_HOT_RELOAD").is_err() { + return None; + } + use futures::StreamExt; + let mut events = fs + .watch(".".as_ref(), std::time::Duration::from_millis(100)) + .await; + let mut current_child: Option = None; + while let Some(events) = events.next().await { + if !events.iter().any(|event| { + event + .path + .to_str() + .map(|path| path.contains("/crates/")) + .unwrap_or_default() + }) { + continue; + } + let child = current_child.take().map(|mut child| child.kill()); + log::info!("Storybook changed, rebuilding..."); + current_child = Some( + Command::new("cargo") + .args(["run", "-p", "storybook"]) + .spawn() + .ok()?, + ); + } + Some(()) +} + fn main() { SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); let args = Args::parse(); + let fs = Arc::new(fs::RealFs); + gpui2::App::new(Assets).unwrap().run(move |cx| { let mut store = SettingsStore::default(); store @@ -63,6 +96,10 @@ fn main() { }) .and_then(|theme_name| theme_registry.get(&theme_name).ok()); + cx.spawn(|_| async move { + watch_zed_changes(fs).await; + }) + .detach(); cx.add_window( gpui2::WindowOptions { bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(1700., 980.))), diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml index 821e93a340..7bd9d912a0 100644 --- a/crates/ui/Cargo.toml +++ b/crates/ui/Cargo.toml @@ -13,3 +13,4 @@ settings = { path = "../settings" } smallvec.workspace = true strum = { version = "0.25.0", features = ["derive"] } theme = { path = "../theme" } +rand = "0.8" diff --git a/crates/ui/src/components.rs b/crates/ui/src/components.rs index f96964bd27..0af13040f7 100644 --- a/crates/ui/src/components.rs +++ b/crates/ui/src/components.rs @@ -5,7 +5,7 @@ mod chat_panel; mod collab_panel; mod command_palette; mod context_menu; -mod editor; +mod editor_pane; mod facepile; mod icon_button; mod keybinding; @@ -31,7 +31,7 @@ pub use chat_panel::*; pub use collab_panel::*; pub use command_palette::*; pub use context_menu::*; -pub use editor::*; +pub use editor_pane::*; pub use facepile::*; pub use icon_button::*; pub use keybinding::*; diff --git a/crates/ui/src/components/breadcrumb.rs b/crates/ui/src/components/breadcrumb.rs index 30b40011a5..c14e89ee7b 100644 --- a/crates/ui/src/components/breadcrumb.rs +++ b/crates/ui/src/components/breadcrumb.rs @@ -1,17 +1,35 @@ -use crate::prelude::*; +use std::path::PathBuf; + +use gpui2::elements::div::Div; + use crate::{h_stack, theme}; +use crate::{prelude::*, HighlightedText}; + +#[derive(Clone)] +pub struct Symbol(pub Vec); #[derive(Element)] -pub struct Breadcrumb {} +pub struct Breadcrumb { + path: PathBuf, + symbols: Vec, +} impl Breadcrumb { - pub fn new() -> Self { - Self {} + pub fn new(path: PathBuf, symbols: Vec) -> Self { + Self { path, symbols } + } + + fn render_separator(&self, theme: &Theme) -> Div { + div() + .child(" › ") + .text_color(HighlightColor::Default.hsla(theme)) } fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); + let symbols_len = self.symbols.len(); + h_stack() .px_1() // TODO: Read font from theme (or settings?). @@ -21,11 +39,33 @@ impl Breadcrumb { .rounded_md() .hover() .fill(theme.highest.base.hovered.background) - // TODO: Replace hardcoded breadcrumbs. - .child("crates/ui/src/components/toolbar.rs") - .child(" › ") - .child("impl Breadcrumb") - .child(" › ") - .child("fn render") + .child(self.path.clone().to_str().unwrap().to_string()) + .child(if !self.symbols.is_empty() { + self.render_separator(&theme) + } else { + div() + }) + .child( + div().flex().children( + self.symbols + .iter() + .enumerate() + // TODO: Could use something like `intersperse` here instead. + .flat_map(|(ix, symbol)| { + let mut items = + vec![div().flex().children(symbol.0.iter().map(|segment| { + div().child(segment.text.clone()).text_color(segment.color) + }))]; + + let is_last_segment = ix == symbols_len - 1; + if !is_last_segment { + items.push(self.render_separator(&theme)); + } + + items + }) + .collect::>(), + ), + ) } } diff --git a/crates/ui/src/components/buffer.rs b/crates/ui/src/components/buffer.rs index 88c5a59563..00e5daee55 100644 --- a/crates/ui/src/components/buffer.rs +++ b/crates/ui/src/components/buffer.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use gpui2::{Hsla, WindowContext}; use crate::prelude::*; @@ -33,6 +31,7 @@ pub struct BufferRow { pub show_line_number: bool, } +#[derive(Clone)] pub struct BufferRows { pub show_line_numbers: bool, pub rows: Vec, @@ -108,9 +107,8 @@ impl BufferRow { } } -#[derive(Element)] -pub struct Buffer { - view_type: PhantomData, +#[derive(Element, Clone)] +pub struct Buffer { scroll_state: ScrollState, rows: Option, readonly: bool, @@ -119,10 +117,9 @@ pub struct Buffer { path: Option, } -impl Buffer { +impl Buffer { pub fn new() -> Self { Self { - view_type: PhantomData, scroll_state: ScrollState::default(), rows: Some(BufferRows::default()), readonly: false, @@ -161,7 +158,7 @@ impl Buffer { self } - fn render_row(row: BufferRow, cx: &WindowContext) -> impl IntoElement { + fn render_row(row: BufferRow, cx: &WindowContext) -> impl IntoElement { let theme = theme(cx); let system_color = SystemColor::new(); @@ -172,28 +169,35 @@ impl Buffer { }; let line_number_color = if row.current { - HighlightColor::Default.hsla(cx) + HighlightColor::Default.hsla(&theme) } else { - HighlightColor::Comment.hsla(cx) + HighlightColor::Comment.hsla(&theme) }; h_stack() .fill(line_background) + .w_full() .gap_2() - .px_2() - .child(h_stack().w_4().h_full().px_1().when(row.code_action, |c| { - div().child(IconElement::new(Icon::Bolt)) - })) + .px_1() + .child( + h_stack() + .w_4() + .h_full() + .px_0p5() + .when(row.code_action, |c| { + div().child(IconElement::new(Icon::Bolt)) + }), + ) .when(row.show_line_number, |this| { this.child( - h_stack().justify_end().px_1().w_4().child( + h_stack().justify_end().px_0p5().w_3().child( div() .text_color(line_number_color) .child(row.line_number.to_string()), ), ) }) - .child(div().mx_1().w_1().h_full().fill(row.status.hsla(cx))) + .child(div().mx_0p5().w_1().h_full().fill(row.status.hsla(cx))) .children(row.line.map(|line| { div() .flex() @@ -205,7 +209,7 @@ impl Buffer { })) } - fn render_rows(&self, cx: &WindowContext) -> Vec> { + fn render_rows(&self, cx: &WindowContext) -> Vec> { match &self.rows { Some(rows) => rows .rows @@ -216,7 +220,7 @@ impl Buffer { } } - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); let rows = self.render_rows(cx); v_stack() diff --git a/crates/ui/src/components/chat_panel.rs b/crates/ui/src/components/chat_panel.rs index e5a2d6a556..5ae66967b6 100644 --- a/crates/ui/src/components/chat_panel.rs +++ b/crates/ui/src/components/chat_panel.rs @@ -4,13 +4,12 @@ use chrono::NaiveDateTime; use crate::prelude::*; use crate::theme::theme; -use crate::{Icon, IconButton, Input, Label, LabelColor, Panel, PanelSide}; +use crate::{Icon, IconButton, Input, Label, LabelColor}; #[derive(Element)] pub struct ChatPanel { view_type: PhantomData, scroll_state: ScrollState, - current_side: PanelSide, messages: Vec, } @@ -19,16 +18,10 @@ impl ChatPanel { Self { view_type: PhantomData, scroll_state, - current_side: PanelSide::default(), messages: Vec::new(), } } - pub fn side(mut self, side: PanelSide) -> Self { - self.current_side = side; - self - } - pub fn with_messages(mut self, messages: Vec) -> Self { self.messages = messages; self @@ -37,38 +30,33 @@ impl ChatPanel { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); - struct PanelPayload { - pub scroll_state: ScrollState, - pub messages: Vec, - } - - Panel::new( - self.scroll_state.clone(), - |_, payload| { - let payload = payload.downcast_ref::().unwrap(); - - vec![div() + div() + .flex() + .flex_col() + .justify_between() + .h_full() + .px_2() + .gap_2() + // Header + .child( + div() .flex() - .flex_col() - .h_full() - .px_2() - .gap_2() - // Header + .justify_between() + .py_2() + .child(div().flex().child(Label::new("#design"))) .child( div() .flex() - .justify_between() - .gap_2() - .child(div().flex().child(Label::new("#design"))) - .child( - div() - .flex() - .items_center() - .gap_px() - .child(IconButton::new(Icon::File)) - .child(IconButton::new(Icon::AudioOn)), - ), - ) + .items_center() + .gap_px() + .child(IconButton::new(Icon::File)) + .child(IconButton::new(Icon::AudioOn)), + ), + ) + .child( + div() + .flex() + .flex_col() // Chat Body .child( div() @@ -76,19 +64,12 @@ impl ChatPanel { .flex() .flex_col() .gap_3() - .overflow_y_scroll(payload.scroll_state.clone()) - .children(payload.messages.clone()), + .overflow_y_scroll(self.scroll_state.clone()) + .children(self.messages.clone()), ) // Composer - .child(div().flex().gap_2().child(Input::new("Message #design"))) - .into_any()] - }, - Box::new(PanelPayload { - scroll_state: self.scroll_state.clone(), - messages: self.messages.clone(), - }), - ) - .side(self.current_side) + .child(div().flex().my_2().child(Input::new("Message #design"))), + ) } } diff --git a/crates/ui/src/components/editor.rs b/crates/ui/src/components/editor.rs deleted file mode 100644 index 105ed86c40..0000000000 --- a/crates/ui/src/components/editor.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::marker::PhantomData; - -use crate::prelude::*; -use crate::{Buffer, Toolbar}; - -#[derive(Element)] -struct Editor { - view_type: PhantomData, - toolbar: Toolbar, - buffer: Buffer, -} - -impl Editor { - pub fn new(toolbar: Toolbar, buffer: Buffer) -> Self { - Self { - view_type: PhantomData, - toolbar, - buffer, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - div().child(self.toolbar.clone()) - } -} diff --git a/crates/ui/src/components/editor_pane.rs b/crates/ui/src/components/editor_pane.rs new file mode 100644 index 0000000000..561081164c --- /dev/null +++ b/crates/ui/src/components/editor_pane.rs @@ -0,0 +1,60 @@ +use std::marker::PhantomData; +use std::path::PathBuf; + +use crate::prelude::*; +use crate::{v_stack, Breadcrumb, Buffer, Icon, IconButton, Symbol, Tab, TabBar, Toolbar}; + +pub struct Editor { + pub tabs: Vec, + pub path: PathBuf, + pub symbols: Vec, + pub buffer: Buffer, +} + +#[derive(Element)] +pub struct EditorPane { + view_type: PhantomData, + editor: Editor, +} + +impl EditorPane { + pub fn new(editor: Editor) -> Self { + Self { + view_type: PhantomData, + editor, + } + } + + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + struct LeftItemsPayload { + path: PathBuf, + symbols: Vec, + } + + v_stack() + .w_full() + .h_full() + .flex_1() + .child(TabBar::new(self.editor.tabs.clone())) + .child(Toolbar::new( + |_, payload| { + let payload = payload.downcast_ref::().unwrap(); + + vec![Breadcrumb::new(payload.path.clone(), payload.symbols.clone()).into_any()] + }, + Box::new(LeftItemsPayload { + path: self.editor.path.clone(), + symbols: self.editor.symbols.clone(), + }), + |_, _| { + vec![ + IconButton::new(Icon::InlayHint).into_any(), + IconButton::new(Icon::MagnifyingGlass).into_any(), + IconButton::new(Icon::MagicWand).into_any(), + ] + }, + Box::new(()), + )) + .child(self.editor.buffer.clone()) + } +} diff --git a/crates/ui/src/components/panel.rs b/crates/ui/src/components/panel.rs index 9d64945cc1..cbcf502670 100644 --- a/crates/ui/src/components/panel.rs +++ b/crates/ui/src/components/panel.rs @@ -105,16 +105,12 @@ impl Panel { let theme = theme(cx); let panel_base; - let current_width = if let Some(width) = self.width { - width - } else { - self.initial_width - }; + let current_width = self.width.unwrap_or(self.initial_width); match self.current_side { PanelSide::Left => { panel_base = v_stack() - .overflow_y_scroll(self.scroll_state.clone()) + .flex_initial() .h_full() .w(current_width) .fill(theme.middle.base.default.background) @@ -123,20 +119,20 @@ impl Panel { } PanelSide::Right => { panel_base = v_stack() - .overflow_y_scroll(self.scroll_state.clone()) + .flex_initial() .h_full() .w(current_width) .fill(theme.middle.base.default.background) - .border_r() + .border_l() .border_color(theme.middle.base.default.border); } PanelSide::Bottom => { panel_base = v_stack() - .overflow_y_scroll(self.scroll_state.clone()) + .flex_initial() .w_full() .h(current_width) .fill(theme.middle.base.default.background) - .border_r() + .border_t() .border_color(theme.middle.base.default.border); } } diff --git a/crates/ui/src/components/player_stack.rs b/crates/ui/src/components/player_stack.rs index 4c00aaf2cf..7df6f065fb 100644 --- a/crates/ui/src/components/player_stack.rs +++ b/crates/ui/src/components/player_stack.rs @@ -38,9 +38,8 @@ impl PlayerStack { div().flex().justify_center().w_full().child( div() .w_4() - .h_1() - .rounded_bl_sm() - .rounded_br_sm() + .h_0p5() + .rounded_sm() .fill(player.cursor_color(cx)), ), ) @@ -50,7 +49,7 @@ impl PlayerStack { .items_center() .justify_center() .h_6() - .px_1() + .pl_1() .rounded_lg() .fill(if followers.is_none() { system_color.transparent @@ -59,7 +58,7 @@ impl PlayerStack { }) .child(Avatar::new(player.avatar_src().to_string())) .children(followers.map(|followers| { - div().neg_mr_1().child(Facepile::new(followers.into_iter())) + div().neg_ml_2().child(Facepile::new(followers.into_iter())) })), ) } diff --git a/crates/ui/src/components/project_panel.rs b/crates/ui/src/components/project_panel.rs index cf6f080b1c..1f32c698e5 100644 --- a/crates/ui/src/components/project_panel.rs +++ b/crates/ui/src/components/project_panel.rs @@ -1,17 +1,15 @@ use std::marker::PhantomData; -use std::sync::Arc; use crate::prelude::*; use crate::{ static_project_panel_project_items, static_project_panel_single_items, theme, Input, List, - ListHeader, Panel, PanelSide, Theme, + ListHeader, }; #[derive(Element)] pub struct ProjectPanel { view_type: PhantomData, scroll_state: ScrollState, - current_side: PanelSide, } impl ProjectPanel { @@ -19,69 +17,42 @@ impl ProjectPanel { Self { view_type: PhantomData, scroll_state, - current_side: PanelSide::default(), } } - pub fn side(mut self, side: PanelSide) -> Self { - self.current_side = side; - self - } - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - struct PanelPayload { - pub theme: Arc, - pub scroll_state: ScrollState, - } + let theme = theme(cx); - Panel::new( - self.scroll_state.clone(), - |_, payload| { - let payload = payload.downcast_ref::().unwrap(); - - let theme = payload.theme.clone(); - - vec![div() + div() + .flex() + .flex_col() + .w_full() + .h_full() + .px_2() + .fill(theme.middle.base.default.background) + .child( + div() + .w_56() .flex() .flex_col() - .w_56() - .h_full() - .px_2() - .fill(theme.middle.base.default.background) + .overflow_y_scroll(ScrollState::default()) .child( - div() - .w_56() - .flex() - .flex_col() - .overflow_y_scroll(payload.scroll_state.clone()) - .child( - List::new(static_project_panel_single_items()) - .header( - ListHeader::new("FILES").set_toggle(ToggleState::Toggled), - ) - .empty_message("No files in directory") - .set_toggle(ToggleState::Toggled), - ) - .child( - List::new(static_project_panel_project_items()) - .header( - ListHeader::new("PROJECT").set_toggle(ToggleState::Toggled), - ) - .empty_message("No folders in directory") - .set_toggle(ToggleState::Toggled), - ), + List::new(static_project_panel_single_items()) + .header(ListHeader::new("FILES").set_toggle(ToggleState::Toggled)) + .empty_message("No files in directory") + .set_toggle(ToggleState::Toggled), ) .child( - Input::new("Find something...") - .value("buffe".to_string()) - .state(InteractionState::Focused), - ) - .into_any()] - }, - Box::new(PanelPayload { - theme: theme(cx), - scroll_state: self.scroll_state.clone(), - }), - ) + List::new(static_project_panel_project_items()) + .header(ListHeader::new("PROJECT").set_toggle(ToggleState::Toggled)) + .empty_message("No folders in directory") + .set_toggle(ToggleState::Toggled), + ), + ) + .child( + Input::new("Find something...") + .value("buffe".to_string()) + .state(InteractionState::Focused), + ) } } diff --git a/crates/ui/src/components/tab.rs b/crates/ui/src/components/tab.rs index 9c034d2535..9eb1122775 100644 --- a/crates/ui/src/components/tab.rs +++ b/crates/ui/src/components/tab.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use crate::{theme, Icon, IconColor, IconElement, Label, LabelColor}; -#[derive(Element)] +#[derive(Element, Clone)] pub struct Tab { title: String, icon: Option, diff --git a/crates/ui/src/components/tab_bar.rs b/crates/ui/src/components/tab_bar.rs index 43fef77e2c..8addcb87b1 100644 --- a/crates/ui/src/components/tab_bar.rs +++ b/crates/ui/src/components/tab_bar.rs @@ -7,20 +7,27 @@ use crate::{theme, Icon, IconButton, Tab}; pub struct TabBar { view_type: PhantomData, scroll_state: ScrollState, + tabs: Vec, } impl TabBar { - pub fn new(scroll_state: ScrollState) -> Self { + pub fn new(tabs: Vec) -> Self { Self { view_type: PhantomData, - scroll_state, + scroll_state: ScrollState::default(), + tabs, } } + pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) { + self.scroll_state = scroll_state; + } + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); let can_navigate_back = true; let can_navigate_forward = false; + div() .w_full() .flex() @@ -54,51 +61,7 @@ impl TabBar { div() .flex() .overflow_x_scroll(self.scroll_state.clone()) - .child( - Tab::new() - .title("Cargo.toml".to_string()) - .current(false) - .git_status(GitStatus::Modified), - ) - .child( - Tab::new() - .title("Channels Panel".to_string()) - .current(false), - ) - .child( - Tab::new() - .title("channels_panel.rs".to_string()) - .current(true) - .git_status(GitStatus::Modified), - ) - .child( - Tab::new() - .title("workspace.rs".to_string()) - .current(false) - .git_status(GitStatus::Modified), - ) - .child( - Tab::new() - .title("icon_button.rs".to_string()) - .current(false), - ) - .child( - Tab::new() - .title("storybook.rs".to_string()) - .current(false) - .git_status(GitStatus::Created), - ) - .child(Tab::new().title("theme.rs".to_string()).current(false)) - .child( - Tab::new() - .title("theme_registry.rs".to_string()) - .current(false), - ) - .child( - Tab::new() - .title("styleable_helpers.rs".to_string()) - .current(false), - ), + .children(self.tabs.clone()), ), ) // Right Side diff --git a/crates/ui/src/components/terminal.rs b/crates/ui/src/components/terminal.rs index f5c3a42a64..909cb886ce 100644 --- a/crates/ui/src/components/terminal.rs +++ b/crates/ui/src/components/terminal.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use gpui2::geometry::{relative, rems, Size}; use crate::prelude::*; @@ -20,6 +22,7 @@ impl Terminal { div() .flex() .flex_col() + .w_full() .child( // Terminal Tabs. div() @@ -70,8 +73,12 @@ impl Terminal { width: relative(1.).into(), height: rems(36.).into(), }, - |_, _| vec![], - Box::new(()), + |_, payload| { + let theme = payload.downcast_ref::>().unwrap(); + + vec![crate::static_data::terminal_buffer(&theme).into_any()] + }, + Box::new(theme), )) } } diff --git a/crates/ui/src/components/title_bar.rs b/crates/ui/src/components/title_bar.rs index 196b896396..dd3d6c1fba 100644 --- a/crates/ui/src/components/title_bar.rs +++ b/crates/ui/src/components/title_bar.rs @@ -2,16 +2,24 @@ use std::marker::PhantomData; use std::sync::atomic::AtomicBool; use std::sync::Arc; -use crate::prelude::*; +use crate::{prelude::*, PlayerWithCallStatus}; use crate::{ - static_players_with_call_status, theme, Avatar, Button, Icon, IconButton, IconColor, - PlayerStack, ToolDivider, TrafficLights, + theme, Avatar, Button, Icon, IconButton, IconColor, PlayerStack, ToolDivider, TrafficLights, }; +#[derive(Clone)] +pub struct Livestream { + pub players: Vec, + pub channel: Option, // projects + // windows +} + #[derive(Element)] pub struct TitleBar { view_type: PhantomData, + /// If the window is active from the OS's perspective. is_active: Arc, + livestream: Option, } impl TitleBar { @@ -28,14 +36,24 @@ impl TitleBar { Self { view_type: PhantomData, is_active, + livestream: None, } } + pub fn set_livestream(mut self, livestream: Option) -> Self { + self.livestream = livestream; + self + } + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); let has_focus = cx.window_is_active(); - let player_list = static_players_with_call_status().into_iter(); + let player_list = if let Some(livestream) = &self.livestream { + livestream.players.clone().into_iter() + } else { + vec![].into_iter() + }; div() .flex() @@ -61,7 +79,8 @@ impl TitleBar { .child(Button::new("zed")) .child(Button::new("nate/gpui2-ui-components")), ) - .children(player_list.map(|p| PlayerStack::new(p))), + .children(player_list.map(|p| PlayerStack::new(p))) + .child(IconButton::new(Icon::Plus)), ) .child( div() diff --git a/crates/ui/src/components/toolbar.rs b/crates/ui/src/components/toolbar.rs index aedd634743..e0953bf3b2 100644 --- a/crates/ui/src/components/toolbar.rs +++ b/crates/ui/src/components/toolbar.rs @@ -1,33 +1,49 @@ use crate::prelude::*; -use crate::{theme, Breadcrumb, Icon, IconButton}; +use crate::theme; #[derive(Clone)] pub struct ToolbarItem {} -#[derive(Element, Clone)] -pub struct Toolbar { - items: Vec, +#[derive(Element)] +pub struct Toolbar { + left_items: HackyChildren, + left_items_payload: HackyChildrenPayload, + right_items: HackyChildren, + right_items_payload: HackyChildrenPayload, } -impl Toolbar { - pub fn new() -> Self { - Self { items: Vec::new() } +impl Toolbar { + pub fn new( + left_items: HackyChildren, + left_items_payload: HackyChildrenPayload, + right_items: HackyChildren, + right_items_payload: HackyChildrenPayload, + ) -> Self { + Self { + left_items, + left_items_payload, + right_items, + right_items_payload, + } } - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); div() + .fill(theme.highest.base.default.background) .p_2() .flex() .justify_between() - .child(Breadcrumb::new()) .child( div() .flex() - .child(IconButton::new(Icon::InlayHint)) - .child(IconButton::new(Icon::MagnifyingGlass)) - .child(IconButton::new(Icon::MagicWand)), + .children_any((self.left_items)(cx, self.left_items_payload.as_ref())), + ) + .child( + div() + .flex() + .children_any((self.right_items)(cx, self.right_items_payload.as_ref())), ) } } diff --git a/crates/ui/src/components/workspace.rs b/crates/ui/src/components/workspace.rs index 0c6331dc9b..b609546f7f 100644 --- a/crates/ui/src/components/workspace.rs +++ b/crates/ui/src/components/workspace.rs @@ -1,10 +1,15 @@ +use std::sync::Arc; + use chrono::DateTime; use gpui2::geometry::{relative, rems, Size}; -use crate::prelude::*; use crate::{ - theme, v_stack, ChatMessage, ChatPanel, Pane, PaneGroup, Panel, PanelAllowedSides, PanelSide, - ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar, + hello_world_rust_editor_with_status_example, prelude::*, random_players_with_call_status, + Livestream, +}; +use crate::{ + theme, v_stack, ChatMessage, ChatPanel, EditorPane, Pane, PaneGroup, Panel, PanelAllowedSides, + PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar, }; #[derive(Element, Default)] @@ -17,6 +22,8 @@ pub struct WorkspaceElement { impl WorkspaceElement { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx).clone(); + let temp_size = rems(36.).into(); let root_group = PaneGroup::new_groups( @@ -29,8 +36,15 @@ impl WorkspaceElement { width: relative(1.).into(), height: temp_size, }, - |_, _| vec![Terminal::new().into_any()], - Box::new(()), + |_, payload| { + let theme = payload.downcast_ref::>().unwrap(); + + vec![EditorPane::new(hello_world_rust_editor_with_status_example( + &theme, + )) + .into_any()] + }, + Box::new(theme.clone()), ), Pane::new( ScrollState::default(), @@ -51,8 +65,15 @@ impl WorkspaceElement { width: relative(1.).into(), height: relative(1.).into(), }, - |_, _| vec![Terminal::new().into_any()], - Box::new(()), + |_, payload| { + let theme = payload.downcast_ref::>().unwrap(); + + vec![EditorPane::new(hello_world_rust_editor_with_status_example( + &theme, + )) + .into_any()] + }, + Box::new(theme.clone()), )], SplitDirection::Vertical, ), @@ -60,8 +81,6 @@ impl WorkspaceElement { SplitDirection::Horizontal, ); - let theme = theme(cx).clone(); - div() .size_full() .flex() @@ -72,7 +91,10 @@ impl WorkspaceElement { .items_start() .text_color(theme.lowest.base.default.foreground) .fill(theme.lowest.base.default.background) - .child(TitleBar::new(cx)) + .child(TitleBar::new(cx).set_livestream(Some(Livestream { + players: random_players_with_call_status(7), + channel: Some("gpui2-ui".to_string()), + }))) .child( div() .flex_1() @@ -84,8 +106,12 @@ impl WorkspaceElement { .border_b() .border_color(theme.lowest.base.default.border) .child( - ProjectPanel::new(self.left_panel_scroll_state.clone()) - .side(PanelSide::Left), + Panel::new( + self.left_panel_scroll_state.clone(), + |_, payload| vec![ProjectPanel::new(ScrollState::default()).into_any()], + Box::new(()), + ) + .side(PanelSide::Left), ) .child( v_stack() @@ -110,26 +136,37 @@ impl WorkspaceElement { .side(PanelSide::Bottom), ), ) - .child(ChatPanel::new(ScrollState::default()).with_messages(vec![ - ChatMessage::new( - "osiewicz".to_string(), - "is this thing on?".to_string(), - DateTime::parse_from_rfc3339( - "2023-09-27T15:40:52.707Z", - ) - .unwrap() - .naive_local(), - ), - ChatMessage::new( - "maxdeviant".to_string(), - "Reading you loud and clear!".to_string(), - DateTime::parse_from_rfc3339( - "2023-09-28T15:40:52.707Z", - ) - .unwrap() - .naive_local(), - ), - ])), + .child( + Panel::new( + self.right_panel_scroll_state.clone(), + |_, payload| { + vec![ChatPanel::new(ScrollState::default()) + .with_messages(vec![ + ChatMessage::new( + "osiewicz".to_string(), + "is this thing on?".to_string(), + DateTime::parse_from_rfc3339( + "2023-09-27T15:40:52.707Z", + ) + .unwrap() + .naive_local(), + ), + ChatMessage::new( + "maxdeviant".to_string(), + "Reading you loud and clear!".to_string(), + DateTime::parse_from_rfc3339( + "2023-09-28T15:40:52.707Z", + ) + .unwrap() + .naive_local(), + ), + ]) + .into_any()] + }, + Box::new(()), + ) + .side(PanelSide::Right), + ), ) .child(StatusBar::new()) } diff --git a/crates/ui/src/elements/icon.rs b/crates/ui/src/elements/icon.rs index ca357b4f02..6d4053a4ae 100644 --- a/crates/ui/src/elements/icon.rs +++ b/crates/ui/src/elements/icon.rs @@ -84,6 +84,7 @@ pub enum Icon { Plus, Quote, Screen, + SelectAll, Split, SplitMessage, Terminal, @@ -131,6 +132,7 @@ impl Icon { Icon::Plus => "icons/plus.svg", Icon::Quote => "icons/quote.svg", Icon::Screen => "icons/desktop.svg", + Icon::SelectAll => "icons/select-all.svg", Icon::Split => "icons/split.svg", Icon::SplitMessage => "icons/split_message.svg", Icon::Terminal => "icons/terminal.svg", diff --git a/crates/ui/src/elements/input.rs b/crates/ui/src/elements/input.rs index 1a860028d2..fd860f30c2 100644 --- a/crates/ui/src/elements/input.rs +++ b/crates/ui/src/elements/input.rs @@ -81,6 +81,7 @@ impl Input { div() .h_7() + .w_full() .px_2() .border() .border_color(border_color_default) diff --git a/crates/ui/src/elements/player.rs b/crates/ui/src/elements/player.rs index e9e269a2cb..465542dc7f 100644 --- a/crates/ui/src/elements/player.rs +++ b/crates/ui/src/elements/player.rs @@ -65,7 +65,7 @@ impl PlayerCallStatus { } } -#[derive(Clone)] +#[derive(PartialEq, Clone)] pub struct Player { index: usize, avatar_src: String, @@ -73,6 +73,7 @@ pub struct Player { status: PlayerStatus, } +#[derive(Clone)] pub struct PlayerWithCallStatus { player: Player, call_status: PlayerCallStatus, diff --git a/crates/ui/src/prelude.rs b/crates/ui/src/prelude.rs index c3cecbfc61..b19b2becd1 100644 --- a/crates/ui/src/prelude.rs +++ b/crates/ui/src/prelude.rs @@ -2,7 +2,7 @@ pub use gpui2::elements::div::{div, ScrollState}; pub use gpui2::style::{StyleHelpers, Styleable}; pub use gpui2::{Element, IntoElement, ParentElement, ViewContext}; -pub use crate::{theme, ButtonVariant, HackyChildren, HackyChildrenPayload, InputVariant}; +pub use crate::{theme, ButtonVariant, HackyChildren, HackyChildrenPayload, InputVariant, Theme}; use gpui2::{hsla, rgb, Hsla, WindowContext}; use strum::EnumIter; @@ -40,8 +40,7 @@ pub enum HighlightColor { } impl HighlightColor { - pub fn hsla(&self, cx: &WindowContext) -> Hsla { - let theme = theme(cx); + pub fn hsla(&self, theme: &Theme) -> Hsla { let system_color = SystemColor::new(); match self { @@ -74,7 +73,7 @@ impl HighlightColor { } } -#[derive(Default, PartialEq, EnumIter)] +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] pub enum FileSystemStatus { #[default] None, @@ -92,7 +91,7 @@ impl FileSystemStatus { } } -#[derive(Default, PartialEq, EnumIter, Clone, Copy)] +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] pub enum GitStatus { #[default] None, @@ -130,7 +129,7 @@ impl GitStatus { } } -#[derive(Default, PartialEq)] +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] pub enum DiagnosticStatus { #[default] None, @@ -139,14 +138,14 @@ pub enum DiagnosticStatus { Info, } -#[derive(Default, PartialEq)] +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] pub enum IconSide { #[default] Left, Right, } -#[derive(Default, PartialEq)] +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] pub enum OrderMethod { #[default] Ascending, @@ -154,14 +153,14 @@ pub enum OrderMethod { MostRecent, } -#[derive(Default, PartialEq, Clone, Copy)] +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] pub enum Shape { #[default] Circle, RoundedRectangle, } -#[derive(Default, PartialEq, Clone, Copy)] +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] pub enum DisclosureControlVisibility { #[default] OnHover, diff --git a/crates/ui/src/static_data.rs b/crates/ui/src/static_data.rs index fed2d40a73..b8c4e18f14 100644 --- a/crates/ui/src/static_data.rs +++ b/crates/ui/src/static_data.rs @@ -1,12 +1,109 @@ -use gpui2::WindowContext; +use std::path::PathBuf; +use std::str::FromStr; + +use rand::Rng; use crate::{ - Buffer, BufferRow, BufferRows, GitStatus, HighlightColor, HighlightedLine, HighlightedText, - Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListItem, MicStatus, - ModifierKeys, PaletteItem, Player, PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, - ToggleState, + Buffer, BufferRow, BufferRows, Editor, FileSystemStatus, GitStatus, HighlightColor, + HighlightedLine, HighlightedText, Icon, Keybinding, Label, LabelColor, ListEntry, + ListEntrySize, ListItem, Livestream, MicStatus, ModifierKeys, PaletteItem, Player, + PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, Theme, ToggleState, + VideoStatus, }; +pub fn static_tabs_example() -> Vec { + vec![ + Tab::new() + .title("wip.rs".to_string()) + .icon(Icon::FileRust) + .current(false) + .fs_status(FileSystemStatus::Deleted), + Tab::new() + .title("Cargo.toml".to_string()) + .icon(Icon::FileToml) + .current(false) + .git_status(GitStatus::Modified), + Tab::new() + .title("Channels Panel".to_string()) + .icon(Icon::Hash) + .current(false), + Tab::new() + .title("channels_panel.rs".to_string()) + .icon(Icon::FileRust) + .current(true) + .git_status(GitStatus::Modified), + Tab::new() + .title("workspace.rs".to_string()) + .current(false) + .icon(Icon::FileRust) + .git_status(GitStatus::Modified), + Tab::new() + .title("icon_button.rs".to_string()) + .icon(Icon::FileRust) + .current(false), + Tab::new() + .title("storybook.rs".to_string()) + .icon(Icon::FileRust) + .current(false) + .git_status(GitStatus::Created), + Tab::new() + .title("theme.rs".to_string()) + .icon(Icon::FileRust) + .current(false), + Tab::new() + .title("theme_registry.rs".to_string()) + .icon(Icon::FileRust) + .current(false), + Tab::new() + .title("styleable_helpers.rs".to_string()) + .icon(Icon::FileRust) + .current(false), + ] +} + +pub fn static_tabs_1() -> Vec { + vec![ + Tab::new() + .title("project_panel.rs".to_string()) + .icon(Icon::FileRust) + .current(false) + .fs_status(FileSystemStatus::Deleted), + Tab::new() + .title("tab_bar.rs".to_string()) + .icon(Icon::FileRust) + .current(false) + .git_status(GitStatus::Modified), + Tab::new() + .title("workspace.rs".to_string()) + .icon(Icon::FileRust) + .current(false), + Tab::new() + .title("tab.rs".to_string()) + .icon(Icon::FileRust) + .current(true) + .git_status(GitStatus::Modified), + ] +} + +pub fn static_tabs_2() -> Vec { + vec![ + Tab::new() + .title("tab_bar.rs".to_string()) + .icon(Icon::FileRust) + .current(false) + .fs_status(FileSystemStatus::Deleted), + Tab::new() + .title("static_data.rs".to_string()) + .icon(Icon::FileRust) + .current(true) + .git_status(GitStatus::Modified), + ] +} + +pub fn static_tabs_3() -> Vec { + vec![Tab::new().git_status(GitStatus::Created).current(true)] +} + pub fn static_players() -> Vec { vec![ Player::new( @@ -37,6 +134,154 @@ pub fn static_players() -> Vec { ] } +#[derive(Debug)] +pub struct PlayerData { + pub url: String, + pub name: String, +} +pub fn static_player_data() -> Vec { + vec![ + PlayerData { + url: "https://avatars.githubusercontent.com/u/1714999?v=4".into(), + name: "iamnbutler".into(), + }, + PlayerData { + url: "https://avatars.githubusercontent.com/u/326587?v=4".into(), + name: "maxbrunsfeld".into(), + }, + PlayerData { + url: "https://avatars.githubusercontent.com/u/482957?v=4".into(), + name: "as-cii".into(), + }, + PlayerData { + url: "https://avatars.githubusercontent.com/u/1789?v=4".into(), + name: "nathansobo".into(), + }, + PlayerData { + url: "https://avatars.githubusercontent.com/u/1486634?v=4".into(), + name: "ForLoveOfCats".into(), + }, + PlayerData { + url: "https://avatars.githubusercontent.com/u/2690773?v=4".into(), + name: "SomeoneToIgnore".into(), + }, + PlayerData { + url: "https://avatars.githubusercontent.com/u/19867440?v=4".into(), + name: "JosephTLyons".into(), + }, + PlayerData { + url: "https://avatars.githubusercontent.com/u/24362066?v=4".into(), + name: "osiewicz".into(), + }, + PlayerData { + url: "https://avatars.githubusercontent.com/u/22121886?v=4".into(), + name: "KCaverly".into(), + }, + PlayerData { + url: "https://avatars.githubusercontent.com/u/1486634?v=4".into(), + name: "maxdeviant".into(), + }, + ] +} +pub fn create_static_players(player_data: Vec) -> Vec { + let mut players = Vec::new(); + for data in player_data { + players.push(Player::new(players.len(), data.url, data.name)); + } + players +} +pub fn static_player_1(data: &Vec) -> Player { + Player::new(1, data[0].url.clone(), data[0].name.clone()) +} +pub fn static_player_2(data: &Vec) -> Player { + Player::new(2, data[1].url.clone(), data[1].name.clone()) +} +pub fn static_player_3(data: &Vec) -> Player { + Player::new(3, data[2].url.clone(), data[2].name.clone()) +} +pub fn static_player_4(data: &Vec) -> Player { + Player::new(4, data[3].url.clone(), data[3].name.clone()) +} +pub fn static_player_5(data: &Vec) -> Player { + Player::new(5, data[4].url.clone(), data[4].name.clone()) +} +pub fn static_player_6(data: &Vec) -> Player { + Player::new(6, data[5].url.clone(), data[5].name.clone()) +} +pub fn static_player_7(data: &Vec) -> Player { + Player::new(7, data[6].url.clone(), data[6].name.clone()) +} +pub fn static_player_8(data: &Vec) -> Player { + Player::new(8, data[7].url.clone(), data[7].name.clone()) +} +pub fn static_player_9(data: &Vec) -> Player { + Player::new(9, data[8].url.clone(), data[8].name.clone()) +} +pub fn static_player_10(data: &Vec) -> Player { + Player::new(10, data[9].url.clone(), data[9].name.clone()) +} +pub fn static_livestream() -> Livestream { + Livestream { + players: random_players_with_call_status(7), + channel: Some("gpui2-ui".to_string()), + } +} +pub fn populate_player_call_status( + player: Player, + followers: Option>, +) -> PlayerCallStatus { + let mut rng = rand::thread_rng(); + let in_current_project: bool = rng.gen(); + let disconnected: bool = rng.gen(); + let voice_activity: f32 = rng.gen(); + let mic_status = if rng.gen_bool(0.5) { + MicStatus::Muted + } else { + MicStatus::Unmuted + }; + let video_status = if rng.gen_bool(0.5) { + VideoStatus::On + } else { + VideoStatus::Off + }; + let screen_share_status = if rng.gen_bool(0.5) { + ScreenShareStatus::Shared + } else { + ScreenShareStatus::NotShared + }; + PlayerCallStatus { + mic_status, + voice_activity, + video_status, + screen_share_status, + in_current_project, + disconnected, + following: None, + followers, + } +} +pub fn random_players_with_call_status(number_of_players: usize) -> Vec { + let players = create_static_players(static_player_data()); + let mut player_status = vec![]; + for i in 0..number_of_players { + let followers = if i == 0 { + Some(vec![ + players[1].clone(), + players[3].clone(), + players[5].clone(), + players[6].clone(), + ]) + } else if i == 1 { + Some(vec![players[2].clone(), players[6].clone()]) + } else { + None + }; + let call_status = populate_player_call_status(players[i].clone(), followers); + player_status.push(PlayerWithCallStatus::new(players[i].clone(), call_status)); + } + player_status +} + pub fn static_players_with_call_status() -> Vec { let players = static_players(); let mut player_0_status = PlayerCallStatus::new(); @@ -123,7 +368,7 @@ pub fn static_project_panel_project_items() -> Vec { .left_icon(Icon::FolderOpen.into()) .indent_level(3) .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("derrive_element.rs")) + ListEntry::new(Label::new("derive_element.rs")) .left_icon(Icon::FileRust.into()) .indent_level(4), ListEntry::new(Label::new("storybook").color(LabelColor::Modified)) @@ -337,33 +582,49 @@ pub fn example_editor_actions() -> Vec { ] } -pub fn empty_buffer_example() -> Buffer { +pub fn empty_editor_example() -> Editor { + Editor { + tabs: static_tabs_example(), + path: PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(), + symbols: vec![], + buffer: empty_buffer_example(), + } +} + +pub fn empty_buffer_example() -> Buffer { Buffer::new().set_rows(Some(BufferRows::default())) } -pub fn hello_world_rust_buffer_example(cx: &WindowContext) -> Buffer { +pub fn hello_world_rust_editor_example(theme: &Theme) -> Editor { + Editor { + tabs: static_tabs_example(), + path: PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(), + symbols: vec![Symbol(vec![ + HighlightedText { + text: "fn ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + HighlightedText { + text: "main".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + ])], + buffer: hello_world_rust_buffer_example(theme), + } +} + +pub fn hello_world_rust_buffer_example(theme: &Theme) -> Buffer { Buffer::new() .set_title("hello_world.rs".to_string()) .set_path("src/hello_world.rs".to_string()) .set_language("rust".to_string()) .set_rows(Some(BufferRows { show_line_numbers: true, - rows: hello_world_rust_buffer_rows(cx), + rows: hello_world_rust_buffer_rows(theme), })) } -pub fn hello_world_rust_buffer_with_status_example(cx: &WindowContext) -> Buffer { - Buffer::new() - .set_title("hello_world.rs".to_string()) - .set_path("src/hello_world.rs".to_string()) - .set_language("rust".to_string()) - .set_rows(Some(BufferRows { - show_line_numbers: true, - rows: hello_world_rust_with_status_buffer_rows(cx), - })) -} - -pub fn hello_world_rust_buffer_rows(cx: &WindowContext) -> Vec { +pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { let show_line_number = true; vec![ @@ -375,15 +636,15 @@ pub fn hello_world_rust_buffer_rows(cx: &WindowContext) -> Vec { highlighted_texts: vec![ HighlightedText { text: "fn ".to_string(), - color: HighlightColor::Keyword.hsla(cx), + color: HighlightColor::Keyword.hsla(&theme), }, HighlightedText { text: "main".to_string(), - color: HighlightColor::Function.hsla(cx), + color: HighlightColor::Function.hsla(&theme), }, HighlightedText { text: "() {".to_string(), - color: HighlightColor::Default.hsla(cx), + color: HighlightColor::Default.hsla(&theme), }, ], }), @@ -399,7 +660,7 @@ pub fn hello_world_rust_buffer_rows(cx: &WindowContext) -> Vec { highlighted_texts: vec![HighlightedText { text: " // Statements here are executed when the compiled binary is called." .to_string(), - color: HighlightColor::Comment.hsla(cx), + color: HighlightColor::Comment.hsla(&theme), }], }), cursors: None, @@ -422,7 +683,7 @@ pub fn hello_world_rust_buffer_rows(cx: &WindowContext) -> Vec { line: Some(HighlightedLine { highlighted_texts: vec![HighlightedText { text: " // Print text to the console.".to_string(), - color: HighlightColor::Comment.hsla(cx), + color: HighlightColor::Comment.hsla(&theme), }], }), cursors: None, @@ -433,10 +694,34 @@ pub fn hello_world_rust_buffer_rows(cx: &WindowContext) -> Vec { line_number: 5, code_action: false, current: false, + line: Some(HighlightedLine { + highlighted_texts: vec![ + HighlightedText { + text: " println!(".to_string(), + color: HighlightColor::Default.hsla(&theme), + }, + HighlightedText { + text: "\"Hello, world!\"".to_string(), + color: HighlightColor::String.hsla(&theme), + }, + HighlightedText { + text: ");".to_string(), + color: HighlightColor::Default.hsla(&theme), + }, + ], + }), + cursors: None, + status: GitStatus::None, + show_line_number, + }, + BufferRow { + line_number: 6, + code_action: false, + current: false, line: Some(HighlightedLine { highlighted_texts: vec![HighlightedText { text: "}".to_string(), - color: HighlightColor::Default.hsla(cx), + color: HighlightColor::Default.hsla(&theme), }], }), cursors: None, @@ -446,7 +731,36 @@ pub fn hello_world_rust_buffer_rows(cx: &WindowContext) -> Vec { ] } -pub fn hello_world_rust_with_status_buffer_rows(cx: &WindowContext) -> Vec { +pub fn hello_world_rust_editor_with_status_example(theme: &Theme) -> Editor { + Editor { + tabs: static_tabs_example(), + path: PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(), + symbols: vec![Symbol(vec![ + HighlightedText { + text: "fn ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + HighlightedText { + text: "main".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + ])], + buffer: hello_world_rust_buffer_with_status_example(theme), + } +} + +pub fn hello_world_rust_buffer_with_status_example(theme: &Theme) -> Buffer { + Buffer::new() + .set_title("hello_world.rs".to_string()) + .set_path("src/hello_world.rs".to_string()) + .set_language("rust".to_string()) + .set_rows(Some(BufferRows { + show_line_numbers: true, + rows: hello_world_rust_with_status_buffer_rows(theme), + })) +} + +pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec { let show_line_number = true; vec![ @@ -458,15 +772,15 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &WindowContext) -> Vec Vec Vec Vec Vec Vec Vec Buffer { + Buffer::new() + .set_title("zed — fish".to_string()) + .set_rows(Some(BufferRows { + show_line_numbers: false, + rows: terminal_buffer_rows(theme), + })) +} + +pub fn terminal_buffer_rows(theme: &Theme) -> Vec { + let show_line_number = false; + + vec![ + BufferRow { + line_number: 1, + code_action: false, + current: false, + line: Some(HighlightedLine { + highlighted_texts: vec![ + HighlightedText { + text: "maxdeviant ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + HighlightedText { + text: "in ".to_string(), + color: HighlightColor::Default.hsla(&theme), + }, + HighlightedText { + text: "profaned-capital ".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + HighlightedText { + text: "in ".to_string(), + color: HighlightColor::Default.hsla(&theme), + }, + HighlightedText { + text: "~/p/zed ".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + HighlightedText { + text: "on ".to_string(), + color: HighlightColor::Default.hsla(&theme), + }, + HighlightedText { + text: " gpui2-ui ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + ], + }), + cursors: None, + status: GitStatus::None, + show_line_number, + }, + BufferRow { + line_number: 2, + code_action: false, + current: false, + line: Some(HighlightedLine { + highlighted_texts: vec![HighlightedText { + text: "λ ".to_string(), + color: HighlightColor::String.hsla(&theme), + }], + }), + cursors: None, + status: GitStatus::None, + show_line_number, + }, + ] +}