Add Buffer component

This commit is contained in:
Marshall Bowers 2023-10-06 17:47:10 -04:00
parent d09f53c380
commit 208d5df106
14 changed files with 661 additions and 19 deletions

View File

@ -8,8 +8,13 @@ use smallvec::smallvec;
pub trait StyleHelpers: Sized + Styled<Style = Style> {
gpui3_macros::style_helpers!();
fn h(mut self, height: Length) -> Self {
self.declared_style().size.height = Some(height);
fn w<L: Into<Length>>(mut self, width: L) -> Self {
self.declared_style().size.width = Some(width.into());
self
}
fn h<L: Into<Length>>(mut self, height: L) -> Self {
self.declared_style().size.height = Some(height.into());
self
}

View File

@ -1,2 +1,3 @@
pub mod assistant_panel;
pub mod buffer;
pub mod panel;

View File

@ -0,0 +1,46 @@
use std::marker::PhantomData;
use gpui3::rems;
use ui::prelude::*;
use ui::{
empty_buffer_example, hello_world_rust_buffer_example,
hello_world_rust_buffer_with_status_example, Buffer,
};
use crate::story::Story;
#[derive(Element)]
pub struct BufferStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> BufferStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
Story::container(cx)
.child(Story::title_for::<_, Buffer<S>>(cx))
.child(Story::label(cx, "Default"))
.child(div().w(rems(64.)).h_96().child(empty_buffer_example()))
.child(Story::label(cx, "Hello World (Rust)"))
.child(
div()
.w(rems(64.))
.h_96()
.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(&theme)),
)
}
}

View File

@ -33,6 +33,7 @@ impl ElementStory {
#[strum(serialize_all = "snake_case")]
pub enum ComponentStory {
AssistantPanel,
Buffer,
Panel,
}
@ -44,6 +45,7 @@ impl ComponentStory {
Self::AssistantPanel => {
components::assistant_panel::AssistantPanelStory::new().into_any()
}
Self::Buffer => components::buffer::BufferStory::new().into_any(),
Self::Panel => components::panel::PanelStory::new().into_any(),
}
}

View File

@ -1,9 +1,11 @@
mod assistant_panel;
mod buffer;
mod icon_button;
mod list;
mod panel;
pub use assistant_panel::*;
pub use buffer::*;
pub use icon_button::*;
pub use list::*;
pub use panel::*;

View File

