Clean out UI
This commit is contained in:
parent
b4275008f9
commit
205607a9cd
37 changed files with 530 additions and 1876 deletions
|
@ -4,6 +4,10 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
name = "ui2"
|
||||
path = "src/ui2.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
chrono = "0.4"
|
||||
|
|
|
@ -2,56 +2,32 @@ mod avatar;
|
|||
mod button;
|
||||
mod checkbox;
|
||||
mod context_menu;
|
||||
mod details;
|
||||
mod divider;
|
||||
mod elevated_surface;
|
||||
mod facepile;
|
||||
mod icon;
|
||||
mod icon_button;
|
||||
mod indicator;
|
||||
mod input;
|
||||
mod keybinding;
|
||||
mod label;
|
||||
mod list;
|
||||
mod modal;
|
||||
mod notification_toast;
|
||||
mod palette;
|
||||
mod panel;
|
||||
mod player;
|
||||
mod player_stack;
|
||||
mod slot;
|
||||
mod stack;
|
||||
mod tab;
|
||||
mod toast;
|
||||
mod stories;
|
||||
mod toggle;
|
||||
mod tool_divider;
|
||||
mod tooltip;
|
||||
|
||||
pub use avatar::*;
|
||||
pub use button::*;
|
||||
pub use checkbox::*;
|
||||
pub use context_menu::*;
|
||||
pub use details::*;
|
||||
pub use divider::*;
|
||||
pub use elevated_surface::*;
|
||||
pub use facepile::*;
|
||||
pub use icon::*;
|
||||
pub use icon_button::*;
|
||||
pub use indicator::*;
|
||||
pub use input::*;
|
||||
pub use keybinding::*;
|
||||
pub use label::*;
|
||||
pub use list::*;
|
||||
pub use modal::*;
|
||||
pub use notification_toast::*;
|
||||
pub use palette::*;
|
||||
pub use panel::*;
|
||||
pub use player::*;
|
||||
pub use player_stack::*;
|
||||
pub use slot::*;
|
||||
pub use stack::*;
|
||||
pub use tab::*;
|
||||
pub use toast::*;
|
||||
pub use stories::*;
|
||||
pub use toggle::*;
|
||||
pub use tool_divider::*;
|
||||
pub use tooltip::*;
|
||||
|
|
|
@ -39,31 +39,3 @@ impl Avatar {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::Story;
|
||||
use gpui::{Div, Render};
|
||||
|
||||
pub struct AvatarStory;
|
||||
|
||||
impl Render for AvatarStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Avatar>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(Avatar::new(
|
||||
"https://avatars.githubusercontent.com/u/1714999?v=4",
|
||||
))
|
||||
.child(Avatar::new(
|
||||
"https://avatars.githubusercontent.com/u/326587?v=4",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -231,171 +231,3 @@ impl ButtonGroup {
|
|||
Self { buttons }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{h_stack, v_stack, Story, TextColor};
|
||||
use gpui::{rems, Div, Render};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
pub struct ButtonStory;
|
||||
|
||||
impl Render for ButtonStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let states = InteractionState::iter();
|
||||
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Button>(cx))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_8()
|
||||
.child(
|
||||
div()
|
||||
.child(Story::label(cx, "Ghost (Default)"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label").variant(ButtonVariant::Ghost), // .state(state),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Ghost – Left Icon"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Left), // .state(state),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Ghost – Right Icon"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Right), // .state(state),
|
||||
)
|
||||
}))),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.child(Story::label(cx, "Filled"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label").variant(ButtonVariant::Filled), // .state(state),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Filled – Left Button"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Left), // .state(state),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Filled – Right Button"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Right), // .state(state),
|
||||
)
|
||||
}))),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.child(Story::label(cx, "Fixed With"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
// .state(state)
|
||||
.width(Some(rems(6.).into())),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Fixed With – Left Icon"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
// .state(state)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Left)
|
||||
.width(Some(rems(6.).into())),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Fixed With – Right Icon"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
// .state(state)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Right)
|
||||
.width(Some(rems(6.).into())),
|
||||
)
|
||||
}))),
|
||||
),
|
||||
)
|
||||
.child(Story::label(cx, "Button with `on_click`"))
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.on_click(|_, cx| println!("Button clicked.")),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -283,63 +283,3 @@ impl Checkbox {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{h_stack, Story};
|
||||
use gpui::{Div, Render, ViewContext};
|
||||
|
||||
pub struct CheckboxStory;
|
||||
|
||||
impl Render for CheckboxStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Checkbox>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(
|
||||
h_stack()
|
||||
.p_2()
|
||||
.gap_2()
|
||||
.rounded_md()
|
||||
.border()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(Checkbox::new("checkbox-enabled", Selection::Unselected))
|
||||
.child(Checkbox::new(
|
||||
"checkbox-intermediate",
|
||||
Selection::Indeterminate,
|
||||
))
|
||||
.child(Checkbox::new("checkbox-selected", Selection::Selected)),
|
||||
)
|
||||
.child(Story::label(cx, "Disabled"))
|
||||
.child(
|
||||
h_stack()
|
||||
.p_2()
|
||||
.gap_2()
|
||||
.rounded_md()
|
||||
.border()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(
|
||||
Checkbox::new("checkbox-disabled", Selection::Unselected)
|
||||
.disabled(true),
|
||||
)
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"checkbox-disabled-intermediate",
|
||||
Selection::Indeterminate,
|
||||
)
|
||||
.disabled(true),
|
||||
)
|
||||
.child(
|
||||
Checkbox::new("checkbox-disabled-selected", Selection::Selected)
|
||||
.disabled(true),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,116 +297,3 @@ impl<M: ManagedView> RenderOnce for MenuHandle<M> {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{story::Story, Label};
|
||||
use gpui::{actions, Div, Render};
|
||||
|
||||
actions!(PrintCurrentDate, PrintBestFood);
|
||||
|
||||
fn build_menu(cx: &mut WindowContext, header: impl Into<SharedString>) -> View<ContextMenu> {
|
||||
ContextMenu::build(cx, |menu, _| {
|
||||
menu.header(header)
|
||||
.separator()
|
||||
.entry(
|
||||
ListItem::new("Print current time", Label::new("Print current time")),
|
||||
|v, cx| {
|
||||
println!("dispatching PrintCurrentTime action");
|
||||
cx.dispatch_action(PrintCurrentDate.boxed_clone())
|
||||
},
|
||||
)
|
||||
.entry(
|
||||
ListItem::new("Print best food", Label::new("Print best food")),
|
||||
|v, cx| cx.dispatch_action(PrintBestFood.boxed_clone()),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub struct ContextMenuStory;
|
||||
|
||||
impl Render for ContextMenuStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.on_action(|_: &PrintCurrentDate, _| {
|
||||
println!("printing unix time!");
|
||||
if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
|
||||
println!("Current Unix time is {:?}", unix_time.as_secs());
|
||||
}
|
||||
})
|
||||
.on_action(|_: &PrintBestFood, _| {
|
||||
println!("burrito");
|
||||
})
|
||||
.flex()
|
||||
.flex_row()
|
||||
.justify_between()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.justify_between()
|
||||
.child(
|
||||
menu_handle("test2")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"TOP LEFT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
})
|
||||
.menu(move |cx| build_menu(cx, "top left")),
|
||||
)
|
||||
.child(
|
||||
menu_handle("test1")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"BOTTOM LEFT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
})
|
||||
.anchor(AnchorCorner::BottomLeft)
|
||||
.attach(AnchorCorner::TopLeft)
|
||||
.menu(move |cx| build_menu(cx, "bottom left")),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.justify_between()
|
||||
.child(
|
||||
menu_handle("test3")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"TOP RIGHT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
})
|
||||
.anchor(AnchorCorner::TopRight)
|
||||
.menu(move |cx| build_menu(cx, "top right")),
|
||||
)
|
||||
.child(
|
||||
menu_handle("test4")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"BOTTOM RIGHT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
})
|
||||
.anchor(AnchorCorner::BottomRight)
|
||||
.attach(AnchorCorner::TopRight)
|
||||
.menu(move |cx| build_menu(cx, "bottom right")),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
use crate::{v_stack, ButtonGroup};
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Details {
|
||||
text: &'static str,
|
||||
meta: Option<&'static str>,
|
||||
actions: Option<ButtonGroup>,
|
||||
}
|
||||
|
||||
impl Component for Details {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
v_stack()
|
||||
.p_1()
|
||||
.gap_0p5()
|
||||
.text_ui_sm()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.size_full()
|
||||
.child(self.text)
|
||||
.children(self.meta.map(|m| m))
|
||||
.children(self.actions.map(|a| a))
|
||||
}
|
||||
}
|
||||
|
||||
impl Details {
|
||||
pub fn new(text: &'static str) -> Self {
|
||||
Self {
|
||||
text,
|
||||
meta: None,
|
||||
actions: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta_text(mut self, meta: &'static str) -> Self {
|
||||
self.meta = Some(meta);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn actions(mut self, actions: ButtonGroup) -> Self {
|
||||
self.actions = Some(actions);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{Button, Story};
|
||||
use gpui::{Div, Render};
|
||||
|
||||
pub struct DetailsStory;
|
||||
|
||||
impl Render for DetailsStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Details>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(Details::new("The quick brown fox jumps over the lazy dog"))
|
||||
.child(Story::label(cx, "With meta"))
|
||||
.child(
|
||||
Details::new("The quick brown fox jumps over the lazy dog")
|
||||
.meta_text("Sphinx of black quartz, judge my vow."),
|
||||
)
|
||||
.child(Story::label(cx, "With meta and actions"))
|
||||
.child(
|
||||
Details::new("The quick brown fox jumps over the lazy dog")
|
||||
.meta_text("Sphinx of black quartz, judge my vow.")
|
||||
.actions(ButtonGroup::new(vec![
|
||||
Button::new("Decline"),
|
||||
Button::new("Accept").variant(crate::ButtonVariant::Filled),
|
||||
])),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
use gpui::Div;
|
||||
|
||||
use crate::{prelude::*, v_stack};
|
||||
|
||||
/// Create an elevated surface.
|
||||
///
|
||||
/// Must be used inside of a relative parent element
|
||||
pub fn elevated_surface(level: ElevationIndex, cx: &mut WindowContext) -> Div {
|
||||
let colors = cx.theme().colors();
|
||||
|
||||
// let shadow = BoxShadow {
|
||||
// color: hsla(0., 0., 0., 0.1),
|
||||
// offset: point(px(0.), px(1.)),
|
||||
// blur_radius: px(3.),
|
||||
// spread_radius: px(0.),
|
||||
// };
|
||||
|
||||
v_stack()
|
||||
.rounded_lg()
|
||||
.bg(colors.elevated_surface_background)
|
||||
.border()
|
||||
.border_color(colors.border)
|
||||
.shadow(level.shadow())
|
||||
}
|
||||
|
||||
pub fn modal(cx: &mut WindowContext) -> Div {
|
||||
elevated_surface(ElevationIndex::ModalSurface, cx)
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
use crate::{Avatar, Player};
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Facepile {
|
||||
players: Vec<Player>,
|
||||
}
|
||||
|
||||
impl Component for Facepile {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl Facepile {
|
||||
pub fn new<P: Iterator<Item = Player>>(players: P) -> Self {
|
||||
Self {
|
||||
players: players.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce};
|
|
@ -197,31 +197,3 @@ impl IconElement {
|
|||
.text_color(self.color.color(cx))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use gpui::{Div, Render};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::Story;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct IconStory;
|
||||
|
||||
impl Render for IconStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let icons = Icon::iter();
|
||||
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<IconElement>(cx))
|
||||
.child(Story::label(cx, "All Icons"))
|
||||
.child(div().flex().gap_3().children(icons.map(IconElement::new)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
use gpui::{px, Div, RenderOnce};
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct UnreadIndicator;
|
||||
|
||||
impl Component for UnreadIndicator {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
div()
|
||||
.rounded_full()
|
||||
.border_2()
|
||||
.border_color(cx.theme().colors().surface_background)
|
||||
.w(px(9.0))
|
||||
.h(px(9.0))
|
||||
.z_index(2)
|
||||
.bg(cx.theme().status().info)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnreadIndicator {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> impl Element {
|
||||
div()
|
||||
.rounded_full()
|
||||
.border_2()
|
||||
.border_color(cx.theme().colors().surface_background)
|
||||
.w(px(9.0))
|
||||
.h(px(9.0))
|
||||
.z_index(2)
|
||||
.bg(cx.theme().status().info)
|
||||
}
|
||||
}
|
|
@ -106,26 +106,3 @@ impl Input {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::Story;
|
||||
use gpui::{Div, Render};
|
||||
|
||||
pub struct InputStory;
|
||||
|
||||
impl Render for InputStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Input>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(div().flex().child(Input::new("Search")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,70 +69,3 @@ impl Key {
|
|||
Self { key: key.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
pub use crate::KeyBinding;
|
||||
use crate::Story;
|
||||
use gpui::{actions, Div, Render};
|
||||
use itertools::Itertools;
|
||||
pub struct KeybindingStory;
|
||||
|
||||
actions!(NoAction);
|
||||
|
||||
pub fn binding(key: &str) -> gpui::KeyBinding {
|
||||
gpui::KeyBinding::new(key, NoAction {}, None)
|
||||
}
|
||||
|
||||
impl Render for KeybindingStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let all_modifier_permutations =
|
||||
["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
|
||||
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<KeyBinding>(cx))
|
||||
.child(Story::label(cx, "Single Key"))
|
||||
.child(KeyBinding::new(binding("Z")))
|
||||
.child(Story::label(cx, "Single Key with Modifier"))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.child(KeyBinding::new(binding("ctrl-c")))
|
||||
.child(KeyBinding::new(binding("alt-c")))
|
||||
.child(KeyBinding::new(binding("cmd-c")))
|
||||
.child(KeyBinding::new(binding("shift-c"))),
|
||||
)
|
||||
.child(Story::label(cx, "Single Key with Modifier (Permuted)"))
|
||||
.child(
|
||||
div().flex().flex_col().children(
|
||||
all_modifier_permutations
|
||||
.chunks(4)
|
||||
.into_iter()
|
||||
.map(|chunk| {
|
||||
div()
|
||||
.flex()
|
||||
.gap_4()
|
||||
.py_3()
|
||||
.children(chunk.map(|permutation| {
|
||||
KeyBinding::new(binding(&*(permutation.join("-") + "-x")))
|
||||
}))
|
||||
}),
|
||||
),
|
||||
)
|
||||
.child(Story::label(cx, "Single Key with All Modifiers"))
|
||||
.child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")))
|
||||
.child(Story::label(cx, "Chord"))
|
||||
.child(KeyBinding::new(binding("a z")))
|
||||
.child(Story::label(cx, "Chord with Modifier"))
|
||||
.child(KeyBinding::new(binding("ctrl-a shift-z")))
|
||||
.child(KeyBinding::new(binding("fn-s")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -230,35 +230,3 @@ struct Run {
|
|||
pub text: String,
|
||||
pub color: Hsla,
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::Story;
|
||||
use gpui::{Div, Render};
|
||||
|
||||
pub struct LabelStory;
|
||||
|
||||
impl Render for LabelStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Label>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(Label::new("Hello, world!"))
|
||||
.child(Story::label(cx, "Highlighted"))
|
||||
.child(HighlightedLabel::new(
|
||||
"Hello, world!",
|
||||
vec![0, 1, 2, 7, 8, 12],
|
||||
))
|
||||
.child(HighlightedLabel::new(
|
||||
"Héllo, world!",
|
||||
vec![0, 1, 3, 8, 9, 13],
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
use gpui::{AnyElement, Div, RenderOnce};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{h_stack, prelude::*, v_stack, Button, Icon, IconButton, Label};
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Modal {
|
||||
id: ElementId,
|
||||
title: Option<SharedString>,
|
||||
primary_action: Option<Button>,
|
||||
secondary_action: Option<Button>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl Component for Modal {
|
||||
type Rendered = gpui::Stateful<Div>;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
v_stack()
|
||||
.id(self.id.clone())
|
||||
.w_96()
|
||||
// .rounded_xl()
|
||||
.bg(cx.theme().colors().background)
|
||||
.border()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.shadow_2xl()
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.p_1()
|
||||
.border_b()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(div().children(self.title.clone().map(|t| Label::new(t))))
|
||||
.child(IconButton::new("close", Icon::Close)),
|
||||
)
|
||||
.child(v_stack().p_1().children(self.children))
|
||||
.when(
|
||||
self.primary_action.is_some() || self.secondary_action.is_some(),
|
||||
|this| {
|
||||
this.child(
|
||||
h_stack()
|
||||
.border_t()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.p_1()
|
||||
.justify_end()
|
||||
.children(self.secondary_action)
|
||||
.children(self.primary_action),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Modal {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
title: None,
|
||||
primary_action: None,
|
||||
secondary_action: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title(mut self, title: impl Into<SharedString>) -> Self {
|
||||
self.title = Some(title.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn primary_action(mut self, action: Button) -> Self {
|
||||
self.primary_action = Some(action);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn secondary_action(mut self, action: Button) -> Self {
|
||||
self.secondary_action = Some(action);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for Modal {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
use gpui::rems;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{h_stack, Icon};
|
||||
|
||||
// #[derive(RenderOnce)]
|
||||
pub struct NotificationToast {
|
||||
label: SharedString,
|
||||
icon: Option<Icon>,
|
||||
}
|
||||
|
||||
impl NotificationToast {
|
||||
pub fn new(label: SharedString) -> Self {
|
||||
Self { label, icon: None }
|
||||
}
|
||||
|
||||
pub fn icon<I>(mut self, icon: I) -> Self
|
||||
where
|
||||
I: Into<Option<Icon>>,
|
||||
{
|
||||
self.icon = icon.into();
|
||||
self
|
||||
}
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> impl Element {
|
||||
h_stack()
|
||||
.z_index(5)
|
||||
.absolute()
|
||||
.top_1()
|
||||
.right_1()
|
||||
.w(rems(9999.))
|
||||
.max_w_56()
|
||||
.py_1()
|
||||
.px_1p5()
|
||||
.rounded_lg()
|
||||
.shadow_md()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.child(div().size_full().child(self.label.clone()))
|
||||
}
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
use crate::{h_stack, prelude::*, v_stack, KeyBinding, Label};
|
||||
use gpui::prelude::*;
|
||||
use gpui::Div;
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Palette {
|
||||
id: ElementId,
|
||||
input_placeholder: SharedString,
|
||||
empty_string: SharedString,
|
||||
items: Vec<PaletteItem>,
|
||||
default_order: OrderMethod,
|
||||
}
|
||||
|
||||
impl Component for Palette {
|
||||
type Rendered = gpui::Stateful<Div>;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
v_stack()
|
||||
.id(self.id)
|
||||
.w_96()
|
||||
.rounded_lg()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.border()
|
||||
.border_color(cx.theme().colors().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(TextColor::Placeholder),
|
||||
),
|
||||
))
|
||||
.child(
|
||||
div()
|
||||
.h_px()
|
||||
.w_full()
|
||||
.bg(cx.theme().colors().element_background),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.id("items")
|
||||
.py_0p5()
|
||||
.px_1()
|
||||
.grow()
|
||||
.max_h_96()
|
||||
.overflow_y_scroll()
|
||||
.children(
|
||||
vec![if self.items.is_empty() {
|
||||
Some(h_stack().justify_between().px_2().py_1().child(
|
||||
Label::new(self.empty_string).color(TextColor::Muted),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}]
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.children(self.items.into_iter().enumerate().map(|(index, item)| {
|
||||
h_stack()
|
||||
.id(index)
|
||||
.justify_between()
|
||||
.px_2()
|
||||
.py_0p5()
|
||||
.rounded_lg()
|
||||
.hover(|style| {
|
||||
style.bg(cx.theme().colors().ghost_element_hover)
|
||||
})
|
||||
.active(|style| {
|
||||
style.bg(cx.theme().colors().ghost_element_active)
|
||||
})
|
||||
.child(item)
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Palette {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
input_placeholder: "Find something...".into(),
|
||||
empty_string: "No items found.".into(),
|
||||
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: impl Into<SharedString>) -> Self {
|
||||
self.input_placeholder = input_placeholder.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn empty_string(mut self, empty_string: impl Into<SharedString>) -> Self {
|
||||
self.empty_string = empty_string.into();
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: Hook up sort order
|
||||
pub fn default_order(mut self, default_order: OrderMethod) -> Self {
|
||||
self.default_order = default_order;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct PaletteItem {
|
||||
pub label: SharedString,
|
||||
pub sublabel: Option<SharedString>,
|
||||
pub key_binding: Option<KeyBinding>,
|
||||
}
|
||||
|
||||
impl Component for PaletteItem {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
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.key_binding)
|
||||
}
|
||||
}
|
||||
|
||||
impl PaletteItem {
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
label: label.into(),
|
||||
sublabel: None,
|
||||
key_binding: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
self.label = label.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sublabel(mut self, sublabel: impl Into<Option<SharedString>>) -> Self {
|
||||
self.sublabel = sublabel.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn key_binding(mut self, key_binding: impl Into<Option<KeyBinding>>) -> Self {
|
||||
self.key_binding = key_binding.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::ElementId;
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use gpui::{Div, Render};
|
||||
|
||||
use crate::{binding, Story};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct PaletteStory;
|
||||
|
||||
impl Render for PaletteStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
{
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Palette>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(Palette::new("palette-1"))
|
||||
.child(Story::label(cx, "With Items"))
|
||||
.child(
|
||||
Palette::new("palette-2")
|
||||
.placeholder("Execute a command...")
|
||||
.items(vec![
|
||||
PaletteItem::new("theme selector: toggle")
|
||||
.key_binding(KeyBinding::new(binding("cmd-k cmd-t"))),
|
||||
PaletteItem::new("assistant: inline assist")
|
||||
.key_binding(KeyBinding::new(binding("cmd-enter"))),
|
||||
PaletteItem::new("assistant: quote selection")
|
||||
.key_binding(KeyBinding::new(binding("cmd-<"))),
|
||||
PaletteItem::new("assistant: toggle focus")
|
||||
.key_binding(KeyBinding::new(binding("cmd-?"))),
|
||||
PaletteItem::new("auto update: check"),
|
||||
PaletteItem::new("auto update: view release notes"),
|
||||
PaletteItem::new("branches: open recent")
|
||||
.key_binding(KeyBinding::new(binding("cmd-alt-b"))),
|
||||
PaletteItem::new("chat panel: toggle focus"),
|
||||
PaletteItem::new("cli: install"),
|
||||
PaletteItem::new("client: sign in"),
|
||||
PaletteItem::new("client: sign out"),
|
||||
PaletteItem::new("editor: cancel")
|
||||
.key_binding(KeyBinding::new(binding("escape"))),
|
||||
]),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
use gpui::px;
|
||||
use gpui::{prelude::*, AbsoluteLength, AnyElement, Div, RenderOnce};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::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(RenderOnce)]
|
||||
pub struct Panel {
|
||||
id: ElementId,
|
||||
current_side: PanelSide,
|
||||
/// Defaults to PanelAllowedSides::LeftAndRight
|
||||
allowed_sides: PanelAllowedSides,
|
||||
initial_width: AbsoluteLength,
|
||||
width: Option<AbsoluteLength>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl Component for Panel {
|
||||
type Rendered = gpui::Stateful<Div>;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
let current_size = self.width.unwrap_or(self.initial_width);
|
||||
|
||||
v_stack()
|
||||
.id(self.id.clone())
|
||||
.flex_initial()
|
||||
.map(|this| match self.current_side {
|
||||
PanelSide::Left | PanelSide::Right => this.h_full().w(current_size),
|
||||
PanelSide::Bottom => this,
|
||||
})
|
||||
.map(|this| match self.current_side {
|
||||
PanelSide::Left => this.border_r(),
|
||||
PanelSide::Right => this.border_l(),
|
||||
PanelSide::Bottom => this.border_b().w_full().h(current_size),
|
||||
})
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.border_color(cx.theme().colors().border)
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
impl Panel {
|
||||
pub fn new(id: impl Into<ElementId>, cx: &mut WindowContext) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
current_side: PanelSide::default(),
|
||||
allowed_sides: PanelAllowedSides::default(),
|
||||
initial_width: px(320.).into(),
|
||||
width: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for Panel {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{Label, Story};
|
||||
use gpui::{Div, InteractiveElement, Render};
|
||||
|
||||
pub struct PanelStory;
|
||||
|
||||
impl Render for PanelStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Panel>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(
|
||||
Panel::new("panel", cx).child(
|
||||
div()
|
||||
.id("panel-contents")
|
||||
.overflow_y_scroll()
|
||||
.children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1)))),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
use gpui::Hsla;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Represents a person with a Zed account's public profile.
|
||||
/// All data in this struct should be considered public.
|
||||
pub struct PublicPlayer {
|
||||
pub username: SharedString,
|
||||
pub avatar: SharedString,
|
||||
pub is_contact: bool,
|
||||
}
|
||||
|
||||
impl PublicPlayer {
|
||||
pub fn new(username: impl Into<SharedString>, avatar: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
username: username.into(),
|
||||
avatar: avatar.into(),
|
||||
is_contact: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl MicStatus {
|
||||
pub fn inverse(&self) -> Self {
|
||||
match self {
|
||||
Self::Muted => Self::Unmuted,
|
||||
Self::Unmuted => Self::Muted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum VideoStatus {
|
||||
On,
|
||||
#[default]
|
||||
Off,
|
||||
}
|
||||
|
||||
impl VideoStatus {
|
||||
pub fn inverse(&self) -> Self {
|
||||
match self {
|
||||
Self::On => Self::Off,
|
||||
Self::Off => Self::On,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum ScreenShareStatus {
|
||||
Shared,
|
||||
#[default]
|
||||
NotShared,
|
||||
}
|
||||
|
||||
impl ScreenShareStatus {
|
||||
pub fn inverse(&self) -> Self {
|
||||
match self {
|
||||
Self::Shared => Self::NotShared,
|
||||
Self::NotShared => Self::Shared,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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(&self, cx: &mut WindowContext) -> Hsla {
|
||||
cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].cursor
|
||||
}
|
||||
|
||||
pub fn selection_color(&self, cx: &mut WindowContext) -> Hsla {
|
||||
cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].selection
|
||||
}
|
||||
|
||||
pub fn avatar_src(&self) -> &str {
|
||||
&self.avatar_src
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
self.index
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
use gpui::{Div, RenderOnce};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{Avatar, Facepile, PlayerWithCallStatus};
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct PlayerStack {
|
||||
player_with_call_status: PlayerWithCallStatus,
|
||||
}
|
||||
|
||||
impl Component for PlayerStack {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
let player = self.player_with_call_status.get_player();
|
||||
|
||||
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().bg(player.cursor_color(cx))),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.h_6()
|
||||
.pl_1()
|
||||
.rounded_lg()
|
||||
.bg(if followers.is_none() {
|
||||
cx.theme().styles.system.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()))
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayerStack {
|
||||
pub fn new(player_with_call_status: PlayerWithCallStatus) -> Self {
|
||||
Self {
|
||||
player_with_call_status,
|
||||
}
|
||||
}
|
||||
}
|
27
crates/ui2/src/components/stories/avatar.rs
Normal file
27
crates/ui2/src/components/stories/avatar.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::Story;
|
||||
use gpui::{Div, Render};
|
||||
|
||||
pub struct AvatarStory;
|
||||
|
||||
impl Render for AvatarStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Avatar>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(Avatar::new(
|
||||
"https://avatars.githubusercontent.com/u/1714999?v=4",
|
||||
))
|
||||
.child(Avatar::new(
|
||||
"https://avatars.githubusercontent.com/u/326587?v=4",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
167
crates/ui2/src/components/stories/button.rs
Normal file
167
crates/ui2/src/components/stories/button.rs
Normal file
|
@ -0,0 +1,167 @@
|
|||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{h_stack, v_stack, Story, TextColor};
|
||||
use gpui::{rems, Div, Render};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
pub struct ButtonStory;
|
||||
|
||||
impl Render for ButtonStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let states = InteractionState::iter();
|
||||
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Button>(cx))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_8()
|
||||
.child(
|
||||
div()
|
||||
.child(Story::label(cx, "Ghost (Default)"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label").variant(ButtonVariant::Ghost), // .state(state),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Ghost – Left Icon"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Left), // .state(state),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Ghost – Right Icon"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Right), // .state(state),
|
||||
)
|
||||
}))),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.child(Story::label(cx, "Filled"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label").variant(ButtonVariant::Filled), // .state(state),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Filled – Left Button"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Left), // .state(state),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Filled – Right Button"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Right), // .state(state),
|
||||
)
|
||||
}))),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.child(Story::label(cx, "Fixed With"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
// .state(state)
|
||||
.width(Some(rems(6.).into())),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Fixed With – Left Icon"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
// .state(state)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Left)
|
||||
.width(Some(rems(6.).into())),
|
||||
)
|
||||
})))
|
||||
.child(Story::label(cx, "Fixed With – Right Icon"))
|
||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
||||
v_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(state.to_string()).color(TextColor::Muted),
|
||||
)
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Filled)
|
||||
// .state(state)
|
||||
.icon(Icon::Plus)
|
||||
.icon_position(IconPosition::Right)
|
||||
.width(Some(rems(6.).into())),
|
||||
)
|
||||
}))),
|
||||
),
|
||||
)
|
||||
.child(Story::label(cx, "Button with `on_click`"))
|
||||
.child(
|
||||
Button::new("Label")
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.on_click(|_, cx| println!("Button clicked.")),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
59
crates/ui2/src/components/stories/checkbox.rs
Normal file
59
crates/ui2/src/components/stories/checkbox.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{h_stack, Story};
|
||||
use gpui::{Div, Render, ViewContext};
|
||||
|
||||
pub struct CheckboxStory;
|
||||
|
||||
impl Render for CheckboxStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Checkbox>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(
|
||||
h_stack()
|
||||
.p_2()
|
||||
.gap_2()
|
||||
.rounded_md()
|
||||
.border()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(Checkbox::new("checkbox-enabled", Selection::Unselected))
|
||||
.child(Checkbox::new(
|
||||
"checkbox-intermediate",
|
||||
Selection::Indeterminate,
|
||||
))
|
||||
.child(Checkbox::new("checkbox-selected", Selection::Selected)),
|
||||
)
|
||||
.child(Story::label(cx, "Disabled"))
|
||||
.child(
|
||||
h_stack()
|
||||
.p_2()
|
||||
.gap_2()
|
||||
.rounded_md()
|
||||
.border()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(
|
||||
Checkbox::new("checkbox-disabled", Selection::Unselected)
|
||||
.disabled(true),
|
||||
)
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"checkbox-disabled-intermediate",
|
||||
Selection::Indeterminate,
|
||||
)
|
||||
.disabled(true),
|
||||
)
|
||||
.child(
|
||||
Checkbox::new("checkbox-disabled-selected", Selection::Selected)
|
||||
.disabled(true),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
112
crates/ui2/src/components/stories/context_menu.rs
Normal file
112
crates/ui2/src/components/stories/context_menu.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{story::Story, Label};
|
||||
use gpui::{actions, Div, Render};
|
||||
|
||||
actions!(PrintCurrentDate, PrintBestFood);
|
||||
|
||||
fn build_menu(cx: &mut WindowContext, header: impl Into<SharedString>) -> View<ContextMenu> {
|
||||
ContextMenu::build(cx, |menu, _| {
|
||||
menu.header(header)
|
||||
.separator()
|
||||
.entry(
|
||||
ListItem::new("Print current time", Label::new("Print current time")),
|
||||
|v, cx| {
|
||||
println!("dispatching PrintCurrentTime action");
|
||||
cx.dispatch_action(PrintCurrentDate.boxed_clone())
|
||||
},
|
||||
)
|
||||
.entry(
|
||||
ListItem::new("Print best food", Label::new("Print best food")),
|
||||
|v, cx| cx.dispatch_action(PrintBestFood.boxed_clone()),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub struct ContextMenuStory;
|
||||
|
||||
impl Render for ContextMenuStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.on_action(|_: &PrintCurrentDate, _| {
|
||||
println!("printing unix time!");
|
||||
if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
|
||||
println!("Current Unix time is {:?}", unix_time.as_secs());
|
||||
}
|
||||
})
|
||||
.on_action(|_: &PrintBestFood, _| {
|
||||
println!("burrito");
|
||||
})
|
||||
.flex()
|
||||
.flex_row()
|
||||
.justify_between()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.justify_between()
|
||||
.child(
|
||||
menu_handle("test2")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"TOP LEFT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
})
|
||||
.menu(move |cx| build_menu(cx, "top left")),
|
||||
)
|
||||
.child(
|
||||
menu_handle("test1")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"BOTTOM LEFT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
})
|
||||
.anchor(AnchorCorner::BottomLeft)
|
||||
.attach(AnchorCorner::TopLeft)
|
||||
.menu(move |cx| build_menu(cx, "bottom left")),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.justify_between()
|
||||
.child(
|
||||
menu_handle("test3")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"TOP RIGHT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
})
|
||||
.anchor(AnchorCorner::TopRight)
|
||||
.menu(move |cx| build_menu(cx, "top right")),
|
||||
)
|
||||
.child(
|
||||
menu_handle("test4")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"BOTTOM RIGHT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
})
|
||||
.anchor(AnchorCorner::BottomRight)
|
||||
.attach(AnchorCorner::TopRight)
|
||||
.menu(move |cx| build_menu(cx, "bottom right")),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
27
crates/ui2/src/components/stories/icon.rs
Normal file
27
crates/ui2/src/components/stories/icon.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use gpui::{Div, Render};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::Story;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct IconStory;
|
||||
|
||||
impl Render for IconStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let icons = Icon::iter();
|
||||
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<IconElement>(cx))
|
||||
.child(Story::label(cx, "All Icons"))
|
||||
.child(div().flex().gap_3().children(icons.map(IconElement::new)))
|
||||
}
|
||||
}
|
||||
}
|
22
crates/ui2/src/components/stories/input.rs
Normal file
22
crates/ui2/src/components/stories/input.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::Story;
|
||||
use gpui::{Div, Render};
|
||||
|
||||
pub struct InputStory;
|
||||
|
||||
impl Render for InputStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Input>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(div().flex().child(Input::new("Search")))
|
||||
}
|
||||
}
|
||||
}
|
66
crates/ui2/src/components/stories/keybinding.rs
Normal file
66
crates/ui2/src/components/stories/keybinding.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
pub use crate::KeyBinding;
|
||||
use crate::Story;
|
||||
use gpui::{actions, Div, Render};
|
||||
use itertools::Itertools;
|
||||
pub struct KeybindingStory;
|
||||
|
||||
actions!(NoAction);
|
||||
|
||||
pub fn binding(key: &str) -> gpui::KeyBinding {
|
||||
gpui::KeyBinding::new(key, NoAction {}, None)
|
||||
}
|
||||
|
||||
impl Render for KeybindingStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let all_modifier_permutations =
|
||||
["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
|
||||
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<KeyBinding>(cx))
|
||||
.child(Story::label(cx, "Single Key"))
|
||||
.child(KeyBinding::new(binding("Z")))
|
||||
.child(Story::label(cx, "Single Key with Modifier"))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.child(KeyBinding::new(binding("ctrl-c")))
|
||||
.child(KeyBinding::new(binding("alt-c")))
|
||||
.child(KeyBinding::new(binding("cmd-c")))
|
||||
.child(KeyBinding::new(binding("shift-c"))),
|
||||
)
|
||||
.child(Story::label(cx, "Single Key with Modifier (Permuted)"))
|
||||
.child(
|
||||
div().flex().flex_col().children(
|
||||
all_modifier_permutations
|
||||
.chunks(4)
|
||||
.into_iter()
|
||||
.map(|chunk| {
|
||||
div()
|
||||
.flex()
|
||||
.gap_4()
|
||||
.py_3()
|
||||
.children(chunk.map(|permutation| {
|
||||
KeyBinding::new(binding(&*(permutation.join("-") + "-x")))
|
||||
}))
|
||||
}),
|
||||
),
|
||||
)
|
||||
.child(Story::label(cx, "Single Key with All Modifiers"))
|
||||
.child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")))
|
||||
.child(Story::label(cx, "Chord"))
|
||||
.child(KeyBinding::new(binding("a z")))
|
||||
.child(Story::label(cx, "Chord with Modifier"))
|
||||
.child(KeyBinding::new(binding("ctrl-a shift-z")))
|
||||
.child(KeyBinding::new(binding("fn-s")))
|
||||
}
|
||||
}
|
||||
}
|
31
crates/ui2/src/components/stories/label.rs
Normal file
31
crates/ui2/src/components/stories/label.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::Story;
|
||||
use gpui::{Div, Render};
|
||||
|
||||
pub struct LabelStory;
|
||||
|
||||
impl Render for LabelStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Label>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(Label::new("Hello, world!"))
|
||||
.child(Story::label(cx, "Highlighted"))
|
||||
.child(HighlightedLabel::new(
|
||||
"Hello, world!",
|
||||
vec![0, 1, 2, 7, 8, 12],
|
||||
))
|
||||
.child(HighlightedLabel::new(
|
||||
"Héllo, world!",
|
||||
vec![0, 1, 3, 8, 9, 13],
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
8
crates/ui2/src/components/stories/mod.rs
Normal file
8
crates/ui2/src/components/stories/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
mod avatar;
|
||||
mod button;
|
||||
mod checkbox;
|
||||
mod context_menu;
|
||||
mod icon;
|
||||
mod input;
|
||||
mod keybinding;
|
||||
mod label;
|
|
@ -1,276 +0,0 @@
|
|||
// use crate::prelude::*;
|
||||
// use crate::{Icon, IconElement, Label, TextColor};
|
||||
// use gpui::{prelude::*, red, Div, ElementId, Render, RenderOnce, View};
|
||||
|
||||
// #[derive(RenderOnce, Clone)]
|
||||
// pub struct Tab {
|
||||
// id: ElementId,
|
||||
// title: String,
|
||||
// icon: Option<Icon>,
|
||||
// current: bool,
|
||||
// dirty: bool,
|
||||
// fs_status: FileSystemStatus,
|
||||
// git_status: GitStatus,
|
||||
// diagnostic_status: DiagnosticStatus,
|
||||
// close_side: IconSide,
|
||||
// }
|
||||
|
||||
// #[derive(Clone, Debug)]
|
||||
// struct TabDragState {
|
||||
// title: String,
|
||||
// }
|
||||
|
||||
// impl Render for TabDragState {
|
||||
// type Element = Div;
|
||||
|
||||
// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
// div().w_8().h_4().bg(red())
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Component for Tab {
|
||||
// type Rendered = gpui::Stateful<Div>;
|
||||
|
||||
// fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
// 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(TextColor::Hidden)
|
||||
// .set_strikethrough(true),
|
||||
// (GitStatus::None, false) => Label::new(self.title.clone()),
|
||||
// (GitStatus::Created, false) => Label::new(self.title.clone()).color(TextColor::Created),
|
||||
// (GitStatus::Modified, false) => {
|
||||
// Label::new(self.title.clone()).color(TextColor::Modified)
|
||||
// }
|
||||
// (GitStatus::Renamed, false) => Label::new(self.title.clone()).color(TextColor::Accent),
|
||||
// (GitStatus::Conflict, false) => Label::new(self.title.clone()),
|
||||
// };
|
||||
|
||||
// let close_icon = || IconElement::new(Icon::Close).color(TextColor::Muted);
|
||||
|
||||
// let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current {
|
||||
// false => (
|
||||
// cx.theme().colors().tab_inactive_background,
|
||||
// cx.theme().colors().ghost_element_hover,
|
||||
// cx.theme().colors().ghost_element_active,
|
||||
// ),
|
||||
// true => (
|
||||
// cx.theme().colors().tab_active_background,
|
||||
// cx.theme().colors().element_hover,
|
||||
// cx.theme().colors().element_active,
|
||||
// ),
|
||||
// };
|
||||
|
||||
// let drag_state = TabDragState {
|
||||
// title: self.title.clone(),
|
||||
// };
|
||||
|
||||
// div()
|
||||
// .id(self.id.clone())
|
||||
// .on_drag(move |_view, cx| cx.build_view(|cx| drag_state.clone()))
|
||||
// .drag_over::<TabDragState>(|d| d.bg(cx.theme().colors().drop_target_background))
|
||||
// .on_drop(|_view, state: View<TabDragState>, cx| {
|
||||
// eprintln!("{:?}", state.read(cx));
|
||||
// })
|
||||
// .px_2()
|
||||
// .py_0p5()
|
||||
// .flex()
|
||||
// .items_center()
|
||||
// .justify_center()
|
||||
// .bg(tab_bg)
|
||||
// .hover(|h| h.bg(tab_hover_bg))
|
||||
// .active(|a| a.bg(tab_active_bg))
|
||||
// .child(
|
||||
// div()
|
||||
// .px_1()
|
||||
// .flex()
|
||||
// .items_center()
|
||||
// .gap_1p5()
|
||||
// .children(has_fs_conflict.then(|| {
|
||||
// IconElement::new(Icon::ExclamationTriangle)
|
||||
// .size(crate::IconSize::Small)
|
||||
// .color(TextColor::Warning)
|
||||
// }))
|
||||
// .children(self.icon.map(IconElement::new))
|
||||
// .children(if self.close_side == IconSide::Left {
|
||||
// Some(close_icon())
|
||||
// } else {
|
||||
// None
|
||||
// })
|
||||
// .child(label)
|
||||
// .children(if self.close_side == IconSide::Right {
|
||||
// Some(close_icon())
|
||||
// } else {
|
||||
// None
|
||||
// }),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Tab {
|
||||
// pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
// Self {
|
||||
// id: id.into(),
|
||||
// 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
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[cfg(feature = "stories")]
|
||||
// pub use stories::*;
|
||||
|
||||
// #[cfg(feature = "stories")]
|
||||
// mod stories {
|
||||
// use super::*;
|
||||
// use crate::{h_stack, v_stack, Icon, Story};
|
||||
// use strum::IntoEnumIterator;
|
||||
|
||||
// pub struct TabStory;
|
||||
|
||||
// impl Render for TabStory {
|
||||
// type Element = Div;
|
||||
|
||||
// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
// let git_statuses = GitStatus::iter();
|
||||
// let fs_statuses = FileSystemStatus::iter();
|
||||
|
||||
// Story::container(cx)
|
||||
// .child(Story::title_for::<_, Tab>(cx))
|
||||
// .child(
|
||||
// h_stack().child(
|
||||
// v_stack()
|
||||
// .gap_2()
|
||||
// .child(Story::label(cx, "Default"))
|
||||
// .child(Tab::new("default")),
|
||||
// ),
|
||||
// )
|
||||
// .child(
|
||||
// h_stack().child(
|
||||
// v_stack().gap_2().child(Story::label(cx, "Current")).child(
|
||||
// h_stack()
|
||||
// .gap_4()
|
||||
// .child(
|
||||
// Tab::new("current")
|
||||
// .title("Current".to_string())
|
||||
// .current(true),
|
||||
// )
|
||||
// .child(
|
||||
// Tab::new("not_current")
|
||||
// .title("Not Current".to_string())
|
||||
// .current(false),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// .child(
|
||||
// h_stack().child(
|
||||
// v_stack()
|
||||
// .gap_2()
|
||||
// .child(Story::label(cx, "Titled"))
|
||||
// .child(Tab::new("titled").title("label".to_string())),
|
||||
// ),
|
||||
// )
|
||||
// .child(
|
||||
// h_stack().child(
|
||||
// v_stack()
|
||||
// .gap_2()
|
||||
// .child(Story::label(cx, "With Icon"))
|
||||
// .child(
|
||||
// Tab::new("with_icon")
|
||||
// .title("label".to_string())
|
||||
// .icon(Some(Icon::Envelope)),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// .child(
|
||||
// h_stack().child(
|
||||
// v_stack()
|
||||
// .gap_2()
|
||||
// .child(Story::label(cx, "Close Side"))
|
||||
// .child(
|
||||
// h_stack()
|
||||
// .gap_4()
|
||||
// .child(
|
||||
// Tab::new("left")
|
||||
// .title("Left".to_string())
|
||||
// .close_side(IconSide::Left),
|
||||
// )
|
||||
// .child(Tab::new("right").title("Right".to_string())),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// .child(
|
||||
// v_stack()
|
||||
// .gap_2()
|
||||
// .child(Story::label(cx, "Git Status"))
|
||||
// .child(h_stack().gap_4().children(git_statuses.map(|git_status| {
|
||||
// Tab::new("git_status")
|
||||
// .title(git_status.to_string())
|
||||
// .git_status(git_status)
|
||||
// }))),
|
||||
// )
|
||||
// .child(
|
||||
// v_stack()
|
||||
// .gap_2()
|
||||
// .child(Story::label(cx, "File System Status"))
|
||||
// .child(h_stack().gap_4().children(fs_statuses.map(|fs_status| {
|
||||
// Tab::new("file_system_status")
|
||||
// .title(fs_status.to_string())
|
||||
// .fs_status(fs_status)
|
||||
// }))),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -1,117 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
use gpui::{prelude::*, AnyElement, RenderOnce};
|
||||
use gpui::{Div, Element};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ToastOrigin {
|
||||
#[default]
|
||||
Bottom,
|
||||
BottomRight,
|
||||
}
|
||||
|
||||
/// Don't use toast directly:
|
||||
///
|
||||
/// - For messages with a required action, use a `NotificationToast`.
|
||||
/// - For messages that convey information, use a `StatusToast`.
|
||||
///
|
||||
/// 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(RenderOnce)]
|
||||
pub struct Toast {
|
||||
origin: ToastOrigin,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl Component for Toast {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
let mut div = div();
|
||||
|
||||
if self.origin == ToastOrigin::Bottom {
|
||||
div = div.right_1_2();
|
||||
} else {
|
||||
div = div.right_2();
|
||||
}
|
||||
|
||||
div.z_index(5)
|
||||
.absolute()
|
||||
.bottom_9()
|
||||
.flex()
|
||||
.py_1()
|
||||
.px_1p5()
|
||||
.rounded_lg()
|
||||
.shadow_md()
|
||||
.overflow_hidden()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
impl Toast {
|
||||
pub fn new(origin: ToastOrigin) -> Self {
|
||||
Self {
|
||||
origin,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> impl Element {
|
||||
let mut div = div();
|
||||
|
||||
if self.origin == ToastOrigin::Bottom {
|
||||
div = div.right_1_2();
|
||||
} else {
|
||||
div = div.right_2();
|
||||
}
|
||||
|
||||
div.z_index(5)
|
||||
.absolute()
|
||||
.bottom_9()
|
||||
.flex()
|
||||
.py_1()
|
||||
.px_1p5()
|
||||
.rounded_lg()
|
||||
.shadow_md()
|
||||
.overflow_hidden()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for Toast {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use gpui::{Div, Render};
|
||||
|
||||
use crate::{Label, Story};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct ToastStory;
|
||||
|
||||
impl Render for ToastStory {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<Toast>(cx))
|
||||
.child(Story::label(cx, "Default"))
|
||||
.child(Toast::new(ToastOrigin::Bottom).child(Label::new("label")))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
use gpui::{Div, RenderOnce};
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct ToolDivider;
|
||||
|
||||
impl Component for ToolDivider {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
div().w_px().h_3().bg(cx.theme().colors().border)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolDivider {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> impl Element {
|
||||
div().w_px().h_3().bg(cx.theme().colors().border)
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ pub use gpui::{
|
|||
ViewContext, WindowContext,
|
||||
};
|
||||
|
||||
pub use crate::elevation::*;
|
||||
pub use crate::StyledExt;
|
||||
pub use crate::{ButtonVariant, TextColor};
|
||||
pub use theme2::ActiveTheme;
|
||||
|
|
2
crates/ui2/src/styles.rs
Normal file
2
crates/ui2/src/styles.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
mod elevation;
|
||||
pub use elevation::*;
|
|
@ -1,7 +1,7 @@
|
|||
use gpui::{hsla, point, px, BoxShadow};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
#[doc = include_str!("elevation.md")]
|
||||
#[doc = include_str!("docs/elevation.md")]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Elevation {
|
||||
ElevationIndex(ElevationIndex),
|
|
@ -15,15 +15,15 @@
|
|||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
mod components;
|
||||
mod elevation;
|
||||
pub mod prelude;
|
||||
mod styled_ext;
|
||||
mod styles;
|
||||
pub mod utils;
|
||||
|
||||
pub use components::*;
|
||||
pub use prelude::*;
|
||||
// pub use static_data::*;
|
||||
pub use styled_ext::*;
|
||||
pub use styles::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod story;
|
Loading…
Add table
Add a link
Reference in a new issue