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:
Marshall Bowers 2023-10-12 17:40:20 -04:00 committed by GitHub
parent bac43ae38e
commit 45f3a98359
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
91 changed files with 0 additions and 10580 deletions

View file

@ -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.))
}
}

View file

@ -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<_>>(),
),
)
}
}

View file

@ -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)
}
}

View file

@ -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())))
}
}

View file

@ -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),
)
}
}

View file

@ -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),
)
}
}

View file

@ -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())
}
}

View file

@ -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())
}
}

View file

@ -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)
}
}

View file

@ -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))
}
}

View file

@ -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
}
}

View file

@ -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),
)
}
}

View file

@ -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)
}
}

View file

@ -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)
}))
}
}

View file

@ -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())
}
}

View file

@ -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()))
}
}

View file

@ -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!()
}
}

View file

@ -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()))
})),
)
}
}

View file

@ -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),
)
}
}

View file

@ -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),
)
}
}

View file

@ -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)),
)
}
}

View file

@ -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
}),
)
}
}

View file

@ -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)),
),
)
}
}

View file

@ -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),
))
}
}

View file

@ -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),
)
}
}

View file

@ -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),
),
),
)
}
}

View file

@ -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()))
}
}

View file

@ -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())),
)
}
}

View file

@ -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,
))
}
}

View file

@ -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()),
// ))
}
}