@ -0,0 +1,238 @@
use std::marker::PhantomData;
use gpui3::{Hsla, WindowContext};
use crate::prelude::*;
use crate::{h_stack, theme, v_stack, Icon, IconElement};
#[derive(Default, PartialEq, Copy, Clone)]
pub struct PlayerCursor {
color: Hsla,
index: usize,
}
#[derive(Default, PartialEq, Clone)]
pub struct HighlightedText {
pub text: String,
pub color: Hsla,
}
#[derive(Default, PartialEq, Clone)]
pub struct HighlightedLine {
pub highlighted_texts: Vec<HighlightedText>,
}
#[derive(Default, PartialEq, Clone)]
pub struct BufferRow {
pub line_number: usize,
pub code_action: bool,
pub current: bool,
pub line: Option<HighlightedLine>,
pub cursors: Option<Vec<PlayerCursor>>,
pub status: GitStatus,
pub show_line_number: bool,
}
#[derive(Clone)]
pub struct BufferRows {
pub show_line_numbers: bool,
pub rows: Vec<BufferRow>,
}
impl Default for BufferRows {
fn default() -> Self {
Self {
show_line_numbers: true,
rows: vec![BufferRow {
line_number: 1,
code_action: false,
current: true,
line: None,
cursors: None,
status: GitStatus::None,
show_line_number: true,
}],
}
}
}
impl BufferRow {
pub fn new(line_number: usize) -> Self {
Self {
line_number,
code_action: false,
current: false,
line: None,
cursors: None,
status: GitStatus::None,
show_line_number: true,
}
}
pub fn set_line(mut self, line: Option<HighlightedLine>) -> Self {
self.line = line;
self
}
pub fn set_cursors(mut self, cursors: Option<Vec<PlayerCursor>>) -> Self {
self.cursors = cursors;
self
}
pub fn add_cursor(mut self, cursor: PlayerCursor) -> Self {
if let Some(cursors) = &mut self.cursors {
cursors.push(cursor);
} else {
self.cursors = Some(vec![cursor]);
}
self
}
pub fn set_status(mut self, status: GitStatus) -> Self {
self.status = status;
self
}
pub fn set_show_line_number(mut self, show_line_number: bool) -> Self {
self.show_line_number = show_line_number;
self
}
pub fn set_code_action(mut self, code_action: bool) -> Self {
self.code_action = code_action;
self
}
pub fn set_current(mut self, current: bool) -> Self {
self.current = current;
self
}
}
#[derive(Element, Clone)]
pub struct Buffer<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
scroll_state: ScrollState,
rows: Option<BufferRows>,
readonly: bool,
language: Option<String>,
title: Option<String>,
path: Option<String>,
}
impl<S: 'static + Send + Sync + Clone> Buffer<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
scroll_state: ScrollState::default(),
rows: Some(BufferRows::default()),
readonly: false,
language: None,
title: Some("untitled".to_string()),
path: None,
}
}
pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) {
self.scroll_state = scroll_state;
}
pub fn set_title<T: Into<Option<String>>>(mut self, title: T) -> Self {
self.title = title.into();
self
}
pub fn set_path<P: Into<Option<String>>>(mut self, path: P) -> Self {
self.path = path.into();
self
}
pub fn set_readonly(mut self, readonly: bool) -> Self {
self.readonly = readonly;
self
}
pub fn set_rows<R: Into<Option<BufferRows>>>(mut self, rows: R) -> Self {
self.rows = rows.into();
self
}
pub fn set_language<L: Into<Option<String>>>(mut self, language: L) -> Self {
self.language = language.into();
self
}
fn render_row(row: BufferRow, cx: &WindowContext) -> impl Element<State = S> {
let theme = theme(cx);
let system_color = SystemColor::new();
let line_background = if row.current {
theme.middle.base.default.background
} else {
system_color.transparent
};
let line_number_color = if row.current {
HighlightColor::Default.hsla(&theme)
} else {
HighlightColor::Comment.hsla(&theme)
};
h_stack()
.fill(line_background)
.w_full()
.gap_2()
.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_0p5().w_3().child(
div()
.text_color(line_number_color)
.child(row.line_number.to_string()),
),
)
})
.child(div().mx_0p5().w_1().h_full().fill(row.status.hsla(cx)))
.children(row.line.map(|line| {
div()
.flex()
.children(line.highlighted_texts.iter().map(|highlighted_text| {
div()
.text_color(highlighted_text.color)
.child(highlighted_text.text.clone())
}))
}))
}
fn render_rows(&self, cx: &WindowContext) -> Vec<impl Element<State = S>> {
match &self.rows {
Some(rows) => rows
.rows
.iter()
.map(|row| Self::render_row(row.clone(), cx))
.collect(),
None => vec![],
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
let rows = self.render_rows(cx);
v_stack()
.flex_1()
.w_full()
.h_full()
.fill(theme.highest.base.default.background)
.children(rows)
}
}

View File

