Mainline GPUI2 UI work (#3079)
This PR mainlines the current state of new GPUI2-based UI from the `gpui2-ui` branch. Release Notes: - N/A --------- Co-authored-by: Nate Butler <iamnbutler@gmail.com> Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Co-authored-by: Nate <nate@zed.dev>
This commit is contained in:
parent
08361eb84e
commit
9e1f7c4c18
39 changed files with 1047 additions and 399 deletions
|
@ -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<HighlightedText>);
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Breadcrumb {}
|
||||
pub struct Breadcrumb {
|
||||
path: PathBuf,
|
||||
symbols: Vec<Symbol>,
|
||||
}
|
||||
|
||||
impl Breadcrumb {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
pub fn new(path: PathBuf, symbols: Vec<Symbol>) -> Self {
|
||||
Self { path, symbols }
|
||||
}
|
||||
|
||||
fn render_separator<V: 'static>(&self, theme: &Theme) -> Div<V> {
|
||||
div()
|
||||
.child(" › ")
|
||||
.text_color(HighlightColor::Default.hsla(theme))
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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::<Vec<_>>(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BufferRow>,
|
||||
|
@ -108,9 +107,8 @@ impl BufferRow {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Buffer<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
#[derive(Element, Clone)]
|
||||
pub struct Buffer {
|
||||
scroll_state: ScrollState,
|
||||
rows: Option<BufferRows>,
|
||||
readonly: bool,
|
||||
|
@ -119,10 +117,9 @@ pub struct Buffer<V: 'static> {
|
|||
path: Option<String>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Buffer<V> {
|
||||
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<V: 'static> Buffer<V> {
|
|||
self
|
||||
}
|
||||
|
||||
fn render_row(row: BufferRow, cx: &WindowContext) -> impl IntoElement<V> {
|
||||
fn render_row<V: 'static>(row: BufferRow, cx: &WindowContext) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let system_color = SystemColor::new();
|
||||
|
||||
|
@ -172,28 +169,35 @@ impl<V: 'static> Buffer<V> {
|
|||
};
|
||||
|
||||
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<V: 'static> Buffer<V> {
|
|||
}))
|
||||
}
|
||||
|
||||
fn render_rows(&self, cx: &WindowContext) -> Vec<impl IntoElement<V>> {
|
||||
fn render_rows<V: 'static>(&self, cx: &WindowContext) -> Vec<impl IntoElement<V>> {
|
||||
match &self.rows {
|
||||
Some(rows) => rows
|
||||
.rows
|
||||
|
@ -216,7 +220,7 @@ impl<V: 'static> Buffer<V> {
|
|||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let rows = self.render_rows(cx);
|
||||
v_stack()
|
||||
|
|
|
@ -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<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
current_side: PanelSide,
|
||||
messages: Vec<ChatMessage>,
|
||||
}
|
||||
|
||||
|
@ -19,16 +18,10 @@ impl<V: 'static> ChatPanel<V> {
|
|||
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<ChatMessage>) -> Self {
|
||||
self.messages = messages;
|
||||
self
|
||||
|
@ -37,38 +30,33 @@ impl<V: 'static> ChatPanel<V> {
|
|||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
struct PanelPayload {
|
||||
pub scroll_state: ScrollState,
|
||||
pub messages: Vec<ChatMessage>,
|
||||
}
|
||||
|
||||
Panel::new(
|
||||
self.scroll_state.clone(),
|
||||
|_, payload| {
|
||||
let payload = payload.downcast_ref::<PanelPayload>().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<V: 'static> ChatPanel<V> {
|
|||
.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"))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{Buffer, Toolbar};
|
||||
|
||||
#[derive(Element)]
|
||||
struct Editor<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
toolbar: Toolbar,
|
||||
buffer: Buffer<V>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Editor<V> {
|
||||
pub fn new(toolbar: Toolbar, buffer: Buffer<V>) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
toolbar,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
div().child(self.toolbar.clone())
|
||||
}
|
||||
}
|
60
crates/ui/src/components/editor_pane.rs
Normal file
60
crates/ui/src/components/editor_pane.rs
Normal file
|
@ -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<Tab>,
|
||||
pub path: PathBuf,
|
||||
pub symbols: Vec<Symbol>,
|
||||
pub buffer: Buffer,
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct EditorPane<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
editor: Editor,
|
||||
}
|
||||
|
||||
impl<V: 'static> EditorPane<V> {
|
||||
pub fn new(editor: Editor) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
editor,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
struct LeftItemsPayload {
|
||||
path: PathBuf,
|
||||
symbols: Vec<Symbol>,
|
||||
}
|
||||
|
||||
v_stack()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.flex_1()
|
||||
.child(TabBar::new(self.editor.tabs.clone()))
|
||||
.child(Toolbar::new(
|
||||
|_, payload| {
|
||||
let payload = payload.downcast_ref::<LeftItemsPayload>().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())
|
||||
}
|
||||
}
|
|
@ -105,16 +105,12 @@ impl<V: 'static> Panel<V> {
|
|||
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<V: 'static> Panel<V> {
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()))
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
current_side: PanelSide,
|
||||
}
|
||||
|
||||
impl<V: 'static> ProjectPanel<V> {
|
||||
|
@ -19,69 +17,42 @@ impl<V: 'static> ProjectPanel<V> {
|
|||
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<V>) -> impl IntoElement<V> {
|
||||
struct PanelPayload {
|
||||
pub theme: Arc<Theme>,
|
||||
pub scroll_state: ScrollState,
|
||||
}
|
||||
let theme = theme(cx);
|
||||
|
||||
Panel::new(
|
||||
self.scroll_state.clone(),
|
||||
|_, payload| {
|
||||
let payload = payload.downcast_ref::<PanelPayload>().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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Icon>,
|
||||
|
|
|
@ -7,20 +7,27 @@ use crate::{theme, Icon, IconButton, Tab};
|
|||
pub struct TabBar<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
tabs: Vec<Tab>,
|
||||
}
|
||||
|
||||
impl<V: 'static> TabBar<V> {
|
||||
pub fn new(scroll_state: ScrollState) -> Self {
|
||||
pub fn new(tabs: Vec<Tab>) -> 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<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let can_navigate_back = true;
|
||||
let can_navigate_forward = false;
|
||||
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
|
@ -54,51 +61,7 @@ impl<V: 'static> TabBar<V> {
|
|||
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
|
||||
|
|
|
@ -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::<Arc<Theme>>().unwrap();
|
||||
|
||||
vec![crate::static_data::terminal_buffer(&theme).into_any()]
|
||||
},
|
||||
Box::new(theme),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PlayerWithCallStatus>,
|
||||
pub channel: Option<String>, // projects
|
||||
// windows
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct TitleBar<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
/// If the window is active from the OS's perspective.
|
||||
is_active: Arc<AtomicBool>,
|
||||
livestream: Option<Livestream>,
|
||||
}
|
||||
|
||||
impl<V: 'static> TitleBar<V> {
|
||||
|
@ -28,14 +36,24 @@ impl<V: 'static> TitleBar<V> {
|
|||
Self {
|
||||
view_type: PhantomData,
|
||||
is_active,
|
||||
livestream: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_livestream(mut self, livestream: Option<Livestream>) -> Self {
|
||||
self.livestream = livestream;
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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<V: 'static> TitleBar<V> {
|
|||
.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()
|
||||
|
|
|
@ -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<ToolbarItem>,
|
||||
#[derive(Element)]
|
||||
pub struct Toolbar<V: 'static> {
|
||||
left_items: HackyChildren<V>,
|
||||
left_items_payload: HackyChildrenPayload,
|
||||
right_items: HackyChildren<V>,
|
||||
right_items_payload: HackyChildrenPayload,
|
||||
}
|
||||
|
||||
impl Toolbar {
|
||||
pub fn new() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
impl<V: 'static> Toolbar<V> {
|
||||
pub fn new(
|
||||
left_items: HackyChildren<V>,
|
||||
left_items_payload: HackyChildrenPayload,
|
||||
right_items: HackyChildren<V>,
|
||||
right_items_payload: HackyChildrenPayload,
|
||||
) -> Self {
|
||||
Self {
|
||||
left_items,
|
||||
left_items_payload,
|
||||
right_items,
|
||||
right_items_payload,
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
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::<Arc<Theme>>().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::<Arc<Theme>>().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())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue