Remove old ui and storybook crates (#3125)
This PR deletes the old `ui` and `storybook` crates in favor of their newer variants that we'll be landing to `main` in the near future. ### Motivation These crates are based off the old version of GPUI 2 (the `gpui2` crate). At this point we have since transitioned to the new version of GPUI 2 (the `gpui3` crate, currently still on the `gpui2` branch). Having both copies around is confusing, so the old ones are going the way of the dinosaurs. Release Notes: - N/A
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
use std::any::Any;
|
||||
|
||||
use gpui2::{AnyElement, ViewContext};
|
||||
|
||||
pub type HackyChildren<V> = fn(&mut ViewContext<V>, &dyn Any) -> Vec<AnyElement<V>>;
|
||||
|
||||
pub type HackyChildrenPayload = Box<dyn Any>;
|
||||
@@ -1,163 +0,0 @@
|
||||
mod assistant_panel;
|
||||
mod breadcrumb;
|
||||
mod buffer;
|
||||
mod chat_panel;
|
||||
mod collab_panel;
|
||||
mod command_palette;
|
||||
mod context_menu;
|
||||
mod editor_pane;
|
||||
mod facepile;
|
||||
mod icon_button;
|
||||
mod keybinding;
|
||||
mod language_selector;
|
||||
mod list;
|
||||
mod multi_buffer;
|
||||
mod palette;
|
||||
mod panel;
|
||||
mod panes;
|
||||
mod player_stack;
|
||||
mod project_panel;
|
||||
mod recent_projects;
|
||||
mod status_bar;
|
||||
mod tab;
|
||||
mod tab_bar;
|
||||
mod terminal;
|
||||
mod theme_selector;
|
||||
mod title_bar;
|
||||
mod toast;
|
||||
mod toolbar;
|
||||
mod traffic_lights;
|
||||
mod workspace;
|
||||
|
||||
pub use assistant_panel::*;
|
||||
pub use breadcrumb::*;
|
||||
pub use buffer::*;
|
||||
pub use chat_panel::*;
|
||||
pub use collab_panel::*;
|
||||
pub use command_palette::*;
|
||||
pub use context_menu::*;
|
||||
pub use editor_pane::*;
|
||||
pub use facepile::*;
|
||||
pub use icon_button::*;
|
||||
pub use keybinding::*;
|
||||
pub use language_selector::*;
|
||||
pub use list::*;
|
||||
pub use multi_buffer::*;
|
||||
pub use palette::*;
|
||||
pub use panel::*;
|
||||
pub use panes::*;
|
||||
pub use player_stack::*;
|
||||
pub use project_panel::*;
|
||||
pub use recent_projects::*;
|
||||
pub use status_bar::*;
|
||||
pub use tab::*;
|
||||
pub use tab_bar::*;
|
||||
pub use terminal::*;
|
||||
pub use theme_selector::*;
|
||||
pub use title_bar::*;
|
||||
pub use toast::*;
|
||||
pub use toolbar::*;
|
||||
pub use traffic_lights::*;
|
||||
pub use workspace::*;
|
||||
|
||||
// Nate: Commenting this out for now, unsure if we need it.
|
||||
|
||||
// use std::marker::PhantomData;
|
||||
// use std::rc::Rc;
|
||||
|
||||
// use gpui2::elements::div;
|
||||
// use gpui2::interactive::Interactive;
|
||||
// use gpui2::platform::MouseButton;
|
||||
// use gpui2::{ArcCow, Element, EventContext, IntoElement, ParentElement, ViewContext};
|
||||
|
||||
// struct ButtonHandlers<V, D> {
|
||||
// click: Option<Rc<dyn Fn(&mut V, &D, &mut EventContext<V>)>>,
|
||||
// }
|
||||
|
||||
// impl<V, D> Default for ButtonHandlers<V, D> {
|
||||
// fn default() -> Self {
|
||||
// Self { click: None }
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[derive(Element)]
|
||||
// pub struct Button<V: 'static, D: 'static> {
|
||||
// handlers: ButtonHandlers<V, D>,
|
||||
// label: Option<ArcCow<'static, str>>,
|
||||
// icon: Option<ArcCow<'static, str>>,
|
||||
// data: Rc<D>,
|
||||
// view_type: PhantomData<V>,
|
||||
// }
|
||||
|
||||
// // Impl block for buttons without data.
|
||||
// // See below for an impl block for any button.
|
||||
// impl<V: 'static> Button<V, ()> {
|
||||
// fn new() -> Self {
|
||||
// Self {
|
||||
// handlers: ButtonHandlers::default(),
|
||||
// label: None,
|
||||
// icon: None,
|
||||
// data: Rc::new(()),
|
||||
// view_type: PhantomData,
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
|
||||
// Button {
|
||||
// handlers: ButtonHandlers::default(),
|
||||
// label: self.label,
|
||||
// icon: self.icon,
|
||||
// data: Rc::new(data),
|
||||
// view_type: PhantomData,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Impl block for button regardless of its data type.
|
||||
// impl<V: 'static, D: 'static> Button<V, D> {
|
||||
// pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
|
||||
// self.label = Some(label.into());
|
||||
// self
|
||||
// }
|
||||
|
||||
// pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
|
||||
// self.icon = Some(icon.into());
|
||||
// self
|
||||
// }
|
||||
|
||||
// pub fn on_click(
|
||||
// mut self,
|
||||
// handler: impl Fn(&mut V, &D, &mut EventContext<V>) + 'static,
|
||||
// ) -> Self {
|
||||
// self.handlers.click = Some(Rc::new(handler));
|
||||
// self
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn button<V>() -> Button<V, ()> {
|
||||
// Button::new()
|
||||
// }
|
||||
|
||||
// impl<V: 'static, D: 'static> Button<V, D> {
|
||||
// fn render(
|
||||
// &mut self,
|
||||
// view: &mut V,
|
||||
// cx: &mut ViewContext<V>,
|
||||
// ) -> impl IntoElement<V> + Interactive<V> {
|
||||
// // let colors = &cx.theme::<Theme>().colors;
|
||||
|
||||
// let button = div()
|
||||
// // .fill(colors.error(0.5))
|
||||
// .h_4()
|
||||
// .children(self.label.clone());
|
||||
|
||||
// if let Some(handler) = self.handlers.click.clone() {
|
||||
// let data = self.data.clone();
|
||||
// button.on_mouse_down(MouseButton::Left, move |view, event, cx| {
|
||||
// handler(view, data.as_ref(), cx)
|
||||
// })
|
||||
// } else {
|
||||
// button
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -1,91 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use gpui2::geometry::rems;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme::theme;
|
||||
use crate::{Icon, IconButton, Label, Panel, PanelSide};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct AssistantPanel<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
current_side: PanelSide,
|
||||
}
|
||||
|
||||
impl<V: 'static> AssistantPanel<V> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
scroll_state: ScrollState::default(),
|
||||
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> {
|
||||
let theme = theme(cx);
|
||||
|
||||
struct PanelPayload {
|
||||
pub scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
Panel::new(
|
||||
self.scroll_state.clone(),
|
||||
|_, payload| {
|
||||
let payload = payload.downcast_ref::<PanelPayload>().unwrap();
|
||||
|
||||
vec![div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.h_full()
|
||||
.px_2()
|
||||
.gap_2()
|
||||
// Header
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.justify_between()
|
||||
.gap_2()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.child(IconButton::new(Icon::Menu))
|
||||
.child(Label::new("New Conversation")),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_px()
|
||||
.child(IconButton::new(Icon::SplitMessage))
|
||||
.child(IconButton::new(Icon::Quote))
|
||||
.child(IconButton::new(Icon::MagicWand))
|
||||
.child(IconButton::new(Icon::Plus))
|
||||
.child(IconButton::new(Icon::Maximize)),
|
||||
),
|
||||
)
|
||||
// Chat Body
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.overflow_y_scroll(payload.scroll_state.clone())
|
||||
.child(Label::new("Is this thing on?")),
|
||||
)
|
||||
.into_any()]
|
||||
},
|
||||
Box::new(PanelPayload {
|
||||
scroll_state: self.scroll_state.clone(),
|
||||
}),
|
||||
)
|
||||
.side(self.current_side)
|
||||
.width(rems(32.))
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
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 {
|
||||
path: PathBuf,
|
||||
symbols: Vec<Symbol>,
|
||||
}
|
||||
|
||||
impl Breadcrumb {
|
||||
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?).
|
||||
.font("Zed Mono Extended")
|
||||
.text_sm()
|
||||
.text_color(theme.middle.base.default.foreground)
|
||||
.rounded_md()
|
||||
.hover()
|
||||
.fill(theme.highest.base.hovered.background)
|
||||
.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,233 +0,0 @@
|
||||
use gpui2::{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 {
|
||||
scroll_state: ScrollState,
|
||||
rows: Option<BufferRows>,
|
||||
readonly: bool,
|
||||
language: Option<String>,
|
||||
title: Option<String>,
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
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<V: 'static>(row: BufferRow, cx: &WindowContext) -> impl IntoElement<V> {
|
||||
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<V: 'static>(&self, cx: &WindowContext) -> Vec<impl IntoElement<V>> {
|
||||
match &self.rows {
|
||||
Some(rows) => rows
|
||||
.rows
|
||||
.iter()
|
||||
.map(|row| Self::render_row(row.clone(), cx))
|
||||
.collect(),
|
||||
None => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.fill(theme.highest.base.default.background)
|
||||
.children(rows)
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme::theme;
|
||||
use crate::{Icon, IconButton, Input, Label, LabelColor};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct ChatPanel<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
messages: Vec<ChatMessage>,
|
||||
}
|
||||
|
||||
impl<V: 'static> ChatPanel<V> {
|
||||
pub fn new(scroll_state: ScrollState) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
messages: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_messages(mut self, messages: Vec<ChatMessage>) -> Self {
|
||||
self.messages = messages;
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.justify_between()
|
||||
.h_full()
|
||||
.px_2()
|
||||
.gap_2()
|
||||
// Header
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.justify_between()
|
||||
.py_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)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
// Chat Body
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.overflow_y_scroll(self.scroll_state.clone())
|
||||
.children(self.messages.clone()),
|
||||
)
|
||||
// Composer
|
||||
.child(div().flex().my_2().child(Input::new("Message #design"))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element, Clone)]
|
||||
pub struct ChatMessage {
|
||||
author: String,
|
||||
text: String,
|
||||
sent_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
impl ChatMessage {
|
||||
pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self {
|
||||
Self {
|
||||
author,
|
||||
text,
|
||||
sent_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_2()
|
||||
.child(Label::new(self.author.clone()))
|
||||
.child(
|
||||
Label::new(self.sent_at.format("%m/%d/%Y").to_string())
|
||||
.color(LabelColor::Muted),
|
||||
),
|
||||
)
|
||||
.child(div().child(Label::new(self.text.clone())))
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use gpui2::elements::{img, svg};
|
||||
use gpui2::ArcCow;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme::{theme, Theme};
|
||||
use crate::{
|
||||
static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List,
|
||||
ListHeader, ToggleState,
|
||||
};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct CollabPanel<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
impl<V: 'static> CollabPanel<V> {
|
||||
pub fn new(scroll_state: ScrollState) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
v_stack()
|
||||
.w_64()
|
||||
.h_full()
|
||||
.fill(theme.middle.base.default.background)
|
||||
.child(
|
||||
v_stack()
|
||||
.w_full()
|
||||
.overflow_y_scroll(self.scroll_state.clone())
|
||||
.child(
|
||||
div()
|
||||
.fill(theme.lowest.base.default.background)
|
||||
.pb_1()
|
||||
.border_color(theme.lowest.base.default.border)
|
||||
.border_b()
|
||||
.child(
|
||||
List::new(static_collab_panel_current_call())
|
||||
.header(
|
||||
ListHeader::new("CRDB")
|
||||
.left_icon(Icon::Hash.into())
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_stack().py_1().child(
|
||||
List::new(static_collab_panel_channels())
|
||||
.header(
|
||||
ListHeader::new("CHANNELS").set_toggle(ToggleState::Toggled),
|
||||
)
|
||||
.empty_message("No channels yet. Add a channel to get started.")
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_stack().py_1().child(
|
||||
List::new(static_collab_panel_current_call())
|
||||
.header(
|
||||
ListHeader::new("CONTACTS – ONLINE")
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_stack().py_1().child(
|
||||
List::new(static_collab_panel_current_call())
|
||||
.header(
|
||||
ListHeader::new("CONTACTS – OFFLINE")
|
||||
.set_toggle(ToggleState::NotToggled),
|
||||
)
|
||||
.set_toggle(ToggleState::NotToggled),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.h_7()
|
||||
.px_2()
|
||||
.border_t()
|
||||
.border_color(theme.middle.variant.default.border)
|
||||
.flex()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(theme.middle.variant.default.foreground)
|
||||
.child("Find..."),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn list_section_header(
|
||||
&self,
|
||||
label: impl Into<ArcCow<'static, str>>,
|
||||
expanded: bool,
|
||||
theme: &Theme,
|
||||
) -> impl Element<V> {
|
||||
div()
|
||||
.h_7()
|
||||
.px_2()
|
||||
.flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.child(div().flex().gap_1().text_sm().child(label))
|
||||
.child(
|
||||
div().flex().h_full().gap_1().items_center().child(
|
||||
svg()
|
||||
.path(if expanded {
|
||||
"icons/caret_down.svg"
|
||||
} else {
|
||||
"icons/caret_up.svg"
|
||||
})
|
||||
.w_3p5()
|
||||
.h_3p5()
|
||||
.fill(theme.middle.variant.default.foreground),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn list_item(
|
||||
&self,
|
||||
avatar_uri: impl Into<ArcCow<'static, str>>,
|
||||
label: impl Into<ArcCow<'static, str>>,
|
||||
theme: &Theme,
|
||||
) -> impl Element<V> {
|
||||
div()
|
||||
.h_7()
|
||||
.px_2()
|
||||
.flex()
|
||||
.items_center()
|
||||
.hover()
|
||||
.fill(theme.lowest.variant.hovered.background)
|
||||
.active()
|
||||
.fill(theme.lowest.variant.pressed.background)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.text_sm()
|
||||
.child(
|
||||
img()
|
||||
.uri(avatar_uri)
|
||||
.size_3p5()
|
||||
.rounded_full()
|
||||
.fill(theme.middle.positive.default.foreground),
|
||||
)
|
||||
.child(label),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{example_editor_actions, OrderMethod, Palette};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct CommandPalette<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
impl<V: 'static> CommandPalette<V> {
|
||||
pub fn new(scroll_state: ScrollState) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
div().child(
|
||||
Palette::new(self.scroll_state.clone())
|
||||
.items(example_editor_actions())
|
||||
.placeholder("Execute a command...")
|
||||
.empty_string("No items found.")
|
||||
.default_order(OrderMethod::Ascending),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::theme::theme;
|
||||
use crate::{
|
||||
v_stack, Label, List, ListEntry, ListItem, ListItemVariant, ListSeparator, ListSubHeader,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ContextMenuItem {
|
||||
Header(&'static str),
|
||||
Entry(Label),
|
||||
Separator,
|
||||
}
|
||||
|
||||
impl ContextMenuItem {
|
||||
fn to_list_item(self) -> ListItem {
|
||||
match self {
|
||||
ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
|
||||
ContextMenuItem::Entry(label) => {
|
||||
ListEntry::new(label).variant(ListItemVariant::Inset).into()
|
||||
}
|
||||
ContextMenuItem::Separator => ListSeparator::new().into(),
|
||||
}
|
||||
}
|
||||
pub fn header(label: &'static str) -> Self {
|
||||
Self::Header(label)
|
||||
}
|
||||
pub fn separator() -> Self {
|
||||
Self::Separator
|
||||
}
|
||||
pub fn entry(label: Label) -> Self {
|
||||
Self::Entry(label)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct ContextMenu {
|
||||
items: Vec<ContextMenuItem>,
|
||||
}
|
||||
|
||||
impl ContextMenu {
|
||||
pub fn new(items: impl IntoIterator<Item = ContextMenuItem>) -> Self {
|
||||
Self {
|
||||
items: items.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
fn render<V: 'static>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
v_stack()
|
||||
.flex()
|
||||
.fill(theme.lowest.base.default.background)
|
||||
.border()
|
||||
.border_color(theme.lowest.base.default.border)
|
||||
.child(
|
||||
List::new(
|
||||
self.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(ContextMenuItem::to_list_item)
|
||||
.collect(),
|
||||
)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
)
|
||||
//div().p_1().children(self.items.clone())
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{theme, Avatar, Player};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Facepile {
|
||||
players: Vec<Player>,
|
||||
}
|
||||
|
||||
impl Facepile {
|
||||
pub fn new<P: Iterator<Item = Player>>(players: P) -> Self {
|
||||
Self {
|
||||
players: players.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let player_count = self.players.len();
|
||||
let player_list = self.players.iter().enumerate().map(|(ix, player)| {
|
||||
let isnt_last = ix < player_count - 1;
|
||||
|
||||
div()
|
||||
.when(isnt_last, |div| div.neg_mr_1())
|
||||
.child(Avatar::new(player.avatar_src().to_string()))
|
||||
});
|
||||
div().p_1().flex().items_center().children(player_list)
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{theme, Icon, IconColor, IconElement};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct IconButton {
|
||||
icon: Icon,
|
||||
color: IconColor,
|
||||
variant: ButtonVariant,
|
||||
state: InteractionState,
|
||||
}
|
||||
|
||||
impl IconButton {
|
||||
pub fn new(icon: Icon) -> Self {
|
||||
Self {
|
||||
icon,
|
||||
color: IconColor::default(),
|
||||
variant: ButtonVariant::default(),
|
||||
state: InteractionState::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon(mut self, icon: Icon) -> Self {
|
||||
self.icon = icon;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: IconColor) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn variant(mut self, variant: ButtonVariant) -> Self {
|
||||
self.variant = variant;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn state(mut self, state: InteractionState) -> Self {
|
||||
self.state = state;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
let icon_color = match (self.state, self.color) {
|
||||
(InteractionState::Disabled, _) => IconColor::Disabled,
|
||||
_ => self.color,
|
||||
};
|
||||
|
||||
let mut div = div();
|
||||
if self.variant == ButtonVariant::Filled {
|
||||
div = div.fill(theme.highest.on.default.background);
|
||||
}
|
||||
|
||||
div.w_7()
|
||||
.h_6()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.rounded_md()
|
||||
.hover()
|
||||
.fill(theme.highest.base.hovered.background)
|
||||
.active()
|
||||
.fill(theme.highest.base.pressed.background)
|
||||
.child(IconElement::new(self.icon).color(icon_color))
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme;
|
||||
|
||||
#[derive(Element, Clone)]
|
||||
pub struct Keybinding {
|
||||
/// A keybinding consists of a key and a set of modifier keys.
|
||||
/// More then one keybinding produces a chord.
|
||||
///
|
||||
/// This should always contain at least one element.
|
||||
keybinding: Vec<(String, ModifierKeys)>,
|
||||
}
|
||||
|
||||
impl Keybinding {
|
||||
pub fn new(key: String, modifiers: ModifierKeys) -> Self {
|
||||
Self {
|
||||
keybinding: vec![(key, modifiers)],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_chord(
|
||||
first_note: (String, ModifierKeys),
|
||||
second_note: (String, ModifierKeys),
|
||||
) -> Self {
|
||||
Self {
|
||||
keybinding: vec![first_note, second_note],
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
div()
|
||||
.flex()
|
||||
.gap_2()
|
||||
.children(self.keybinding.iter().map(|(key, modifiers)| {
|
||||
div()
|
||||
.flex()
|
||||
.gap_1()
|
||||
.children(ModifierKey::iter().filter_map(|modifier| {
|
||||
if modifiers.0.contains(&modifier) {
|
||||
Some(Key::new(modifier.glyph()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}))
|
||||
.child(Key::new(key.clone()))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Key {
|
||||
key: String,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn new<K>(key: K) -> Self
|
||||
where
|
||||
K: Into<String>,
|
||||
{
|
||||
Self { key: key.into() }
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.px_2()
|
||||
.py_0()
|
||||
.rounded_md()
|
||||
.text_sm()
|
||||
.text_color(theme.lowest.on.default.foreground)
|
||||
.fill(theme.lowest.on.default.background)
|
||||
.child(self.key.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The order the modifier keys appear in this enum impacts the order in
|
||||
// which they are rendered in the UI.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum ModifierKey {
|
||||
Control,
|
||||
Alt,
|
||||
Command,
|
||||
Shift,
|
||||
}
|
||||
|
||||
impl ModifierKey {
|
||||
/// Returns the glyph for the [`ModifierKey`].
|
||||
pub fn glyph(&self) -> char {
|
||||
match self {
|
||||
Self::Control => '^',
|
||||
Self::Alt => '⌥',
|
||||
Self::Command => '⌘',
|
||||
Self::Shift => '⇧',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModifierKeys(HashSet<ModifierKey>);
|
||||
|
||||
impl ModifierKeys {
|
||||
pub fn new() -> Self {
|
||||
Self(HashSet::new())
|
||||
}
|
||||
|
||||
pub fn all() -> Self {
|
||||
Self(HashSet::from_iter(ModifierKey::iter()))
|
||||
}
|
||||
|
||||
pub fn add(mut self, modifier: ModifierKey) -> Self {
|
||||
self.0.insert(modifier);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn control(mut self, control: bool) -> Self {
|
||||
if control {
|
||||
self.0.insert(ModifierKey::Control);
|
||||
} else {
|
||||
self.0.remove(&ModifierKey::Control);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn alt(mut self, alt: bool) -> Self {
|
||||
if alt {
|
||||
self.0.insert(ModifierKey::Alt);
|
||||
} else {
|
||||
self.0.remove(&ModifierKey::Alt);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn command(mut self, command: bool) -> Self {
|
||||
if command {
|
||||
self.0.insert(ModifierKey::Command);
|
||||
} else {
|
||||
self.0.remove(&ModifierKey::Command);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn shift(mut self, shift: bool) -> Self {
|
||||
if shift {
|
||||
self.0.insert(ModifierKey::Shift);
|
||||
} else {
|
||||
self.0.remove(&ModifierKey::Shift);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{OrderMethod, Palette, PaletteItem};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct LanguageSelector {
|
||||
scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
impl LanguageSelector {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
scroll_state: ScrollState::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
div().child(
|
||||
Palette::new(self.scroll_state.clone())
|
||||
.items(vec![
|
||||
PaletteItem::new("C"),
|
||||
PaletteItem::new("C++"),
|
||||
PaletteItem::new("CSS"),
|
||||
PaletteItem::new("Elixir"),
|
||||
PaletteItem::new("Elm"),
|
||||
PaletteItem::new("ERB"),
|
||||
PaletteItem::new("Rust (current)"),
|
||||
PaletteItem::new("Scheme"),
|
||||
PaletteItem::new("TOML"),
|
||||
PaletteItem::new("TypeScript"),
|
||||
])
|
||||
.placeholder("Select a language...")
|
||||
.empty_string("No matches")
|
||||
.default_order(OrderMethod::Ascending),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,512 +0,0 @@
|
||||
use gpui2::elements::div::Div;
|
||||
use gpui2::{Hsla, WindowContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{
|
||||
h_stack, theme, token, v_stack, Avatar, DisclosureControlVisibility, Icon, IconColor,
|
||||
IconElement, IconSize, InteractionState, Label, LabelColor, LabelSize, SystemColor,
|
||||
ToggleState,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug, PartialEq)]
|
||||
pub enum ListItemVariant {
|
||||
/// The list item extends to the far left and right of the list.
|
||||
#[default]
|
||||
FullWidth,
|
||||
Inset,
|
||||
}
|
||||
|
||||
#[derive(Element, Clone, Copy)]
|
||||
pub struct ListHeader {
|
||||
label: &'static str,
|
||||
left_icon: Option<Icon>,
|
||||
variant: ListItemVariant,
|
||||
state: InteractionState,
|
||||
toggleable: Toggleable,
|
||||
}
|
||||
|
||||
impl ListHeader {
|
||||
pub fn new(label: &'static str) -> Self {
|
||||
Self {
|
||||
label,
|
||||
left_icon: None,
|
||||
variant: ListItemVariant::default(),
|
||||
state: InteractionState::default(),
|
||||
toggleable: Toggleable::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
|
||||
self.toggleable = toggle.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_toggleable(mut self, toggleable: Toggleable) -> Self {
|
||||
self.toggleable = toggleable;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
|
||||
self.left_icon = left_icon;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn state(mut self, state: InteractionState) -> Self {
|
||||
self.state = state;
|
||||
self
|
||||
}
|
||||
|
||||
fn disclosure_control<V: 'static>(&self) -> Div<V> {
|
||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
||||
let is_toggled = Toggleable::is_toggled(&self.toggleable);
|
||||
|
||||
match (is_toggleable, is_toggled) {
|
||||
(false, _) => div(),
|
||||
(_, true) => div().child(IconElement::new(Icon::ChevronRight).color(IconColor::Muted)),
|
||||
(_, false) => div().child(IconElement::new(Icon::ChevronDown).size(IconSize::Small)),
|
||||
}
|
||||
}
|
||||
|
||||
fn background_color(&self, cx: &WindowContext) -> Hsla {
|
||||
let theme = theme(cx);
|
||||
let system_color = SystemColor::new();
|
||||
|
||||
match self.state {
|
||||
InteractionState::Hovered => theme.lowest.base.hovered.background,
|
||||
InteractionState::Active => theme.lowest.base.pressed.background,
|
||||
InteractionState::Enabled => theme.lowest.on.default.background,
|
||||
_ => system_color.transparent,
|
||||
}
|
||||
}
|
||||
|
||||
fn label_color(&self) -> LabelColor {
|
||||
match self.state {
|
||||
InteractionState::Disabled => LabelColor::Disabled,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn icon_color(&self) -> IconColor {
|
||||
match self.state {
|
||||
InteractionState::Disabled => IconColor::Disabled,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let token = token();
|
||||
let system_color = SystemColor::new();
|
||||
let background_color = self.background_color(cx);
|
||||
|
||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
||||
let is_toggled = Toggleable::is_toggled(&self.toggleable);
|
||||
|
||||
let disclosure_control = self.disclosure_control();
|
||||
|
||||
h_stack()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.fill(background_color)
|
||||
.when(self.state == InteractionState::Focused, |this| {
|
||||
this.border()
|
||||
.border_color(theme.lowest.accent.default.border)
|
||||
})
|
||||
.relative()
|
||||
.py_1()
|
||||
.child(
|
||||
div()
|
||||
.h_6()
|
||||
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
||||
.flex()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.children(self.left_icon.map(|i| {
|
||||
IconElement::new(i)
|
||||
.color(IconColor::Muted)
|
||||
.size(IconSize::Small)
|
||||
}))
|
||||
.child(
|
||||
Label::new(self.label)
|
||||
.color(LabelColor::Muted)
|
||||
.size(LabelSize::Small),
|
||||
),
|
||||
)
|
||||
.child(disclosure_control),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element, Clone, Copy)]
|
||||
pub struct ListSubHeader {
|
||||
label: &'static str,
|
||||
left_icon: Option<Icon>,
|
||||
variant: ListItemVariant,
|
||||
}
|
||||
|
||||
impl ListSubHeader {
|
||||
pub fn new(label: &'static str) -> Self {
|
||||
Self {
|
||||
label,
|
||||
left_icon: None,
|
||||
variant: ListItemVariant::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
|
||||
self.left_icon = left_icon;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let token = token();
|
||||
|
||||
h_stack().flex_1().w_full().relative().py_1().child(
|
||||
div()
|
||||
.h_6()
|
||||
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
||||
.flex()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.children(self.left_icon.map(|i| {
|
||||
IconElement::new(i)
|
||||
.color(IconColor::Muted)
|
||||
.size(IconSize::Small)
|
||||
}))
|
||||
.child(
|
||||
Label::new(self.label)
|
||||
.color(LabelColor::Muted)
|
||||
.size(LabelSize::Small),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum LeftContent {
|
||||
Icon(Icon),
|
||||
Avatar(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum ListEntrySize {
|
||||
#[default]
|
||||
Small,
|
||||
Medium,
|
||||
}
|
||||
|
||||
#[derive(Clone, Element)]
|
||||
pub enum ListItem {
|
||||
Entry(ListEntry),
|
||||
Separator(ListSeparator),
|
||||
Header(ListSubHeader),
|
||||
}
|
||||
|
||||
impl From<ListEntry> for ListItem {
|
||||
fn from(entry: ListEntry) -> Self {
|
||||
Self::Entry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListSeparator> for ListItem {
|
||||
fn from(entry: ListSeparator) -> Self {
|
||||
Self::Separator(entry)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListSubHeader> for ListItem {
|
||||
fn from(entry: ListSubHeader) -> Self {
|
||||
Self::Header(entry)
|
||||
}
|
||||
}
|
||||
|
||||
impl ListItem {
|
||||
fn render<V: 'static>(&mut self, v: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
match self {
|
||||
ListItem::Entry(entry) => div().child(entry.render(v, cx)),
|
||||
ListItem::Separator(separator) => div().child(separator.render(v, cx)),
|
||||
ListItem::Header(header) => div().child(header.render(v, cx)),
|
||||
}
|
||||
}
|
||||
pub fn new(label: Label) -> Self {
|
||||
Self::Entry(ListEntry::new(label))
|
||||
}
|
||||
pub fn as_entry(&mut self) -> Option<&mut ListEntry> {
|
||||
if let Self::Entry(entry) = self {
|
||||
Some(entry)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element, Clone)]
|
||||
pub struct ListEntry {
|
||||
disclosure_control_style: DisclosureControlVisibility,
|
||||
indent_level: u32,
|
||||
label: Label,
|
||||
left_content: Option<LeftContent>,
|
||||
variant: ListItemVariant,
|
||||
size: ListEntrySize,
|
||||
state: InteractionState,
|
||||
toggle: Option<ToggleState>,
|
||||
}
|
||||
|
||||
impl ListEntry {
|
||||
pub fn new(label: Label) -> Self {
|
||||
Self {
|
||||
disclosure_control_style: DisclosureControlVisibility::default(),
|
||||
indent_level: 0,
|
||||
label,
|
||||
variant: ListItemVariant::default(),
|
||||
left_content: None,
|
||||
size: ListEntrySize::default(),
|
||||
state: InteractionState::default(),
|
||||
toggle: None,
|
||||
}
|
||||
}
|
||||
pub fn variant(mut self, variant: ListItemVariant) -> Self {
|
||||
self.variant = variant;
|
||||
self
|
||||
}
|
||||
pub fn indent_level(mut self, indent_level: u32) -> Self {
|
||||
self.indent_level = indent_level;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
|
||||
self.toggle = Some(toggle);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_content(mut self, left_content: LeftContent) -> Self {
|
||||
self.left_content = Some(left_content);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_icon(mut self, left_icon: Icon) -> Self {
|
||||
self.left_content = Some(LeftContent::Icon(left_icon));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_avatar(mut self, left_avatar: &'static str) -> Self {
|
||||
self.left_content = Some(LeftContent::Avatar(left_avatar));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn state(mut self, state: InteractionState) -> Self {
|
||||
self.state = state;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: ListEntrySize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disclosure_control_style(
|
||||
mut self,
|
||||
disclosure_control_style: DisclosureControlVisibility,
|
||||
) -> Self {
|
||||
self.disclosure_control_style = disclosure_control_style;
|
||||
self
|
||||
}
|
||||
|
||||
fn background_color(&self, cx: &WindowContext) -> Hsla {
|
||||
let theme = theme(cx);
|
||||
let system_color = SystemColor::new();
|
||||
|
||||
match self.state {
|
||||
InteractionState::Hovered => theme.lowest.base.hovered.background,
|
||||
InteractionState::Active => theme.lowest.base.pressed.background,
|
||||
InteractionState::Enabled => theme.lowest.on.default.background,
|
||||
_ => system_color.transparent,
|
||||
}
|
||||
}
|
||||
|
||||
fn label_color(&self) -> LabelColor {
|
||||
match self.state {
|
||||
InteractionState::Disabled => LabelColor::Disabled,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn icon_color(&self) -> IconColor {
|
||||
match self.state {
|
||||
InteractionState::Disabled => IconColor::Disabled,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn disclosure_control<V: 'static>(
|
||||
&mut self,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Option<impl IntoElement<V>> {
|
||||
let theme = theme(cx);
|
||||
let token = token();
|
||||
|
||||
let disclosure_control_icon = if let Some(ToggleState::Toggled) = self.toggle {
|
||||
IconElement::new(Icon::ChevronDown)
|
||||
} else {
|
||||
IconElement::new(Icon::ChevronRight)
|
||||
}
|
||||
.color(IconColor::Muted)
|
||||
.size(IconSize::Small);
|
||||
|
||||
match (self.toggle, self.disclosure_control_style) {
|
||||
(Some(_), DisclosureControlVisibility::OnHover) => {
|
||||
Some(div().absolute().neg_left_5().child(disclosure_control_icon))
|
||||
}
|
||||
(Some(_), DisclosureControlVisibility::Always) => {
|
||||
Some(div().child(disclosure_control_icon))
|
||||
}
|
||||
(None, _) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let token = token();
|
||||
let system_color = SystemColor::new();
|
||||
let background_color = self.background_color(cx);
|
||||
|
||||
let left_content = match self.left_content {
|
||||
Some(LeftContent::Icon(i)) => {
|
||||
Some(h_stack().child(IconElement::new(i).size(IconSize::Small)))
|
||||
}
|
||||
Some(LeftContent::Avatar(src)) => Some(h_stack().child(Avatar::new(src))),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let sized_item = match self.size {
|
||||
ListEntrySize::Small => div().h_6(),
|
||||
ListEntrySize::Medium => div().h_7(),
|
||||
};
|
||||
|
||||
div()
|
||||
.fill(background_color)
|
||||
.when(self.state == InteractionState::Focused, |this| {
|
||||
this.border()
|
||||
.border_color(theme.lowest.accent.default.border)
|
||||
})
|
||||
.relative()
|
||||
.py_1()
|
||||
.child(
|
||||
sized_item
|
||||
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
||||
// .ml(rems(0.75 * self.indent_level as f32))
|
||||
.children((0..self.indent_level).map(|_| {
|
||||
div()
|
||||
.w(token.list_indent_depth)
|
||||
.h_full()
|
||||
.flex()
|
||||
.justify_center()
|
||||
.child(h_stack().child(div().w_px().h_full()).child(
|
||||
div().w_px().h_full().fill(theme.middle.base.default.border),
|
||||
))
|
||||
}))
|
||||
.flex()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.relative()
|
||||
.children(self.disclosure_control(cx))
|
||||
.children(left_content)
|
||||
.child(self.label.clone()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Element)]
|
||||
pub struct ListSeparator;
|
||||
|
||||
impl ListSeparator {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div().h_px().w_full().fill(theme.lowest.base.default.border)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct List {
|
||||
items: Vec<ListItem>,
|
||||
empty_message: &'static str,
|
||||
header: Option<ListHeader>,
|
||||
toggleable: Toggleable,
|
||||
}
|
||||
|
||||
impl List {
|
||||
pub fn new(items: Vec<ListItem>) -> Self {
|
||||
Self {
|
||||
items,
|
||||
empty_message: "No items",
|
||||
header: None,
|
||||
toggleable: Toggleable::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty_message(mut self, empty_message: &'static str) -> Self {
|
||||
self.empty_message = empty_message;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn header(mut self, header: ListHeader) -> Self {
|
||||
self.header = Some(header);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
|
||||
self.toggleable = toggle.into();
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let token = token();
|
||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
||||
let is_toggled = Toggleable::is_toggled(&self.toggleable);
|
||||
|
||||
let disclosure_control = if is_toggleable {
|
||||
IconElement::new(Icon::ChevronRight)
|
||||
} else {
|
||||
IconElement::new(Icon::ChevronDown)
|
||||
};
|
||||
|
||||
let list_content = match (self.items.is_empty(), is_toggled) {
|
||||
(_, false) => div(),
|
||||
(false, _) => div().children(self.items.iter().cloned()),
|
||||
(true, _) => div().child(Label::new(self.empty_message).color(LabelColor::Muted)),
|
||||
};
|
||||
|
||||
v_stack()
|
||||
.py_1()
|
||||
.children(
|
||||
self.header
|
||||
.clone()
|
||||
.map(|header| header.set_toggleable(self.toggleable)),
|
||||
)
|
||||
.child(list_content)
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{v_stack, Buffer, Icon, IconButton, Label, LabelSize};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct MultiBuffer<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
buffers: Vec<Buffer>,
|
||||
}
|
||||
|
||||
impl<V: 'static> MultiBuffer<V> {
|
||||
pub fn new(buffers: Vec<Buffer>) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
buffers,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
v_stack()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.flex_1()
|
||||
.children(self.buffers.clone().into_iter().map(|buffer| {
|
||||
v_stack()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.p_4()
|
||||
.fill(theme.lowest.base.default.background)
|
||||
.child(Label::new("main.rs").size(LabelSize::Small))
|
||||
.child(IconButton::new(Icon::ArrowUpRight)),
|
||||
)
|
||||
.child(buffer)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme::theme;
|
||||
use crate::{h_stack, v_stack, Keybinding, Label, LabelColor};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Palette<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
input_placeholder: &'static str,
|
||||
empty_string: &'static str,
|
||||
items: Vec<PaletteItem>,
|
||||
default_order: OrderMethod,
|
||||
}
|
||||
|
||||
impl<V: 'static> Palette<V> {
|
||||
pub fn new(scroll_state: ScrollState) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
input_placeholder: "Find something...",
|
||||
empty_string: "No items found.",
|
||||
items: vec![],
|
||||
default_order: OrderMethod::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn items(mut self, items: Vec<PaletteItem>) -> Self {
|
||||
self.items = items;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn placeholder(mut self, input_placeholder: &'static str) -> Self {
|
||||
self.input_placeholder = input_placeholder;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn empty_string(mut self, empty_string: &'static str) -> Self {
|
||||
self.empty_string = empty_string;
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: Hook up sort order
|
||||
pub fn default_order(mut self, default_order: OrderMethod) -> Self {
|
||||
self.default_order = default_order;
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
v_stack()
|
||||
.w_96()
|
||||
.rounded_lg()
|
||||
.fill(theme.lowest.base.default.background)
|
||||
.border()
|
||||
.border_color(theme.lowest.base.default.border)
|
||||
.child(
|
||||
v_stack()
|
||||
.gap_px()
|
||||
.child(v_stack().py_0p5().px_1().child(
|
||||
div().px_2().py_0p5().child(
|
||||
Label::new(self.input_placeholder).color(LabelColor::Placeholder),
|
||||
),
|
||||
))
|
||||
.child(div().h_px().w_full().fill(theme.lowest.base.default.border))
|
||||
.child(
|
||||
v_stack()
|
||||
.py_0p5()
|
||||
.px_1()
|
||||
.grow()
|
||||
.max_h_96()
|
||||
.overflow_y_scroll(self.scroll_state.clone())
|
||||
.children(
|
||||
vec![if self.items.is_empty() {
|
||||
Some(h_stack().justify_between().px_2().py_1().child(
|
||||
Label::new(self.empty_string).color(LabelColor::Muted),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}]
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.children(self.items.iter().map(|item| {
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.px_2()
|
||||
.py_0p5()
|
||||
.rounded_lg()
|
||||
.hover()
|
||||
.fill(theme.lowest.base.hovered.background)
|
||||
.active()
|
||||
.fill(theme.lowest.base.pressed.background)
|
||||
.child(item.clone())
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element, Clone)]
|
||||
pub struct PaletteItem {
|
||||
pub label: &'static str,
|
||||
pub sublabel: Option<&'static str>,
|
||||
pub keybinding: Option<Keybinding>,
|
||||
}
|
||||
|
||||
impl PaletteItem {
|
||||
pub fn new(label: &'static str) -> Self {
|
||||
Self {
|
||||
label,
|
||||
sublabel: None,
|
||||
keybinding: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(mut self, label: &'static str) -> Self {
|
||||
self.label = label;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sublabel<L: Into<Option<&'static str>>>(mut self, sublabel: L) -> Self {
|
||||
self.sublabel = sublabel.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn keybinding<K>(mut self, keybinding: K) -> Self
|
||||
where
|
||||
K: Into<Option<Keybinding>>,
|
||||
{
|
||||
self.keybinding = keybinding.into();
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.grow()
|
||||
.justify_between()
|
||||
.child(
|
||||
v_stack()
|
||||
.child(Label::new(self.label))
|
||||
.children(self.sublabel.map(|sublabel| Label::new(sublabel))),
|
||||
)
|
||||
.children(self.keybinding.clone())
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use gpui2::geometry::AbsoluteLength;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{theme, token, v_stack};
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub enum PanelAllowedSides {
|
||||
LeftOnly,
|
||||
RightOnly,
|
||||
BottomOnly,
|
||||
#[default]
|
||||
LeftAndRight,
|
||||
All,
|
||||
}
|
||||
|
||||
impl PanelAllowedSides {
|
||||
/// Return a `HashSet` that contains the allowable `PanelSide`s.
|
||||
pub fn allowed_sides(&self) -> HashSet<PanelSide> {
|
||||
match self {
|
||||
Self::LeftOnly => HashSet::from_iter([PanelSide::Left]),
|
||||
Self::RightOnly => HashSet::from_iter([PanelSide::Right]),
|
||||
Self::BottomOnly => HashSet::from_iter([PanelSide::Bottom]),
|
||||
Self::LeftAndRight => HashSet::from_iter([PanelSide::Left, PanelSide::Right]),
|
||||
Self::All => HashSet::from_iter([PanelSide::Left, PanelSide::Right, PanelSide::Bottom]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub enum PanelSide {
|
||||
#[default]
|
||||
Left,
|
||||
Right,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Panel<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
current_side: PanelSide,
|
||||
/// Defaults to PanelAllowedSides::LeftAndRight
|
||||
allowed_sides: PanelAllowedSides,
|
||||
initial_width: AbsoluteLength,
|
||||
width: Option<AbsoluteLength>,
|
||||
children: HackyChildren<V>,
|
||||
payload: HackyChildrenPayload,
|
||||
}
|
||||
|
||||
impl<V: 'static> Panel<V> {
|
||||
pub fn new(
|
||||
scroll_state: ScrollState,
|
||||
children: HackyChildren<V>,
|
||||
payload: HackyChildrenPayload,
|
||||
) -> Self {
|
||||
let token = token();
|
||||
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
current_side: PanelSide::default(),
|
||||
allowed_sides: PanelAllowedSides::default(),
|
||||
initial_width: token.default_panel_size,
|
||||
width: None,
|
||||
children,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initial_width(mut self, initial_width: AbsoluteLength) -> Self {
|
||||
self.initial_width = initial_width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn width(mut self, width: AbsoluteLength) -> Self {
|
||||
self.width = Some(width);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn allowed_sides(mut self, allowed_sides: PanelAllowedSides) -> Self {
|
||||
self.allowed_sides = allowed_sides;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn side(mut self, side: PanelSide) -> Self {
|
||||
let allowed_sides = self.allowed_sides.allowed_sides();
|
||||
|
||||
if allowed_sides.contains(&side) {
|
||||
self.current_side = side;
|
||||
} else {
|
||||
panic!(
|
||||
"The panel side {:?} was not added as allowed before it was set.",
|
||||
side
|
||||
);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let token = token();
|
||||
let theme = theme(cx);
|
||||
|
||||
let panel_base;
|
||||
let current_width = self.width.unwrap_or(self.initial_width);
|
||||
|
||||
match self.current_side {
|
||||
PanelSide::Left => {
|
||||
panel_base = v_stack()
|
||||
.flex_initial()
|
||||
.h_full()
|
||||
.w(current_width)
|
||||
.fill(theme.middle.base.default.background)
|
||||
.border_r()
|
||||
.border_color(theme.middle.base.default.border);
|
||||
}
|
||||
PanelSide::Right => {
|
||||
panel_base = v_stack()
|
||||
.flex_initial()
|
||||
.h_full()
|
||||
.w(current_width)
|
||||
.fill(theme.middle.base.default.background)
|
||||
.border_l()
|
||||
.border_color(theme.middle.base.default.border);
|
||||
}
|
||||
PanelSide::Bottom => {
|
||||
panel_base = v_stack()
|
||||
.flex_initial()
|
||||
.w_full()
|
||||
.h(current_width)
|
||||
.fill(theme.middle.base.default.background)
|
||||
.border_t()
|
||||
.border_color(theme.middle.base.default.border);
|
||||
}
|
||||
}
|
||||
|
||||
panel_base.children_any((self.children)(cx, self.payload.as_ref()))
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use gpui2::geometry::{Length, Size};
|
||||
use gpui2::{hsla, Hsla};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme;
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub enum SplitDirection {
|
||||
#[default]
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Pane<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
size: Size<Length>,
|
||||
fill: Hsla,
|
||||
children: HackyChildren<V>,
|
||||
payload: HackyChildrenPayload,
|
||||
}
|
||||
|
||||
impl<V: 'static> Pane<V> {
|
||||
pub fn new(
|
||||
scroll_state: ScrollState,
|
||||
size: Size<Length>,
|
||||
children: HackyChildren<V>,
|
||||
payload: HackyChildrenPayload,
|
||||
) -> Self {
|
||||
// Fill is only here for debugging purposes, remove before release
|
||||
let system_color = SystemColor::new();
|
||||
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
size,
|
||||
fill: hsla(0.3, 0.3, 0.3, 1.),
|
||||
// fill: system_color.transparent,
|
||||
children,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill(mut self, fill: Hsla) -> Self {
|
||||
self.fill = fill;
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_initial()
|
||||
.fill(self.fill)
|
||||
.w(self.size.width)
|
||||
.h(self.size.height)
|
||||
.overflow_y_scroll(self.scroll_state.clone())
|
||||
.children_any((self.children)(cx, self.payload.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct PaneGroup<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
groups: Vec<PaneGroup<V>>,
|
||||
panes: Vec<Pane<V>>,
|
||||
split_direction: SplitDirection,
|
||||
}
|
||||
|
||||
impl<V: 'static> PaneGroup<V> {
|
||||
pub fn new_groups(groups: Vec<PaneGroup<V>>, split_direction: SplitDirection) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
groups,
|
||||
panes: Vec::new(),
|
||||
split_direction,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_panes(panes: Vec<Pane<V>>, split_direction: SplitDirection) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
groups: Vec::new(),
|
||||
panes,
|
||||
split_direction,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
if !self.panes.is_empty() {
|
||||
let el = div()
|
||||
.flex()
|
||||
.flex_1()
|
||||
.gap_px()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.fill(theme.lowest.base.default.background)
|
||||
.children(self.panes.iter_mut().map(|pane| pane.render(view, cx)));
|
||||
|
||||
if self.split_direction == SplitDirection::Horizontal {
|
||||
return el;
|
||||
} else {
|
||||
return el.flex_col();
|
||||
}
|
||||
}
|
||||
|
||||
if !self.groups.is_empty() {
|
||||
let el = div()
|
||||
.flex()
|
||||
.flex_1()
|
||||
.gap_px()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.fill(theme.lowest.base.default.background)
|
||||
.children(self.groups.iter_mut().map(|group| group.render(view, cx)));
|
||||
|
||||
if self.split_direction == SplitDirection::Horizontal {
|
||||
return el;
|
||||
} else {
|
||||
return el.flex_col();
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{Avatar, Facepile, PlayerWithCallStatus};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct PlayerStack {
|
||||
player_with_call_status: PlayerWithCallStatus,
|
||||
}
|
||||
|
||||
impl PlayerStack {
|
||||
pub fn new(player_with_call_status: PlayerWithCallStatus) -> Self {
|
||||
Self {
|
||||
player_with_call_status,
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let system_color = SystemColor::new();
|
||||
let player = self.player_with_call_status.get_player();
|
||||
self.player_with_call_status.get_call_status();
|
||||
|
||||
let followers = self
|
||||
.player_with_call_status
|
||||
.get_call_status()
|
||||
.followers
|
||||
.as_ref()
|
||||
.map(|followers| followers.clone());
|
||||
|
||||
// if we have no followers return a slightly different element
|
||||
// if mic_status == muted add a red ring to avatar
|
||||
|
||||
div()
|
||||
.h_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_px()
|
||||
.justify_center()
|
||||
.child(
|
||||
div().flex().justify_center().w_full().child(
|
||||
div()
|
||||
.w_4()
|
||||
.h_0p5()
|
||||
.rounded_sm()
|
||||
.fill(player.cursor_color(cx)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.h_6()
|
||||
.pl_1()
|
||||
.rounded_lg()
|
||||
.fill(if followers.is_none() {
|
||||
system_color.transparent
|
||||
} else {
|
||||
player.selection_color(cx)
|
||||
})
|
||||
.child(Avatar::new(player.avatar_src().to_string()))
|
||||
.children(followers.map(|followers| {
|
||||
div().neg_ml_2().child(Facepile::new(followers.into_iter()))
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{
|
||||
static_project_panel_project_items, static_project_panel_single_items, theme, Input, List,
|
||||
ListHeader,
|
||||
};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct ProjectPanel<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
impl<V: 'static> ProjectPanel<V> {
|
||||
pub fn new(scroll_state: ScrollState) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
scroll_state,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.px_2()
|
||||
.fill(theme.middle.base.default.background)
|
||||
.child(
|
||||
div()
|
||||
.w_56()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.overflow_y_scroll(ScrollState::default())
|
||||
.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),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Input::new("Find something...")
|
||||
.value("buffe".to_string())
|
||||
.state(InteractionState::Focused),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{OrderMethod, Palette, PaletteItem};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct RecentProjects {
|
||||
scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
impl RecentProjects {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
scroll_state: ScrollState::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
div().child(
|
||||
Palette::new(self.scroll_state.clone())
|
||||
.items(vec![
|
||||
PaletteItem::new("zed").sublabel("~/projects/zed"),
|
||||
PaletteItem::new("saga").sublabel("~/projects/saga"),
|
||||
PaletteItem::new("journal").sublabel("~/journal"),
|
||||
PaletteItem::new("dotfiles").sublabel("~/dotfiles"),
|
||||
PaletteItem::new("zed.dev").sublabel("~/projects/zed.dev"),
|
||||
PaletteItem::new("laminar").sublabel("~/projects/laminar"),
|
||||
])
|
||||
.placeholder("Recent Projects...")
|
||||
.empty_string("No matches")
|
||||
.default_order(OrderMethod::Ascending),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme::{theme, Theme};
|
||||
use crate::{Button, Icon, IconButton, IconColor, ToolDivider};
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub enum Tool {
|
||||
#[default]
|
||||
ProjectPanel,
|
||||
CollaborationPanel,
|
||||
Terminal,
|
||||
Assistant,
|
||||
Feedback,
|
||||
Diagnostics,
|
||||
}
|
||||
|
||||
struct ToolGroup {
|
||||
active_index: Option<usize>,
|
||||
tools: Vec<Tool>,
|
||||
}
|
||||
|
||||
impl Default for ToolGroup {
|
||||
fn default() -> Self {
|
||||
ToolGroup {
|
||||
active_index: None,
|
||||
tools: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct StatusBar<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
left_tools: Option<ToolGroup>,
|
||||
right_tools: Option<ToolGroup>,
|
||||
bottom_tools: Option<ToolGroup>,
|
||||
}
|
||||
|
||||
impl<V: 'static> StatusBar<V> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
left_tools: None,
|
||||
right_tools: None,
|
||||
bottom_tools: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
|
||||
self.left_tools = {
|
||||
let mut tools = vec![tool];
|
||||
tools.extend(self.left_tools.take().unwrap_or_default().tools);
|
||||
Some(ToolGroup {
|
||||
active_index,
|
||||
tools,
|
||||
})
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn right_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
|
||||
self.right_tools = {
|
||||
let mut tools = vec![tool];
|
||||
tools.extend(self.left_tools.take().unwrap_or_default().tools);
|
||||
Some(ToolGroup {
|
||||
active_index,
|
||||
tools,
|
||||
})
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bottom_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
|
||||
self.bottom_tools = {
|
||||
let mut tools = vec![tool];
|
||||
tools.extend(self.left_tools.take().unwrap_or_default().tools);
|
||||
Some(ToolGroup {
|
||||
active_index,
|
||||
tools,
|
||||
})
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.py_0p5()
|
||||
.px_1()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.w_full()
|
||||
.fill(theme.lowest.base.default.background)
|
||||
.child(self.left_tools(&theme))
|
||||
.child(self.right_tools(&theme))
|
||||
}
|
||||
|
||||
fn left_tools(&self, theme: &Theme) -> impl Element<V> {
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(IconButton::new(Icon::FileTree).color(IconColor::Accent))
|
||||
.child(IconButton::new(Icon::Hash))
|
||||
.child(ToolDivider::new())
|
||||
.child(IconButton::new(Icon::XCircle))
|
||||
}
|
||||
fn right_tools(&self, theme: &Theme) -> impl Element<V> {
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(Button::new("116:25"))
|
||||
.child(Button::new("Rust")),
|
||||
)
|
||||
.child(ToolDivider::new())
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(IconButton::new(Icon::Copilot))
|
||||
.child(IconButton::new(Icon::Envelope)),
|
||||
)
|
||||
.child(ToolDivider::new())
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(IconButton::new(Icon::Terminal))
|
||||
.child(IconButton::new(Icon::MessageBubbles))
|
||||
.child(IconButton::new(Icon::Ai)),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{theme, Icon, IconColor, IconElement, Label, LabelColor};
|
||||
|
||||
#[derive(Element, Clone)]
|
||||
pub struct Tab {
|
||||
title: String,
|
||||
icon: Option<Icon>,
|
||||
current: bool,
|
||||
dirty: bool,
|
||||
fs_status: FileSystemStatus,
|
||||
git_status: GitStatus,
|
||||
diagnostic_status: DiagnosticStatus,
|
||||
close_side: IconSide,
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
title: "untitled".to_string(),
|
||||
icon: None,
|
||||
current: false,
|
||||
dirty: false,
|
||||
fs_status: FileSystemStatus::None,
|
||||
git_status: GitStatus::None,
|
||||
diagnostic_status: DiagnosticStatus::None,
|
||||
close_side: IconSide::Right,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current(mut self, current: bool) -> Self {
|
||||
self.current = current;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn title(mut self, title: String) -> Self {
|
||||
self.title = title;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon<I>(mut self, icon: I) -> Self
|
||||
where
|
||||
I: Into<Option<Icon>>,
|
||||
{
|
||||
self.icon = icon.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dirty(mut self, dirty: bool) -> Self {
|
||||
self.dirty = dirty;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
|
||||
self.fs_status = fs_status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn git_status(mut self, git_status: GitStatus) -> Self {
|
||||
self.git_status = git_status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
|
||||
self.diagnostic_status = diagnostic_status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn close_side(mut self, close_side: IconSide) -> Self {
|
||||
self.close_side = close_side;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict;
|
||||
let is_deleted = self.fs_status == FileSystemStatus::Deleted;
|
||||
|
||||
let label = match (self.git_status, is_deleted) {
|
||||
(_, true) | (GitStatus::Deleted, false) => Label::new(self.title.clone())
|
||||
.color(LabelColor::Hidden)
|
||||
.set_strikethrough(true),
|
||||
(GitStatus::None, false) => Label::new(self.title.clone()),
|
||||
(GitStatus::Created, false) => {
|
||||
Label::new(self.title.clone()).color(LabelColor::Created)
|
||||
}
|
||||
(GitStatus::Modified, false) => {
|
||||
Label::new(self.title.clone()).color(LabelColor::Modified)
|
||||
}
|
||||
(GitStatus::Renamed, false) => Label::new(self.title.clone()).color(LabelColor::Accent),
|
||||
(GitStatus::Conflict, false) => Label::new(self.title.clone()),
|
||||
};
|
||||
|
||||
let close_icon = IconElement::new(Icon::Close).color(IconColor::Muted);
|
||||
|
||||
div()
|
||||
.px_2()
|
||||
.py_0p5()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.fill(if self.current {
|
||||
theme.highest.base.default.background
|
||||
} else {
|
||||
theme.middle.base.default.background
|
||||
})
|
||||
.child(
|
||||
div()
|
||||
.px_1()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.children(has_fs_conflict.then(|| {
|
||||
IconElement::new(Icon::ExclamationTriangle)
|
||||
.size(crate::IconSize::Small)
|
||||
.color(IconColor::Warning)
|
||||
}))
|
||||
.children(self.icon.map(IconElement::new))
|
||||
.children(if self.close_side == IconSide::Left {
|
||||
Some(close_icon.clone())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.child(label)
|
||||
.children(if self.close_side == IconSide::Right {
|
||||
Some(close_icon)
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{theme, Icon, IconButton, Tab};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct TabBar<V: 'static> {
|
||||
view_type: PhantomData<V>,
|
||||
scroll_state: ScrollState,
|
||||
tabs: Vec<Tab>,
|
||||
}
|
||||
|
||||
impl<V: 'static> TabBar<V> {
|
||||
pub fn new(tabs: Vec<Tab>) -> Self {
|
||||
Self {
|
||||
view_type: PhantomData,
|
||||
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()
|
||||
.fill(theme.middle.base.default.background)
|
||||
// Left Side
|
||||
.child(
|
||||
div()
|
||||
.px_1()
|
||||
.flex()
|
||||
.flex_none()
|
||||
.gap_2()
|
||||
// Nav Buttons
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_px()
|
||||
.child(
|
||||
IconButton::new(Icon::ArrowLeft)
|
||||
.state(InteractionState::Enabled.if_enabled(can_navigate_back)),
|
||||
)
|
||||
.child(
|
||||
IconButton::new(Icon::ArrowRight).state(
|
||||
InteractionState::Enabled.if_enabled(can_navigate_forward),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div().w_0().flex_1().h_full().child(
|
||||
div()
|
||||
.flex()
|
||||
.overflow_x_scroll(self.scroll_state.clone())
|
||||
.children(self.tabs.clone()),
|
||||
),
|
||||
)
|
||||
// Right Side
|
||||
.child(
|
||||
div()
|
||||
.px_1()
|
||||
.flex()
|
||||
.flex_none()
|
||||
.gap_2()
|
||||
// Nav Buttons
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_px()
|
||||
.child(IconButton::new(Icon::Plus))
|
||||
.child(IconButton::new(Icon::Split)),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui2::geometry::{relative, rems, Size};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{theme, Icon, IconButton, Pane, Tab};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Terminal {}
|
||||
|
||||
impl Terminal {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&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()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.w_full()
|
||||
.child(
|
||||
// Terminal Tabs.
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.fill(theme.middle.base.default.background)
|
||||
.child(
|
||||
div().px_1().flex().flex_none().gap_2().child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_px()
|
||||
.child(
|
||||
IconButton::new(Icon::ArrowLeft).state(
|
||||
InteractionState::Enabled.if_enabled(can_navigate_back),
|
||||
),
|
||||
)
|
||||
.child(IconButton::new(Icon::ArrowRight).state(
|
||||
InteractionState::Enabled.if_enabled(can_navigate_forward),
|
||||
)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div().w_0().flex_1().h_full().child(
|
||||
div()
|
||||
.flex()
|
||||
.child(
|
||||
Tab::new()
|
||||
.title("zed — fish".to_string())
|
||||
.icon(Icon::Terminal)
|
||||
.close_side(IconSide::Right)
|
||||
.current(true),
|
||||
)
|
||||
.child(
|
||||
Tab::new()
|
||||
.title("zed — fish".to_string())
|
||||
.icon(Icon::Terminal)
|
||||
.close_side(IconSide::Right)
|
||||
.current(false),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
// Terminal Pane.
|
||||
.child(Pane::new(
|
||||
ScrollState::default(),
|
||||
Size {
|
||||
width: relative(1.).into(),
|
||||
height: rems(36.).into(),
|
||||
},
|
||||
|_, payload| {
|
||||
let theme = payload.downcast_ref::<Arc<Theme>>().unwrap();
|
||||
|
||||
vec![crate::static_data::terminal_buffer(&theme).into_any()]
|
||||
},
|
||||
Box::new(theme),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{OrderMethod, Palette, PaletteItem};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct ThemeSelector {
|
||||
scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
impl ThemeSelector {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
scroll_state: ScrollState::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
div().child(
|
||||
Palette::new(self.scroll_state.clone())
|
||||
.items(vec![
|
||||
PaletteItem::new("One Dark"),
|
||||
PaletteItem::new("Rosé Pine"),
|
||||
PaletteItem::new("Rosé Pine Moon"),
|
||||
PaletteItem::new("Sandcastle"),
|
||||
PaletteItem::new("Solarized Dark"),
|
||||
PaletteItem::new("Summercamp"),
|
||||
PaletteItem::new("Atelier Cave Light"),
|
||||
PaletteItem::new("Atelier Dune Light"),
|
||||
PaletteItem::new("Atelier Estuary Light"),
|
||||
PaletteItem::new("Atelier Forest Light"),
|
||||
PaletteItem::new("Atelier Heath Light"),
|
||||
])
|
||||
.placeholder("Select Theme...")
|
||||
.empty_string("No matches")
|
||||
.default_order(OrderMethod::Ascending),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{prelude::*, PlayerWithCallStatus};
|
||||
use crate::{
|
||||
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> {
|
||||
pub fn new(cx: &mut ViewContext<V>) -> Self {
|
||||
let is_active = Arc::new(AtomicBool::new(true));
|
||||
let active = is_active.clone();
|
||||
|
||||
cx.observe_window_activation(move |_, is_active, cx| {
|
||||
active.store(is_active, std::sync::atomic::Ordering::SeqCst);
|
||||
cx.notify();
|
||||
})
|
||||
.detach();
|
||||
|
||||
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 = if let Some(livestream) = &self.livestream {
|
||||
livestream.players.clone().into_iter()
|
||||
} else {
|
||||
vec![].into_iter()
|
||||
};
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.w_full()
|
||||
.h_8()
|
||||
.fill(theme.lowest.base.default.background)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.h_full()
|
||||
.gap_4()
|
||||
.px_2()
|
||||
.child(TrafficLights::new().window_has_focus(has_focus))
|
||||
// === Project Info === //
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(Button::new("zed"))
|
||||
.child(Button::new("nate/gpui2-ui-components")),
|
||||
)
|
||||
.children(player_list.map(|p| PlayerStack::new(p)))
|
||||
.child(IconButton::new(Icon::Plus)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.px_2()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(IconButton::new(Icon::FolderX))
|
||||
.child(IconButton::new(Icon::Close)),
|
||||
)
|
||||
.child(ToolDivider::new())
|
||||
.child(
|
||||
div()
|
||||
.px_2()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(IconButton::new(Icon::Mic))
|
||||
.child(IconButton::new(Icon::AudioOn))
|
||||
.child(IconButton::new(Icon::Screen).color(IconColor::Accent)),
|
||||
)
|
||||
.child(
|
||||
div().px_2().flex().items_center().child(
|
||||
Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
|
||||
.shape(Shape::RoundedRectangle),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ToastOrigin {
|
||||
#[default]
|
||||
Bottom,
|
||||
BottomRight,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ToastVariant {
|
||||
#[default]
|
||||
Toast,
|
||||
Status,
|
||||
}
|
||||
|
||||
/// A toast is a small, temporary window that appears to show a message to the user
|
||||
/// or indicate a required action.
|
||||
///
|
||||
/// Toasts should not persist on the screen for more than a few seconds unless
|
||||
/// they are actively showing the a process in progress.
|
||||
///
|
||||
/// Only one toast may be visible at a time.
|
||||
#[derive(Element)]
|
||||
pub struct Toast<V: 'static> {
|
||||
origin: ToastOrigin,
|
||||
children: HackyChildren<V>,
|
||||
payload: HackyChildrenPayload,
|
||||
}
|
||||
|
||||
impl<V: 'static> Toast<V> {
|
||||
pub fn new(
|
||||
origin: ToastOrigin,
|
||||
children: HackyChildren<V>,
|
||||
payload: HackyChildrenPayload,
|
||||
) -> Self {
|
||||
Self {
|
||||
origin,
|
||||
children,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let color = ThemeColor::new(cx);
|
||||
|
||||
let mut div = div();
|
||||
|
||||
if self.origin == ToastOrigin::Bottom {
|
||||
div = div.right_1_2();
|
||||
} else {
|
||||
div = div.right_4();
|
||||
}
|
||||
|
||||
div.absolute()
|
||||
.bottom_4()
|
||||
.flex()
|
||||
.py_2()
|
||||
.px_1p5()
|
||||
.min_w_40()
|
||||
.rounded_md()
|
||||
.fill(color.elevated_surface)
|
||||
.max_w_64()
|
||||
.children_any((self.children)(cx, self.payload.as_ref()))
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::theme;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct 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<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(&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(
|
||||
div()
|
||||
.flex()
|
||||
.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,78 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{theme, token, SystemColor};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum TrafficLightColor {
|
||||
Red,
|
||||
Yellow,
|
||||
Green,
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
struct TrafficLight {
|
||||
color: TrafficLightColor,
|
||||
window_has_focus: bool,
|
||||
}
|
||||
|
||||
impl TrafficLight {
|
||||
fn new(color: TrafficLightColor, window_has_focus: bool) -> Self {
|
||||
Self {
|
||||
color,
|
||||
window_has_focus,
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let system_color = SystemColor::new();
|
||||
|
||||
let fill = match (self.window_has_focus, self.color) {
|
||||
(true, TrafficLightColor::Red) => system_color.mac_os_traffic_light_red,
|
||||
(true, TrafficLightColor::Yellow) => system_color.mac_os_traffic_light_yellow,
|
||||
(true, TrafficLightColor::Green) => system_color.mac_os_traffic_light_green,
|
||||
(false, _) => theme.lowest.base.active.background,
|
||||
};
|
||||
|
||||
div().w_3().h_3().rounded_full().fill(fill)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct TrafficLights {
|
||||
window_has_focus: bool,
|
||||
}
|
||||
|
||||
impl TrafficLights {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
window_has_focus: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_has_focus(mut self, window_has_focus: bool) -> Self {
|
||||
self.window_has_focus = window_has_focus;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let token = token();
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(TrafficLight::new(
|
||||
TrafficLightColor::Red,
|
||||
self.window_has_focus,
|
||||
))
|
||||
.child(TrafficLight::new(
|
||||
TrafficLightColor::Yellow,
|
||||
self.window_has_focus,
|
||||
))
|
||||
.child(TrafficLight::new(
|
||||
TrafficLightColor::Green,
|
||||
self.window_has_focus,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::DateTime;
|
||||
use gpui2::geometry::{relative, rems, Size};
|
||||
|
||||
use crate::{
|
||||
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)]
|
||||
pub struct WorkspaceElement {
|
||||
left_panel_scroll_state: ScrollState,
|
||||
right_panel_scroll_state: ScrollState,
|
||||
tab_bar_scroll_state: ScrollState,
|
||||
bottom_panel_scroll_state: ScrollState,
|
||||
}
|
||||
|
||||
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(
|
||||
vec![
|
||||
PaneGroup::new_panes(
|
||||
vec![
|
||||
Pane::new(
|
||||
ScrollState::default(),
|
||||
Size {
|
||||
width: relative(1.).into(),
|
||||
height: temp_size,
|
||||
},
|
||||
|_, 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(),
|
||||
Size {
|
||||
width: relative(1.).into(),
|
||||
height: temp_size,
|
||||
},
|
||||
|_, _| vec![Terminal::new().into_any()],
|
||||
Box::new(()),
|
||||
),
|
||||
],
|
||||
SplitDirection::Vertical,
|
||||
),
|
||||
PaneGroup::new_panes(
|
||||
vec![Pane::new(
|
||||
ScrollState::default(),
|
||||
Size {
|
||||
width: relative(1.).into(),
|
||||
height: relative(1.).into(),
|
||||
},
|
||||
|_, 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,
|
||||
),
|
||||
],
|
||||
SplitDirection::Horizontal,
|
||||
);
|
||||
|
||||
div()
|
||||
.relative()
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.font("Zed Sans Extended")
|
||||
.gap_0()
|
||||
.justify_start()
|
||||
.items_start()
|
||||
.text_color(theme.lowest.base.default.foreground)
|
||||
.fill(theme.lowest.base.default.background)
|
||||
.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()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.overflow_hidden()
|
||||
.border_t()
|
||||
.border_b()
|
||||
.border_color(theme.lowest.base.default.border)
|
||||
.child(
|
||||
Panel::new(
|
||||
self.left_panel_scroll_state.clone(),
|
||||
|_, payload| vec![ProjectPanel::new(ScrollState::default()).into_any()],
|
||||
Box::new(()),
|
||||
)
|
||||
.side(PanelSide::Left),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.flex_1()
|
||||
.h_full()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_1()
|
||||
// CSS Hack: Flex 1 has to have a set height to properly fill the space
|
||||
// Or it will give you a height of 0
|
||||
.h_px()
|
||||
.child(root_group),
|
||||
)
|
||||
.child(
|
||||
Panel::new(
|
||||
self.bottom_panel_scroll_state.clone(),
|
||||
|_, _| vec![Terminal::new().into_any()],
|
||||
Box::new(()),
|
||||
)
|
||||
.allowed_sides(PanelAllowedSides::BottomOnly)
|
||||
.side(PanelSide::Bottom),
|
||||
),
|
||||
)
|
||||
.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())
|
||||
// An example of a toast is below
|
||||
// Currently because of stacking order this gets obscured by other elements
|
||||
|
||||
// .child(Toast::new(
|
||||
// ToastOrigin::Bottom,
|
||||
// |_, payload| {
|
||||
// let theme = payload.downcast_ref::<Arc<Theme>>().unwrap();
|
||||
|
||||
// vec![Label::new("label").into_any()]
|
||||
// },
|
||||
// Box::new(theme.clone()),
|
||||
// ))
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use gpui2::Element;
|
||||
|
||||
use crate::theme::{Theme, Themed};
|
||||
|
||||
pub trait ElementExt<V: 'static>: Element<V> {
|
||||
fn themed(self, theme: Theme) -> Themed<V, Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl<V: 'static, E: Element<V>> ElementExt<V> for E {
|
||||
fn themed(self, theme: Theme) -> Themed<V, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Themed {
|
||||
child: self,
|
||||
theme,
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
mod avatar;
|
||||
mod button;
|
||||
mod details;
|
||||
mod icon;
|
||||
mod input;
|
||||
mod label;
|
||||
mod player;
|
||||
mod stack;
|
||||
mod tool_divider;
|
||||
|
||||
pub use avatar::*;
|
||||
pub use button::*;
|
||||
pub use details::*;
|
||||
pub use icon::*;
|
||||
pub use input::*;
|
||||
pub use label::*;
|
||||
pub use player::*;
|
||||
pub use stack::*;
|
||||
pub use tool_divider::*;
|
||||
@@ -1,41 +0,0 @@
|
||||
use gpui2::elements::img;
|
||||
use gpui2::ArcCow;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme;
|
||||
|
||||
#[derive(Element, Clone)]
|
||||
pub struct Avatar {
|
||||
src: ArcCow<'static, str>,
|
||||
shape: Shape,
|
||||
}
|
||||
|
||||
impl Avatar {
|
||||
pub fn new(src: impl Into<ArcCow<'static, str>>) -> Self {
|
||||
Self {
|
||||
src: src.into(),
|
||||
shape: Shape::Circle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shape(mut self, shape: Shape) -> Self {
|
||||
self.shape = shape;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
let mut img = img();
|
||||
|
||||
if self.shape == Shape::Circle {
|
||||
img = img.rounded_full();
|
||||
} else {
|
||||
img = img.rounded_md();
|
||||
}
|
||||
|
||||
img.uri(self.src.clone())
|
||||
.size_4()
|
||||
.fill(theme.middle.warning.default.foreground)
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui2::geometry::DefiniteLength;
|
||||
use gpui2::platform::MouseButton;
|
||||
use gpui2::{EventContext, Hsla, Interactive, WindowContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{h_stack, theme, Icon, IconColor, IconElement, Label, LabelColor, LabelSize};
|
||||
|
||||
#[derive(Default, PartialEq, Clone, Copy)]
|
||||
pub enum IconPosition {
|
||||
#[default]
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, PartialEq)]
|
||||
pub enum ButtonVariant {
|
||||
#[default]
|
||||
Ghost,
|
||||
Filled,
|
||||
}
|
||||
|
||||
struct ButtonHandlers<V> {
|
||||
click: Option<Rc<dyn Fn(&mut V, &mut EventContext<V>)>>,
|
||||
}
|
||||
|
||||
impl<V> Default for ButtonHandlers<V> {
|
||||
fn default() -> Self {
|
||||
Self { click: None }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Button<V: 'static> {
|
||||
label: String,
|
||||
variant: ButtonVariant,
|
||||
state: InteractionState,
|
||||
icon: Option<Icon>,
|
||||
icon_position: Option<IconPosition>,
|
||||
width: Option<DefiniteLength>,
|
||||
handlers: ButtonHandlers<V>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Button<V> {
|
||||
pub fn new<L>(label: L) -> Self
|
||||
where
|
||||
L: Into<String>,
|
||||
{
|
||||
Self {
|
||||
label: label.into(),
|
||||
variant: Default::default(),
|
||||
state: Default::default(),
|
||||
icon: None,
|
||||
icon_position: None,
|
||||
width: Default::default(),
|
||||
handlers: ButtonHandlers::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ghost<L>(label: L) -> Self
|
||||
where
|
||||
L: Into<String>,
|
||||
{
|
||||
Self::new(label).variant(ButtonVariant::Ghost)
|
||||
}
|
||||
|
||||
pub fn variant(mut self, variant: ButtonVariant) -> Self {
|
||||
self.variant = variant;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn state(mut self, state: InteractionState) -> Self {
|
||||
self.state = state;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon(mut self, icon: Icon) -> Self {
|
||||
self.icon = Some(icon);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon_position(mut self, icon_position: IconPosition) -> Self {
|
||||
if self.icon.is_none() {
|
||||
panic!("An icon must be present if an icon_position is provided.");
|
||||
}
|
||||
self.icon_position = Some(icon_position);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn width(mut self, width: Option<DefiniteLength>) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_click(mut self, handler: impl Fn(&mut V, &mut EventContext<V>) + 'static) -> Self {
|
||||
self.handlers.click = Some(Rc::new(handler));
|
||||
self
|
||||
}
|
||||
|
||||
fn background_color(&self, cx: &mut ViewContext<V>) -> Hsla {
|
||||
let theme = theme(cx);
|
||||
let system_color = SystemColor::new();
|
||||
|
||||
match (self.variant, self.state) {
|
||||
(ButtonVariant::Ghost, InteractionState::Hovered) => {
|
||||
theme.lowest.base.hovered.background
|
||||
}
|
||||
(ButtonVariant::Ghost, InteractionState::Active) => {
|
||||
theme.lowest.base.pressed.background
|
||||
}
|
||||
(ButtonVariant::Filled, InteractionState::Enabled) => {
|
||||
theme.lowest.on.default.background
|
||||
}
|
||||
(ButtonVariant::Filled, InteractionState::Hovered) => {
|
||||
theme.lowest.on.hovered.background
|
||||
}
|
||||
(ButtonVariant::Filled, InteractionState::Active) => theme.lowest.on.pressed.background,
|
||||
(ButtonVariant::Filled, InteractionState::Disabled) => {
|
||||
theme.lowest.on.disabled.background
|
||||
}
|
||||
_ => system_color.transparent,
|
||||
}
|
||||
}
|
||||
|
||||
fn label_color(&self) -> LabelColor {
|
||||
match self.state {
|
||||
InteractionState::Disabled => LabelColor::Disabled,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn icon_color(&self) -> IconColor {
|
||||
match self.state {
|
||||
InteractionState::Disabled => IconColor::Disabled,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn border_color(&self, cx: &WindowContext) -> Hsla {
|
||||
let theme = theme(cx);
|
||||
let system_color = SystemColor::new();
|
||||
|
||||
match self.state {
|
||||
InteractionState::Focused => theme.lowest.accent.default.border,
|
||||
_ => system_color.transparent,
|
||||
}
|
||||
}
|
||||
|
||||
fn render_label(&self) -> Label {
|
||||
Label::new(self.label.clone())
|
||||
.size(LabelSize::Small)
|
||||
.color(self.label_color())
|
||||
}
|
||||
|
||||
fn render_icon(&self, icon_color: IconColor) -> Option<IconElement> {
|
||||
self.icon.map(|i| IconElement::new(i).color(icon_color))
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let icon_color = self.icon_color();
|
||||
let system_color = SystemColor::new();
|
||||
let border_color = self.border_color(cx);
|
||||
|
||||
let mut el = h_stack()
|
||||
.h_6()
|
||||
.px_1()
|
||||
.items_center()
|
||||
.rounded_md()
|
||||
.border()
|
||||
.border_color(border_color)
|
||||
.fill(self.background_color(cx));
|
||||
|
||||
match (self.icon, self.icon_position) {
|
||||
(Some(_), Some(IconPosition::Left)) => {
|
||||
el = el
|
||||
.gap_1()
|
||||
.child(self.render_label())
|
||||
.children(self.render_icon(icon_color))
|
||||
}
|
||||
(Some(_), Some(IconPosition::Right)) => {
|
||||
el = el
|
||||
.gap_1()
|
||||
.children(self.render_icon(icon_color))
|
||||
.child(self.render_label())
|
||||
}
|
||||
(_, _) => el = el.child(self.render_label()),
|
||||
}
|
||||
|
||||
if let Some(width) = self.width {
|
||||
el = el.w(width).justify_center();
|
||||
}
|
||||
|
||||
if let Some(click_handler) = self.handlers.click.clone() {
|
||||
el = el.on_mouse_down(MouseButton::Left, move |view, event, cx| {
|
||||
click_handler(view, cx);
|
||||
});
|
||||
}
|
||||
|
||||
el
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::theme;
|
||||
|
||||
#[derive(Element, Clone)]
|
||||
pub struct Details {
|
||||
text: &'static str,
|
||||
meta: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl Details {
|
||||
pub fn new(text: &'static str) -> Self {
|
||||
Self { text, meta: None }
|
||||
}
|
||||
|
||||
pub fn meta_text(mut self, meta: &'static str) -> Self {
|
||||
self.meta = Some(meta);
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
// .flex()
|
||||
// .w_full()
|
||||
.p_1()
|
||||
.gap_0p5()
|
||||
.text_xs()
|
||||
.text_color(theme.lowest.base.default.foreground)
|
||||
.child(self.text)
|
||||
.children(self.meta.map(|m| m))
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui2::elements::svg;
|
||||
use gpui2::Hsla;
|
||||
use strum::EnumIter;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme::theme;
|
||||
use crate::Theme;
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum IconSize {
|
||||
Small,
|
||||
#[default]
|
||||
Large,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum IconColor {
|
||||
#[default]
|
||||
Default,
|
||||
Muted,
|
||||
Disabled,
|
||||
Placeholder,
|
||||
Accent,
|
||||
Error,
|
||||
Warning,
|
||||
Success,
|
||||
Info,
|
||||
}
|
||||
|
||||
impl IconColor {
|
||||
pub fn color(self, theme: Arc<Theme>) -> Hsla {
|
||||
match self {
|
||||
IconColor::Default => theme.lowest.base.default.foreground,
|
||||
IconColor::Muted => theme.lowest.variant.default.foreground,
|
||||
IconColor::Disabled => theme.lowest.base.disabled.foreground,
|
||||
IconColor::Placeholder => theme.lowest.base.disabled.foreground,
|
||||
IconColor::Accent => theme.lowest.accent.default.foreground,
|
||||
IconColor::Error => theme.lowest.negative.default.foreground,
|
||||
IconColor::Warning => theme.lowest.warning.default.foreground,
|
||||
IconColor::Success => theme.lowest.positive.default.foreground,
|
||||
IconColor::Info => theme.lowest.accent.default.foreground,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone, EnumIter)]
|
||||
pub enum Icon {
|
||||
Ai,
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
ArrowUpRight,
|
||||
AudioOff,
|
||||
AudioOn,
|
||||
Bolt,
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
ChevronUp,
|
||||
Close,
|
||||
ExclamationTriangle,
|
||||
ExternalLink,
|
||||
File,
|
||||
FileGeneric,
|
||||
FileDoc,
|
||||
FileGit,
|
||||
FileLock,
|
||||
FileRust,
|
||||
FileToml,
|
||||
FileTree,
|
||||
Folder,
|
||||
FolderOpen,
|
||||
FolderX,
|
||||
#[default]
|
||||
Hash,
|
||||
InlayHint,
|
||||
MagicWand,
|
||||
MagnifyingGlass,
|
||||
Maximize,
|
||||
Menu,
|
||||
MessageBubbles,
|
||||
Mic,
|
||||
MicMute,
|
||||
Plus,
|
||||
Quote,
|
||||
Screen,
|
||||
SelectAll,
|
||||
Split,
|
||||
SplitMessage,
|
||||
Terminal,
|
||||
XCircle,
|
||||
Copilot,
|
||||
Envelope,
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
pub fn path(self) -> &'static str {
|
||||
match self {
|
||||
Icon::Ai => "icons/ai.svg",
|
||||
Icon::ArrowLeft => "icons/arrow_left.svg",
|
||||
Icon::ArrowRight => "icons/arrow_right.svg",
|
||||
Icon::ArrowUpRight => "icons/arrow_up_right.svg",
|
||||
Icon::AudioOff => "icons/speaker-off.svg",
|
||||
Icon::AudioOn => "icons/speaker-loud.svg",
|
||||
Icon::Bolt => "icons/bolt.svg",
|
||||
Icon::ChevronDown => "icons/chevron_down.svg",
|
||||
Icon::ChevronLeft => "icons/chevron_left.svg",
|
||||
Icon::ChevronRight => "icons/chevron_right.svg",
|
||||
Icon::ChevronUp => "icons/chevron_up.svg",
|
||||
Icon::Close => "icons/x.svg",
|
||||
Icon::ExclamationTriangle => "icons/warning.svg",
|
||||
Icon::ExternalLink => "icons/external_link.svg",
|
||||
Icon::File => "icons/file.svg",
|
||||
Icon::FileGeneric => "icons/file_icons/file.svg",
|
||||
Icon::FileDoc => "icons/file_icons/book.svg",
|
||||
Icon::FileGit => "icons/file_icons/git.svg",
|
||||
Icon::FileLock => "icons/file_icons/lock.svg",
|
||||
Icon::FileRust => "icons/file_icons/rust.svg",
|
||||
Icon::FileToml => "icons/file_icons/toml.svg",
|
||||
Icon::FileTree => "icons/project.svg",
|
||||
Icon::Folder => "icons/file_icons/folder.svg",
|
||||
Icon::FolderOpen => "icons/file_icons/folder_open.svg",
|
||||
Icon::FolderX => "icons/stop_sharing.svg",
|
||||
Icon::Hash => "icons/hash.svg",
|
||||
Icon::InlayHint => "icons/inlay_hint.svg",
|
||||
Icon::MagicWand => "icons/magic-wand.svg",
|
||||
Icon::MagnifyingGlass => "icons/magnifying_glass.svg",
|
||||
Icon::Maximize => "icons/maximize.svg",
|
||||
Icon::Menu => "icons/menu.svg",
|
||||
Icon::MessageBubbles => "icons/conversations.svg",
|
||||
Icon::Mic => "icons/mic.svg",
|
||||
Icon::MicMute => "icons/mic-mute.svg",
|
||||
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",
|
||||
Icon::XCircle => "icons/error.svg",
|
||||
Icon::Copilot => "icons/copilot.svg",
|
||||
Icon::Envelope => "icons/feedback.svg",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element, Clone)]
|
||||
pub struct IconElement {
|
||||
icon: Icon,
|
||||
color: IconColor,
|
||||
size: IconSize,
|
||||
}
|
||||
|
||||
impl IconElement {
|
||||
pub fn new(icon: Icon) -> Self {
|
||||
Self {
|
||||
icon,
|
||||
color: IconColor::default(),
|
||||
size: IconSize::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: IconColor) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: IconSize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let fill = self.color.color(theme);
|
||||
|
||||
let sized_svg = match self.size {
|
||||
IconSize::Small => svg().size_3p5(),
|
||||
IconSize::Large => svg().size_4(),
|
||||
};
|
||||
|
||||
sized_svg.flex_none().path(self.icon.path()).fill(fill)
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::theme;
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub enum InputVariant {
|
||||
#[default]
|
||||
Ghost,
|
||||
Filled,
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Input {
|
||||
placeholder: &'static str,
|
||||
value: String,
|
||||
state: InteractionState,
|
||||
variant: InputVariant,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn new(placeholder: &'static str) -> Self {
|
||||
Self {
|
||||
placeholder,
|
||||
value: "".to_string(),
|
||||
state: InteractionState::default(),
|
||||
variant: InputVariant::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(mut self, value: String) -> Self {
|
||||
self.value = value;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn state(mut self, state: InteractionState) -> Self {
|
||||
self.state = state;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn variant(mut self, variant: InputVariant) -> Self {
|
||||
self.variant = variant;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
let text_el;
|
||||
let text_color;
|
||||
let background_color_default;
|
||||
let background_color_active;
|
||||
|
||||
let mut border_color_default = theme.middle.base.default.border;
|
||||
let mut border_color_hover = theme.middle.base.hovered.border;
|
||||
let mut border_color_active = theme.middle.base.pressed.border;
|
||||
let border_color_focus = theme.middle.base.pressed.background;
|
||||
|
||||
match self.variant {
|
||||
InputVariant::Ghost => {
|
||||
background_color_default = theme.middle.base.default.background;
|
||||
background_color_active = theme.middle.base.active.background;
|
||||
}
|
||||
InputVariant::Filled => {
|
||||
background_color_default = theme.middle.on.default.background;
|
||||
background_color_active = theme.middle.on.active.background;
|
||||
}
|
||||
};
|
||||
|
||||
if self.state == InteractionState::Focused {
|
||||
border_color_default = theme.players[0].cursor;
|
||||
border_color_hover = theme.players[0].cursor;
|
||||
border_color_active = theme.players[0].cursor;
|
||||
}
|
||||
|
||||
if self.state == InteractionState::Focused || self.state == InteractionState::Active {
|
||||
text_el = self.value.clone();
|
||||
text_color = theme.lowest.base.default.foreground;
|
||||
} else {
|
||||
text_el = self.placeholder.to_string().clone();
|
||||
text_color = theme.lowest.base.disabled.foreground;
|
||||
}
|
||||
|
||||
div()
|
||||
.h_7()
|
||||
.w_full()
|
||||
.px_2()
|
||||
.border()
|
||||
.border_color(border_color_default)
|
||||
.fill(background_color_default)
|
||||
.hover()
|
||||
.border_color(border_color_hover)
|
||||
.active()
|
||||
.border_color(border_color_active)
|
||||
.fill(background_color_active)
|
||||
.flex()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.text_sm()
|
||||
.text_color(text_color)
|
||||
.child(text_el)
|
||||
.child(div().text_color(theme.players[0].cursor).child("|")),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
use gpui2::{Hsla, WindowContext};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::theme::theme;
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum LabelColor {
|
||||
#[default]
|
||||
Default,
|
||||
Muted,
|
||||
Created,
|
||||
Modified,
|
||||
Deleted,
|
||||
Disabled,
|
||||
Hidden,
|
||||
Placeholder,
|
||||
Accent,
|
||||
}
|
||||
|
||||
impl LabelColor {
|
||||
pub fn hsla(&self, cx: &WindowContext) -> Hsla {
|
||||
let theme = theme(cx);
|
||||
|
||||
match self {
|
||||
Self::Default => theme.middle.base.default.foreground,
|
||||
Self::Muted => theme.middle.variant.default.foreground,
|
||||
Self::Created => theme.middle.positive.default.foreground,
|
||||
Self::Modified => theme.middle.warning.default.foreground,
|
||||
Self::Deleted => theme.middle.negative.default.foreground,
|
||||
Self::Disabled => theme.middle.base.disabled.foreground,
|
||||
Self::Hidden => theme.middle.variant.default.foreground,
|
||||
Self::Placeholder => theme.middle.base.disabled.foreground,
|
||||
Self::Accent => theme.middle.accent.default.foreground,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum LabelSize {
|
||||
#[default]
|
||||
Default,
|
||||
Small,
|
||||
}
|
||||
|
||||
#[derive(Element, Clone)]
|
||||
pub struct Label {
|
||||
label: String,
|
||||
color: LabelColor,
|
||||
size: LabelSize,
|
||||
highlight_indices: Vec<usize>,
|
||||
strikethrough: bool,
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn new<L>(label: L) -> Self
|
||||
where
|
||||
L: Into<String>,
|
||||
{
|
||||
Self {
|
||||
label: label.into(),
|
||||
color: LabelColor::Default,
|
||||
size: LabelSize::Default,
|
||||
highlight_indices: Vec::new(),
|
||||
strikethrough: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: LabelColor) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: LabelSize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_highlights(mut self, indices: Vec<usize>) -> Self {
|
||||
self.highlight_indices = indices;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
|
||||
self.strikethrough = strikethrough;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
let highlight_color = theme.lowest.accent.default.foreground;
|
||||
|
||||
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
|
||||
|
||||
let mut runs: SmallVec<[Run; 8]> = SmallVec::new();
|
||||
|
||||
for (char_ix, char) in self.label.char_indices() {
|
||||
let mut color = self.color.hsla(cx);
|
||||
|
||||
if let Some(highlight_ix) = highlight_indices.peek() {
|
||||
if char_ix == *highlight_ix {
|
||||
color = highlight_color;
|
||||
|
||||
highlight_indices.next();
|
||||
}
|
||||
}
|
||||
|
||||
let last_run = runs.last_mut();
|
||||
|
||||
let start_new_run = if let Some(last_run) = last_run {
|
||||
if color == last_run.color {
|
||||
last_run.text.push(char);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if start_new_run {
|
||||
runs.push(Run {
|
||||
text: char.to_string(),
|
||||
color,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.when(self.strikethrough, |this| {
|
||||
this.relative().child(
|
||||
div()
|
||||
.absolute()
|
||||
.top_px()
|
||||
.my_auto()
|
||||
.w_full()
|
||||
.h_px()
|
||||
.fill(LabelColor::Hidden.hsla(cx)),
|
||||
)
|
||||
})
|
||||
.children(runs.into_iter().map(|run| {
|
||||
let mut div = div();
|
||||
|
||||
if self.size == LabelSize::Small {
|
||||
div = div.text_xs();
|
||||
} else {
|
||||
div = div.text_sm();
|
||||
}
|
||||
|
||||
div.text_color(run.color).child(run.text)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// A run of text that receives the same style.
|
||||
struct Run {
|
||||
pub text: String,
|
||||
pub color: Hsla,
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
use gpui2::{Hsla, ViewContext};
|
||||
|
||||
use crate::theme;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum PlayerStatus {
|
||||
#[default]
|
||||
Offline,
|
||||
Online,
|
||||
InCall,
|
||||
Away,
|
||||
DoNotDisturb,
|
||||
Invisible,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum MicStatus {
|
||||
Muted,
|
||||
#[default]
|
||||
Unmuted,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum VideoStatus {
|
||||
On,
|
||||
#[default]
|
||||
Off,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum ScreenShareStatus {
|
||||
Shared,
|
||||
#[default]
|
||||
NotShared,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlayerCallStatus {
|
||||
pub mic_status: MicStatus,
|
||||
/// Indicates if the player is currently speaking
|
||||
/// And the intensity of the volume coming through
|
||||
///
|
||||
/// 0.0 - 1.0
|
||||
pub voice_activity: f32,
|
||||
pub video_status: VideoStatus,
|
||||
pub screen_share_status: ScreenShareStatus,
|
||||
pub in_current_project: bool,
|
||||
pub disconnected: bool,
|
||||
pub following: Option<Vec<Player>>,
|
||||
pub followers: Option<Vec<Player>>,
|
||||
}
|
||||
|
||||
impl PlayerCallStatus {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mic_status: MicStatus::default(),
|
||||
voice_activity: 0.,
|
||||
video_status: VideoStatus::default(),
|
||||
screen_share_status: ScreenShareStatus::default(),
|
||||
in_current_project: true,
|
||||
disconnected: false,
|
||||
following: None,
|
||||
followers: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub struct Player {
|
||||
index: usize,
|
||||
avatar_src: String,
|
||||
username: String,
|
||||
status: PlayerStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlayerWithCallStatus {
|
||||
player: Player,
|
||||
call_status: PlayerCallStatus,
|
||||
}
|
||||
|
||||
impl PlayerWithCallStatus {
|
||||
pub fn new(player: Player, call_status: PlayerCallStatus) -> Self {
|
||||
Self {
|
||||
player,
|
||||
call_status,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_player(&self) -> &Player {
|
||||
&self.player
|
||||
}
|
||||
|
||||
pub fn get_call_status(&self) -> &PlayerCallStatus {
|
||||
&self.call_status
|
||||
}
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new(index: usize, avatar_src: String, username: String) -> Self {
|
||||
Self {
|
||||
index,
|
||||
avatar_src,
|
||||
username,
|
||||
status: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_status(mut self, status: PlayerStatus) -> Self {
|
||||
self.status = status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cursor_color<V>(&self, cx: &mut ViewContext<V>) -> Hsla {
|
||||
let theme = theme(cx);
|
||||
let index = self.index % 8;
|
||||
theme.players[self.index].cursor
|
||||
}
|
||||
|
||||
pub fn selection_color<V>(&self, cx: &mut ViewContext<V>) -> Hsla {
|
||||
let theme = theme(cx);
|
||||
let index = self.index % 8;
|
||||
theme.players[self.index].selection
|
||||
}
|
||||
|
||||
pub fn avatar_src(&self) -> &str {
|
||||
&self.avatar_src
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
use gpui2::elements::div::Div;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub trait Stack: StyleHelpers {
|
||||
/// Horizontally stacks elements.
|
||||
fn h_stack(self) -> Self {
|
||||
self.flex().flex_row().items_center()
|
||||
}
|
||||
|
||||
/// Vertically stacks elements.
|
||||
fn v_stack(self) -> Self {
|
||||
self.flex().flex_col()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Stack for Div<V> {}
|
||||
|
||||
/// Horizontally stacks elements.
|
||||
///
|
||||
/// Sets `flex()`, `flex_row()`, `items_center()`
|
||||
pub fn h_stack<V: 'static>() -> Div<V> {
|
||||
div().h_stack()
|
||||
}
|
||||
|
||||
/// Vertically stacks elements.
|
||||
///
|
||||
/// Sets `flex()`, `flex_col()`
|
||||
pub fn v_stack<V: 'static>() -> Div<V> {
|
||||
div().v_stack()
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use crate::theme;
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct ToolDivider {}
|
||||
|
||||
impl ToolDivider {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div().w_px().h_3().fill(theme.lowest.base.default.border)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
mod children;
|
||||
mod components;
|
||||
mod element_ext;
|
||||
mod elements;
|
||||
pub mod prelude;
|
||||
mod static_data;
|
||||
mod theme;
|
||||
mod tokens;
|
||||
|
||||
pub use children::*;
|
||||
pub use components::*;
|
||||
pub use element_ext::*;
|
||||
pub use elements::*;
|
||||
pub use prelude::*;
|
||||
pub use static_data::*;
|
||||
pub use tokens::*;
|
||||
|
||||
pub use crate::theme::*;
|
||||
@@ -1,274 +0,0 @@
|
||||
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, Theme};
|
||||
|
||||
use gpui2::{hsla, rgb, Hsla, WindowContext};
|
||||
use strum::EnumIter;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SystemColor {
|
||||
pub transparent: Hsla,
|
||||
pub mac_os_traffic_light_red: Hsla,
|
||||
pub mac_os_traffic_light_yellow: Hsla,
|
||||
pub mac_os_traffic_light_green: Hsla,
|
||||
}
|
||||
|
||||
impl SystemColor {
|
||||
pub fn new() -> SystemColor {
|
||||
SystemColor {
|
||||
transparent: hsla(0.0, 0.0, 0.0, 0.0),
|
||||
mac_os_traffic_light_red: rgb::<Hsla>(0xEC695E),
|
||||
mac_os_traffic_light_yellow: rgb::<Hsla>(0xF4BF4F),
|
||||
mac_os_traffic_light_green: rgb::<Hsla>(0x62C554),
|
||||
}
|
||||
}
|
||||
pub fn color(&self) -> Hsla {
|
||||
self.transparent
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ThemeColor {
|
||||
pub border: Hsla,
|
||||
pub border_variant: Hsla,
|
||||
/// The background color of an elevated surface, like a modal, tooltip or toast.
|
||||
pub elevated_surface: Hsla,
|
||||
}
|
||||
|
||||
impl ThemeColor {
|
||||
pub fn new(cx: &WindowContext) -> Self {
|
||||
let theme = theme(cx);
|
||||
|
||||
Self {
|
||||
border: theme.lowest.base.default.border,
|
||||
border_variant: theme.lowest.variant.default.border,
|
||||
elevated_surface: theme.middle.base.default.background,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, EnumIter, Clone, Copy)]
|
||||
pub enum HighlightColor {
|
||||
#[default]
|
||||
Default,
|
||||
Comment,
|
||||
String,
|
||||
Function,
|
||||
Keyword,
|
||||
}
|
||||
|
||||
impl HighlightColor {
|
||||
pub fn hsla(&self, theme: &Theme) -> Hsla {
|
||||
let system_color = SystemColor::new();
|
||||
|
||||
match self {
|
||||
Self::Default => theme
|
||||
.syntax
|
||||
.get("primary")
|
||||
.expect("no theme.syntax.primary")
|
||||
.clone(),
|
||||
Self::Comment => theme
|
||||
.syntax
|
||||
.get("comment")
|
||||
.expect("no theme.syntax.comment")
|
||||
.clone(),
|
||||
Self::String => theme
|
||||
.syntax
|
||||
.get("string")
|
||||
.expect("no theme.syntax.string")
|
||||
.clone(),
|
||||
Self::Function => theme
|
||||
.syntax
|
||||
.get("function")
|
||||
.expect("no theme.syntax.function")
|
||||
.clone(),
|
||||
Self::Keyword => theme
|
||||
.syntax
|
||||
.get("keyword")
|
||||
.expect("no theme.syntax.keyword")
|
||||
.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum FileSystemStatus {
|
||||
#[default]
|
||||
None,
|
||||
Conflict,
|
||||
Deleted,
|
||||
}
|
||||
|
||||
impl FileSystemStatus {
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::None => "None".to_string(),
|
||||
Self::Conflict => "Conflict".to_string(),
|
||||
Self::Deleted => "Deleted".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum GitStatus {
|
||||
#[default]
|
||||
None,
|
||||
Created,
|
||||
Modified,
|
||||
Deleted,
|
||||
Conflict,
|
||||
Renamed,
|
||||
}
|
||||
|
||||
impl GitStatus {
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::None => "None".to_string(),
|
||||
Self::Created => "Created".to_string(),
|
||||
Self::Modified => "Modified".to_string(),
|
||||
Self::Deleted => "Deleted".to_string(),
|
||||
Self::Conflict => "Conflict".to_string(),
|
||||
Self::Renamed => "Renamed".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hsla(&self, cx: &WindowContext) -> Hsla {
|
||||
let theme = theme(cx);
|
||||
let system_color = SystemColor::new();
|
||||
|
||||
match self {
|
||||
Self::None => system_color.transparent,
|
||||
Self::Created => theme.lowest.positive.default.foreground,
|
||||
Self::Modified => theme.lowest.warning.default.foreground,
|
||||
Self::Deleted => theme.lowest.negative.default.foreground,
|
||||
Self::Conflict => theme.lowest.warning.default.foreground,
|
||||
Self::Renamed => theme.lowest.accent.default.foreground,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum DiagnosticStatus {
|
||||
#[default]
|
||||
None,
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum IconSide {
|
||||
#[default]
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum OrderMethod {
|
||||
#[default]
|
||||
Ascending,
|
||||
Descending,
|
||||
MostRecent,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum Shape {
|
||||
#[default]
|
||||
Circle,
|
||||
RoundedRectangle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum DisclosureControlVisibility {
|
||||
#[default]
|
||||
OnHover,
|
||||
Always,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone, EnumIter, strum::Display)]
|
||||
pub enum InteractionState {
|
||||
#[default]
|
||||
Enabled,
|
||||
Hovered,
|
||||
Active,
|
||||
Focused,
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl InteractionState {
|
||||
pub fn if_enabled(&self, enabled: bool) -> Self {
|
||||
if enabled {
|
||||
*self
|
||||
} else {
|
||||
InteractionState::Disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub enum SelectedState {
|
||||
#[default]
|
||||
Unselected,
|
||||
PartiallySelected,
|
||||
Selected,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Toggleable {
|
||||
Toggleable(ToggleState),
|
||||
#[default]
|
||||
NotToggleable,
|
||||
}
|
||||
|
||||
impl Toggleable {
|
||||
pub fn is_toggled(&self) -> bool {
|
||||
match self {
|
||||
Self::Toggleable(ToggleState::Toggled) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ToggleState> for Toggleable {
|
||||
fn from(state: ToggleState) -> Self {
|
||||
Self::Toggleable(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ToggleState {
|
||||
/// The "on" state of a toggleable element.
|
||||
///
|
||||
/// Example:
|
||||
/// - A collasable list that is currently expanded
|
||||
/// - A toggle button that is currently on.
|
||||
Toggled,
|
||||
/// The "off" state of a toggleable element.
|
||||
///
|
||||
/// Example:
|
||||
/// - A collasable list that is currently collapsed
|
||||
/// - A toggle button that is currently off.
|
||||
#[default]
|
||||
NotToggled,
|
||||
}
|
||||
|
||||
impl From<Toggleable> for ToggleState {
|
||||
fn from(toggleable: Toggleable) -> Self {
|
||||
match toggleable {
|
||||
Toggleable::Toggleable(state) => state,
|
||||
Toggleable::NotToggleable => ToggleState::NotToggled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for ToggleState {
|
||||
fn from(toggled: bool) -> Self {
|
||||
if toggled {
|
||||
ToggleState::Toggled
|
||||
} else {
|
||||
ToggleState::NotToggled
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,966 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{
|
||||
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<Tab> {
|
||||
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<Tab> {
|
||||
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<Tab> {
|
||||
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<Tab> {
|
||||
vec![Tab::new().git_status(GitStatus::Created).current(true)]
|
||||
}
|
||||
|
||||
pub fn static_players() -> Vec<Player> {
|
||||
vec![
|
||||
Player::new(
|
||||
0,
|
||||
"https://avatars.githubusercontent.com/u/1714999?v=4".into(),
|
||||
"nathansobo".into(),
|
||||
),
|
||||
Player::new(
|
||||
1,
|
||||
"https://avatars.githubusercontent.com/u/326587?v=4".into(),
|
||||
"maxbrunsfeld".into(),
|
||||
),
|
||||
Player::new(
|
||||
2,
|
||||
"https://avatars.githubusercontent.com/u/482957?v=4".into(),
|
||||
"as-cii".into(),
|
||||
),
|
||||
Player::new(
|
||||
3,
|
||||
"https://avatars.githubusercontent.com/u/1714999?v=4".into(),
|
||||
"iamnbutler".into(),
|
||||
),
|
||||
Player::new(
|
||||
4,
|
||||
"https://avatars.githubusercontent.com/u/1486634?v=4".into(),
|
||||
"maxdeviant".into(),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PlayerData {
|
||||
pub url: String,
|
||||
pub name: String,
|
||||
}
|
||||
pub fn static_player_data() -> Vec<PlayerData> {
|
||||
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<PlayerData>) -> Vec<Player> {
|
||||
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<PlayerData>) -> Player {
|
||||
Player::new(1, data[0].url.clone(), data[0].name.clone())
|
||||
}
|
||||
pub fn static_player_2(data: &Vec<PlayerData>) -> Player {
|
||||
Player::new(2, data[1].url.clone(), data[1].name.clone())
|
||||
}
|
||||
pub fn static_player_3(data: &Vec<PlayerData>) -> Player {
|
||||
Player::new(3, data[2].url.clone(), data[2].name.clone())
|
||||
}
|
||||
pub fn static_player_4(data: &Vec<PlayerData>) -> Player {
|
||||
Player::new(4, data[3].url.clone(), data[3].name.clone())
|
||||
}
|
||||
pub fn static_player_5(data: &Vec<PlayerData>) -> Player {
|
||||
Player::new(5, data[4].url.clone(), data[4].name.clone())
|
||||
}
|
||||
pub fn static_player_6(data: &Vec<PlayerData>) -> Player {
|
||||
Player::new(6, data[5].url.clone(), data[5].name.clone())
|
||||
}
|
||||
pub fn static_player_7(data: &Vec<PlayerData>) -> Player {
|
||||
Player::new(7, data[6].url.clone(), data[6].name.clone())
|
||||
}
|
||||
pub fn static_player_8(data: &Vec<PlayerData>) -> Player {
|
||||
Player::new(8, data[7].url.clone(), data[7].name.clone())
|
||||
}
|
||||
pub fn static_player_9(data: &Vec<PlayerData>) -> Player {
|
||||
Player::new(9, data[8].url.clone(), data[8].name.clone())
|
||||
}
|
||||
pub fn static_player_10(data: &Vec<PlayerData>) -> 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<Vec<Player>>,
|
||||
) -> 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<PlayerWithCallStatus> {
|
||||
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<PlayerWithCallStatus> {
|
||||
let players = static_players();
|
||||
let mut player_0_status = PlayerCallStatus::new();
|
||||
let player_1_status = PlayerCallStatus::new();
|
||||
let player_2_status = PlayerCallStatus::new();
|
||||
let mut player_3_status = PlayerCallStatus::new();
|
||||
let mut player_4_status = PlayerCallStatus::new();
|
||||
|
||||
player_0_status.screen_share_status = ScreenShareStatus::Shared;
|
||||
player_0_status.followers = Some(vec![players[1].clone(), players[3].clone()]);
|
||||
|
||||
player_3_status.voice_activity = 0.5;
|
||||
player_4_status.mic_status = MicStatus::Muted;
|
||||
player_4_status.in_current_project = false;
|
||||
|
||||
vec![
|
||||
PlayerWithCallStatus::new(players[0].clone(), player_0_status),
|
||||
PlayerWithCallStatus::new(players[1].clone(), player_1_status),
|
||||
PlayerWithCallStatus::new(players[2].clone(), player_2_status),
|
||||
PlayerWithCallStatus::new(players[3].clone(), player_3_status),
|
||||
PlayerWithCallStatus::new(players[4].clone(), player_4_status),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn static_project_panel_project_items() -> Vec<ListItem> {
|
||||
vec![
|
||||
ListEntry::new(Label::new("zed"))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(0)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new(".cargo"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new(".config"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new(".git").color(LabelColor::Hidden))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new(".cargo"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new(".idea").color(LabelColor::Hidden))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("assets"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("crates"))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(1)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new("activity_indicator"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("ai"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("audio"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("auto_update"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("breadcrumbs"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("call"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("sqlez").color(LabelColor::Modified))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2)
|
||||
.set_toggle(ToggleState::NotToggled),
|
||||
ListEntry::new(Label::new("gpui2"))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(2)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new("src"))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(3)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new("derive_element.rs"))
|
||||
.left_icon(Icon::FileRust.into())
|
||||
.indent_level(4),
|
||||
ListEntry::new(Label::new("storybook").color(LabelColor::Modified))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(1)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new("docs").color(LabelColor::Default))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new("src").color(LabelColor::Modified))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(3)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new("ui").color(LabelColor::Modified))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(4)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new("component").color(LabelColor::Created))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(5)
|
||||
.set_toggle(ToggleState::Toggled),
|
||||
ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default))
|
||||
.left_icon(Icon::FileRust.into())
|
||||
.indent_level(6),
|
||||
ListEntry::new(Label::new("follow_group.rs").color(LabelColor::Default))
|
||||
.left_icon(Icon::FileRust.into())
|
||||
.indent_level(6),
|
||||
ListEntry::new(Label::new("list_item.rs").color(LabelColor::Created))
|
||||
.left_icon(Icon::FileRust.into())
|
||||
.indent_level(6),
|
||||
ListEntry::new(Label::new("tab.rs").color(LabelColor::Default))
|
||||
.left_icon(Icon::FileRust.into())
|
||||
.indent_level(6),
|
||||
ListEntry::new(Label::new("target").color(LabelColor::Hidden))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new(".dockerignore"))
|
||||
.left_icon(Icon::FileGeneric.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new(".DS_Store").color(LabelColor::Hidden))
|
||||
.left_icon(Icon::FileGeneric.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("Cargo.lock"))
|
||||
.left_icon(Icon::FileLock.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("Cargo.toml"))
|
||||
.left_icon(Icon::FileToml.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("Dockerfile"))
|
||||
.left_icon(Icon::FileGeneric.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("Procfile"))
|
||||
.left_icon(Icon::FileGeneric.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("README.md"))
|
||||
.left_icon(Icon::FileDoc.into())
|
||||
.indent_level(1),
|
||||
]
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn static_project_panel_single_items() -> Vec<ListItem> {
|
||||
vec![
|
||||
ListEntry::new(Label::new("todo.md"))
|
||||
.left_icon(Icon::FileDoc.into())
|
||||
.indent_level(0),
|
||||
ListEntry::new(Label::new("README.md"))
|
||||
.left_icon(Icon::FileDoc.into())
|
||||
.indent_level(0),
|
||||
ListEntry::new(Label::new("config.json"))
|
||||
.left_icon(Icon::FileGeneric.into())
|
||||
.indent_level(0),
|
||||
]
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn static_collab_panel_current_call() -> Vec<ListItem> {
|
||||
vec![
|
||||
ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"),
|
||||
ListEntry::new(Label::new("nathansobo"))
|
||||
.left_avatar("http://github.com/nathansobo.png?s=50"),
|
||||
ListEntry::new(Label::new("maxbrunsfeld"))
|
||||
.left_avatar("http://github.com/maxbrunsfeld.png?s=50"),
|
||||
]
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn static_collab_panel_channels() -> Vec<ListItem> {
|
||||
vec![
|
||||
ListEntry::new(Label::new("zed"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(0),
|
||||
ListEntry::new(Label::new("community"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("dashboards"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("feedback"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("teams-in-channels-alpha"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("current-projects"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("codegen"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("gpui2"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("livestreaming"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("open-source"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("replace"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("semantic-index"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("vim"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
ListEntry::new(Label::new("web-tech"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
.size(ListEntrySize::Medium)
|
||||
.indent_level(2),
|
||||
]
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn example_editor_actions() -> Vec<PaletteItem> {
|
||||
vec![
|
||||
PaletteItem::new("New File").keybinding(Keybinding::new(
|
||||
"N".to_string(),
|
||||
ModifierKeys::new().control(true),
|
||||
)),
|
||||
PaletteItem::new("Open File").keybinding(Keybinding::new(
|
||||
"O".to_string(),
|
||||
ModifierKeys::new().control(true),
|
||||
)),
|
||||
PaletteItem::new("Save File").keybinding(Keybinding::new(
|
||||
"S".to_string(),
|
||||
ModifierKeys::new().control(true),
|
||||
)),
|
||||
PaletteItem::new("Cut").keybinding(Keybinding::new(
|
||||
"X".to_string(),
|
||||
ModifierKeys::new().control(true),
|
||||
)),
|
||||
PaletteItem::new("Copy").keybinding(Keybinding::new(
|
||||
"C".to_string(),
|
||||
ModifierKeys::new().control(true),
|
||||
)),
|
||||
PaletteItem::new("Paste").keybinding(Keybinding::new(
|
||||
"V".to_string(),
|
||||
ModifierKeys::new().control(true),
|
||||
)),
|
||||
PaletteItem::new("Undo").keybinding(Keybinding::new(
|
||||
"Z".to_string(),
|
||||
ModifierKeys::new().control(true),
|
||||
)),
|
||||
PaletteItem::new("Redo").keybinding(Keybinding::new(
|
||||
"Z".to_string(),
|
||||
ModifierKeys::new().control(true).shift(true),
|
||||
)),
|
||||
PaletteItem::new("Find").keybinding(Keybinding::new(
|
||||
"F".to_string(),
|
||||
ModifierKeys::new().control(true),
|
||||
)),
|
||||
PaletteItem::new("Replace").keybinding(Keybinding::new(
|
||||
"R".to_string(),
|
||||
ModifierKeys::new().control(true),
|
||||
)),
|
||||
PaletteItem::new("Jump to Line"),
|
||||
PaletteItem::new("Select All"),
|
||||
PaletteItem::new("Deselect All"),
|
||||
PaletteItem::new("Switch Document"),
|
||||
PaletteItem::new("Insert Line Below"),
|
||||
PaletteItem::new("Insert Line Above"),
|
||||
PaletteItem::new("Move Line Up"),
|
||||
PaletteItem::new("Move Line Down"),
|
||||
PaletteItem::new("Toggle Comment"),
|
||||
PaletteItem::new("Delete Line"),
|
||||
]
|
||||
}
|
||||
|
||||
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_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(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_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<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(theme: &Theme) -> 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<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,
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui2::color::Hsla;
|
||||
use gpui2::element::Element;
|
||||
use gpui2::{serde_json, AppContext, IntoElement, Vector2F, ViewContext, WindowContext};
|
||||
use serde::de::Visitor;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use theme::ThemeSettings;
|
||||
|
||||
#[derive(Deserialize, Clone, Default, Debug)]
|
||||
pub struct Theme {
|
||||
pub name: String,
|
||||
pub is_light: bool,
|
||||
pub lowest: Layer,
|
||||
pub middle: Layer,
|
||||
pub highest: Layer,
|
||||
pub popover_shadow: Shadow,
|
||||
pub modal_shadow: Shadow,
|
||||
#[serde(deserialize_with = "deserialize_player_colors")]
|
||||
pub players: Vec<PlayerColors>,
|
||||
#[serde(deserialize_with = "deserialize_syntax_colors")]
|
||||
pub syntax: HashMap<String, Hsla>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Default, Debug)]
|
||||
pub struct Layer {
|
||||
pub base: StyleSet,
|
||||
pub variant: StyleSet,
|
||||
pub on: StyleSet,
|
||||
pub accent: StyleSet,
|
||||
pub positive: StyleSet,
|
||||
pub warning: StyleSet,
|
||||
pub negative: StyleSet,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Default, Debug)]
|
||||
pub struct StyleSet {
|
||||
#[serde(rename = "default")]
|
||||
pub default: ContainerColors,
|
||||
pub hovered: ContainerColors,
|
||||
pub pressed: ContainerColors,
|
||||
pub active: ContainerColors,
|
||||
pub disabled: ContainerColors,
|
||||
pub inverted: ContainerColors,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Default, Debug)]
|
||||
pub struct ContainerColors {
|
||||
pub background: Hsla,
|
||||
pub foreground: Hsla,
|
||||
pub border: Hsla,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Default, Debug)]
|
||||
pub struct PlayerColors {
|
||||
pub selection: Hsla,
|
||||
pub cursor: Hsla,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Default, Debug)]
|
||||
pub struct Shadow {
|
||||
pub blur: u8,
|
||||
pub color: Hsla,
|
||||
pub offset: Vec<u8>,
|
||||
}
|
||||
|
||||
fn deserialize_player_colors<'de, D>(deserializer: D) -> Result<Vec<PlayerColors>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct PlayerArrayVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for PlayerArrayVisitor {
|
||||
type Value = Vec<PlayerColors>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("an object with integer keys")
|
||||
}
|
||||
|
||||
fn visit_map<A: serde::de::MapAccess<'de>>(
|
||||
self,
|
||||
mut map: A,
|
||||
) -> Result<Self::Value, A::Error> {
|
||||
let mut players = Vec::with_capacity(8);
|
||||
while let Some((key, value)) = map.next_entry::<usize, PlayerColors>()? {
|
||||
if key < 8 {
|
||||
players.push(value);
|
||||
} else {
|
||||
return Err(serde::de::Error::invalid_value(
|
||||
serde::de::Unexpected::Unsigned(key as u64),
|
||||
&"a key in range 0..7",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(players)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_map(PlayerArrayVisitor)
|
||||
}
|
||||
|
||||
fn deserialize_syntax_colors<'de, D>(deserializer: D) -> Result<HashMap<String, Hsla>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct ColorWrapper {
|
||||
color: Hsla,
|
||||
}
|
||||
|
||||
struct SyntaxVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for SyntaxVisitor {
|
||||
type Value = HashMap<String, Hsla>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a map with keys and objects with a single color field as values")
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut map: M) -> Result<HashMap<String, Hsla>, M::Error>
|
||||
where
|
||||
M: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut result = HashMap::new();
|
||||
while let Some(key) = map.next_key()? {
|
||||
let wrapper: ColorWrapper = map.next_value()?; // Deserialize values as Hsla
|
||||
result.insert(key, wrapper.color);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_map(SyntaxVisitor)
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Themed<V: 'static, E: Element<V>> {
|
||||
pub(crate) theme: Theme,
|
||||
pub(crate) child: E,
|
||||
pub(crate) view_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<V: 'static, E: Element<V>> Element<V> for Themed<V, E> {
|
||||
type PaintState = E::PaintState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> anyhow::Result<(gpui2::LayoutId, Self::PaintState)>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
cx.push_theme(self.theme.clone());
|
||||
let result = self.child.layout(view, cx);
|
||||
cx.pop_theme();
|
||||
result
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
parent_origin: Vector2F,
|
||||
layout: &gpui2::Layout,
|
||||
state: &mut Self::PaintState,
|
||||
cx: &mut ViewContext<V>,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
cx.push_theme(self.theme.clone());
|
||||
self.child.paint(view, parent_origin, layout, state, cx);
|
||||
cx.pop_theme();
|
||||
}
|
||||
}
|
||||
|
||||
fn preferred_theme<V: 'static>(cx: &AppContext) -> Theme {
|
||||
settings::get::<ThemeSettings>(cx)
|
||||
.theme
|
||||
.deserialized_base_theme
|
||||
.lock()
|
||||
.get_or_insert_with(|| {
|
||||
let theme: Theme =
|
||||
serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
|
||||
.unwrap();
|
||||
Box::new(theme)
|
||||
})
|
||||
.downcast_ref::<Theme>()
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn theme(cx: &WindowContext) -> Arc<Theme> {
|
||||
cx.theme::<Theme>()
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
use gpui2::geometry::AbsoluteLength;
|
||||
use gpui2::{hsla, Hsla};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Token {
|
||||
pub list_indent_depth: AbsoluteLength,
|
||||
pub default_panel_size: AbsoluteLength,
|
||||
pub state_hover_background: Hsla,
|
||||
pub state_active_background: Hsla,
|
||||
}
|
||||
|
||||
impl Default for Token {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
list_indent_depth: AbsoluteLength::Rems(0.5),
|
||||
default_panel_size: AbsoluteLength::Rems(16.),
|
||||
state_hover_background: hsla(0.0, 0.0, 0.0, 0.08),
|
||||
state_active_background: hsla(0.0, 0.0, 0.0, 0.16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn token() -> Token {
|
||||
Token::default()
|
||||
}
|
||||
Reference in New Issue
Block a user