@ -2,8 +2,8 @@ use std::marker::PhantomData;
use gpui3::{div, Div, Hsla, WindowContext};
use crate::theme::theme;
use crate::prelude::*;
use crate::theme::theme;
use crate::{
h_stack, token, v_stack, Avatar, Icon, IconColor, IconElement, IconSize, Label, LabelColor,
LabelSize,
@ -424,7 +424,7 @@ impl<S: 'static + Send + Sync + Clone> ListEntry<S> {
// .ml(rems(0.75 * self.indent_level as f32))
.children((0..self.indent_level).map(|_| {
div()
// .w(token.list_indent_depth)
.w(token.list_indent_depth)
.h_full()
.flex()
.justify_center()

View File

@ -112,7 +112,7 @@ impl<S: 'static + Send + Sync> Panel<S> {
panel_base = v_stack()
.flex_initial()
.h_full()
// .w(current_width)
.w(current_width)
.w_64()
.fill(theme.middle.base.default.background)
.border_r()
@ -122,7 +122,7 @@ impl<S: 'static + Send + Sync> Panel<S> {
panel_base = v_stack()
.flex_initial()
.h_full()
// .w(current_width)
.w(current_width)
.w_64()
.fill(theme.middle.base.default.background)
.border_l()

View File

@ -2,8 +2,8 @@ use std::marker::PhantomData;
use gpui3::{img, ArcCow};
use crate::theme::theme;
use crate::prelude::*;
use crate::theme::theme;
#[derive(Element, Clone)]
pub struct Avatar<S: 'static + Send + Sync> {

View File

@ -4,8 +4,8 @@ use std::sync::Arc;
use gpui3::{svg, Hsla};
use strum::EnumIter;
use crate::theme::{theme, Theme};
use crate::prelude::*;
use crate::theme::{theme, Theme};
#[derive(Default, PartialEq, Copy, Clone)]
pub enum IconSize {

View File

@ -3,8 +3,8 @@ use std::marker::PhantomData;
use gpui3::{Hsla, WindowContext};
use smallvec::SmallVec;
use crate::theme::theme;
use crate::prelude::*;
use crate::theme::theme;
#[derive(Default, PartialEq, Copy, Clone)]
pub enum LabelColor {

View File

@ -5,6 +5,7 @@ mod components;
mod element_ext;
mod elements;
pub mod prelude;
mod static_data;
mod theme;
mod tokens;
@ -13,6 +14,7 @@ pub use components::*;
pub use element_ext::*;
pub use elements::*;
pub use prelude::*;
pub use static_data::*;
pub use tokens::*;
pub use crate::theme::*;

View File

@ -48,28 +48,28 @@ impl HighlightColor {
Self::Default => theme
.syntax
.get("primary")
.expect("no theme.syntax.primary")
.clone(),
.cloned()
.unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
Self::Comment => theme
.syntax
.get("comment")
.expect("no theme.syntax.comment")
.clone(),
.cloned()
.unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
Self::String => theme
.syntax
.get("string")
.expect("no theme.syntax.string")
.clone(),
.cloned()
.unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
Self::Function => theme
.syntax
.get("function")
.expect("no theme.syntax.function")
.clone(),
.cloned()
.unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
Self::Keyword => theme
.syntax
.get("keyword")
.expect("no theme.syntax.keyword")
.clone(),
.cloned()
.unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
}
}
}

View File

@ -0,0 +1,346 @@
use crate::{
Buffer, BufferRow, BufferRows, GitStatus, HighlightColor, HighlightedLine, HighlightedText,
Theme,
};
pub fn empty_buffer_example<S: 'static + Send + Sync + Clone>() -> Buffer<S> {
Buffer::new().set_rows(Some(BufferRows::default()))
}
pub fn hello_world_rust_buffer_example<S: 'static + Send + Sync + Clone>(
theme: &Theme,
) -> Buffer<S> {
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(theme),
}))
}
pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
let show_line_number = true;
vec![
BufferRow {
line_number: 1,
code_action: false,
current: true,
line: Some(HighlightedLine {
highlighted_texts: vec![
HighlightedText {
text: "fn ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "main".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
HighlightedText {
text: "() {".to_string(),
color: HighlightColor::Default.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: " // Statements here are executed when the compiled binary is called."
.to_string(),
color: HighlightColor::Comment.hsla(&theme),
}],
}),
cursors: None,
status: GitStatus::None,
show_line_number,
},
BufferRow {
line_number: 3,
code_action: false,
current: false,
line: None,
cursors: None,
status: GitStatus::None,
show_line_number,
},
BufferRow {
line_number: 4,
code_action: false,
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
text: " // Print text to the console.".to_string(),
color: HighlightColor::Comment.hsla(&theme),
}],
}),
cursors: None,
status: GitStatus::None,
show_line_number,
},
BufferRow {
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(&theme),
}],
}),
cursors: None,
status: GitStatus::None,
show_line_number,
},
]
}
pub fn hello_world_rust_buffer_with_status_example<S: 'static + Send + Sync + Clone>(
theme: &Theme,
) -> Buffer<S> {
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<BufferRow> {
let show_line_number = true;
vec![
BufferRow {
line_number: 1,
code_action: false,
current: true,
line: Some(HighlightedLine {
highlighted_texts: vec![
HighlightedText {
text: "fn ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "main".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
HighlightedText {
text: "() {".to_string(),
color: HighlightColor::Default.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: "// Statements here are executed when the compiled binary is called."
.to_string(),
color: HighlightColor::Comment.hsla(&theme),
}],
}),
cursors: None,
status: GitStatus::Modified,
show_line_number,
},
BufferRow {
line_number: 3,
code_action: false,
current: false,
line: None,
cursors: None,
status: GitStatus::None,
show_line_number,
},
BufferRow {
line_number: 4,
code_action: false,
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
text: " // Print text to the console.".to_string(),
color: HighlightColor::Comment.hsla(&theme),
}],
}),
cursors: None,
status: GitStatus::None,
show_line_number,
},
BufferRow {
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(&theme),
}],
}),
cursors: None,
status: GitStatus::None,
show_line_number,
},
BufferRow {
line_number: 7,
code_action: false,
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
text: "".to_string(),
color: HighlightColor::Default.hsla(&theme),
}],
}),
cursors: None,
status: GitStatus::Created,
show_line_number,
},
BufferRow {
line_number: 8,
code_action: false,
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
text: "// Marshall and Nate were here".to_string(),
color: HighlightColor::Comment.hsla(&theme),
}],
}),
cursors: None,
status: GitStatus::Created,
show_line_number,
},
]
}
pub fn terminal_buffer<S: 'static + Send + Sync + Clone>(theme: &Theme) -> Buffer<S> {
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<BufferRow> {
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,
},
]
}