ui2
cleanup (#3374)
[[PR Description]] Tidies up the `ui2` crate, removing all components we aren't using and doing a bit of reorganization. Release Notes: - N/A
This commit is contained in:
commit
79c890f31a
54 changed files with 711 additions and 2211 deletions
|
@ -37,7 +37,7 @@ use gpui::{
|
|||
};
|
||||
use project::Project;
|
||||
use theme::ActiveTheme;
|
||||
use ui::{h_stack, Button, ButtonVariant, KeyBinding, Label, TextColor, Tooltip};
|
||||
use ui::{h_stack, Button, ButtonVariant, Color, KeyBinding, Label, Tooltip};
|
||||
use workspace::Workspace;
|
||||
|
||||
// const MAX_PROJECT_NAME_LENGTH: usize = 40;
|
||||
|
@ -115,7 +115,7 @@ impl Render for CollabTitlebarItem {
|
|||
.child(
|
||||
Button::new("player")
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.color(Some(TextColor::Player(0))),
|
||||
.color(Some(Color::Player(0))),
|
||||
)
|
||||
.tooltip(move |cx| Tooltip::text("Toggle following", cx)),
|
||||
)
|
||||
|
@ -133,7 +133,7 @@ impl Render for CollabTitlebarItem {
|
|||
.child(
|
||||
Button::new("branch_name")
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.color(Some(TextColor::Muted)),
|
||||
.color(Some(Color::Muted)),
|
||||
)
|
||||
.tooltip(move |cx| {
|
||||
cx.build_view(|_| {
|
||||
|
|
|
@ -36,7 +36,7 @@ use std::{
|
|||
};
|
||||
use theme::ActiveTheme;
|
||||
pub use toolbar_controls::ToolbarControls;
|
||||
use ui::{h_stack, HighlightedLabel, Icon, IconElement, Label, TextColor};
|
||||
use ui::{h_stack, Color, HighlightedLabel, Icon, IconElement, Label};
|
||||
use util::TryFutureExt;
|
||||
use workspace::{
|
||||
item::{BreadcrumbText, Item, ItemEvent, ItemHandle},
|
||||
|
@ -778,15 +778,15 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
|
|||
.bg(gpui::red())
|
||||
.map(|stack| {
|
||||
let icon = if diagnostic.severity == DiagnosticSeverity::ERROR {
|
||||
IconElement::new(Icon::XCircle).color(TextColor::Error)
|
||||
IconElement::new(Icon::XCircle).color(Color::Error)
|
||||
} else {
|
||||
IconElement::new(Icon::ExclamationTriangle).color(TextColor::Warning)
|
||||
IconElement::new(Icon::ExclamationTriangle).color(Color::Warning)
|
||||
};
|
||||
|
||||
stack.child(div().pl_8().child(icon))
|
||||
})
|
||||
.when_some(diagnostic.source.as_ref(), |stack, source| {
|
||||
stack.child(Label::new(format!("{source}:")).color(TextColor::Accent))
|
||||
stack.child(Label::new(format!("{source}:")).color(Color::Accent))
|
||||
})
|
||||
.child(HighlightedLabel::new(message.clone(), highlights.clone()))
|
||||
.when_some(diagnostic.code.as_ref(), |stack, code| {
|
||||
|
|
|
@ -7,7 +7,7 @@ use gpui::{
|
|||
use language::Diagnostic;
|
||||
use lsp::LanguageServerId;
|
||||
use theme::ActiveTheme;
|
||||
use ui::{h_stack, Icon, IconElement, Label, TextColor, Tooltip};
|
||||
use ui::{h_stack, Color, Icon, IconElement, Label, Tooltip};
|
||||
use workspace::{item::ItemHandle, StatusItemView, ToolbarItemEvent, Workspace};
|
||||
|
||||
use crate::ProjectDiagnosticsEditor;
|
||||
|
@ -26,20 +26,20 @@ impl Render for DiagnosticIndicator {
|
|||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) {
|
||||
(0, 0) => h_stack().child(IconElement::new(Icon::Check).color(TextColor::Success)),
|
||||
(0, 0) => h_stack().child(IconElement::new(Icon::Check).color(Color::Success)),
|
||||
(0, warning_count) => h_stack()
|
||||
.gap_1()
|
||||
.child(IconElement::new(Icon::ExclamationTriangle).color(TextColor::Warning))
|
||||
.child(IconElement::new(Icon::ExclamationTriangle).color(Color::Warning))
|
||||
.child(Label::new(warning_count.to_string())),
|
||||
(error_count, 0) => h_stack()
|
||||
.gap_1()
|
||||
.child(IconElement::new(Icon::XCircle).color(TextColor::Error))
|
||||
.child(IconElement::new(Icon::XCircle).color(Color::Error))
|
||||
.child(Label::new(error_count.to_string())),
|
||||
(error_count, warning_count) => h_stack()
|
||||
.gap_1()
|
||||
.child(IconElement::new(Icon::XCircle).color(TextColor::Error))
|
||||
.child(IconElement::new(Icon::XCircle).color(Color::Error))
|
||||
.child(Label::new(error_count.to_string()))
|
||||
.child(IconElement::new(Icon::ExclamationTriangle).color(TextColor::Warning))
|
||||
.child(IconElement::new(Icon::ExclamationTriangle).color(Color::Warning))
|
||||
.child(Label::new(warning_count.to_string())),
|
||||
};
|
||||
|
||||
|
|
|
@ -4408,7 +4408,7 @@ impl Editor {
|
|||
editor.fold_at(&FoldAt { buffer_row }, cx);
|
||||
}
|
||||
}))
|
||||
.color(ui::TextColor::Muted)
|
||||
.color(ui::Color::Muted)
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
|
|
|
@ -30,7 +30,7 @@ use std::{
|
|||
};
|
||||
use text::Selection;
|
||||
use theme::{ActiveTheme, Theme};
|
||||
use ui::{Label, TextColor};
|
||||
use ui::{Color, Label};
|
||||
use util::{paths::PathExt, ResultExt, TryFutureExt};
|
||||
use workspace::item::{BreadcrumbText, FollowEvent, FollowableEvents, FollowableItemHandle};
|
||||
use workspace::{
|
||||
|
@ -604,7 +604,7 @@ impl Item for Editor {
|
|||
&description,
|
||||
MAX_TAB_TITLE_LEN,
|
||||
))
|
||||
.color(TextColor::Muted),
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
})),
|
||||
|
|
|
@ -5,7 +5,7 @@ use gpui::{
|
|||
};
|
||||
use text::{Bias, Point};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{h_stack, v_stack, Label, StyledExt, TextColor};
|
||||
use ui::{h_stack, v_stack, Color, Label, StyledExt};
|
||||
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
||||
use workspace::Workspace;
|
||||
|
||||
|
@ -176,7 +176,7 @@ impl Render for GoToLine {
|
|||
.justify_between()
|
||||
.px_2()
|
||||
.py_1()
|
||||
.child(Label::new(self.current_text.clone()).color(TextColor::Muted)),
|
||||
.child(Label::new(self.current_text.clone()).color(Color::Muted)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use gpui::{
|
|||
MouseDownEvent, Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext,
|
||||
};
|
||||
use std::{cmp, sync::Arc};
|
||||
use ui::{prelude::*, v_stack, Divider, Label, TextColor};
|
||||
use ui::{prelude::*, v_stack, Color, Divider, Label};
|
||||
|
||||
pub struct Picker<D: PickerDelegate> {
|
||||
pub delegate: D,
|
||||
|
@ -250,7 +250,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
|
|||
v_stack().p_1().grow().child(
|
||||
div()
|
||||
.px_1()
|
||||
.child(Label::new("No matches").color(TextColor::Muted)),
|
||||
.child(Label::new("No matches").color(Color::Muted)),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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,34 @@ mod avatar;
|
|||
mod button;
|
||||
mod checkbox;
|
||||
mod context_menu;
|
||||
mod details;
|
||||
mod disclosure;
|
||||
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 disclosure::*;
|
||||
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::*;
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
use crate::prelude::*;
|
||||
use gpui::{img, Img, RenderOnce};
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub enum Shape {
|
||||
#[default]
|
||||
Circle,
|
||||
RoundedRectangle,
|
||||
}
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Avatar {
|
||||
src: SharedString,
|
||||
|
@ -39,31 +46,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",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use gpui::{
|
|||
};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{h_stack, Icon, IconButton, IconElement, Label, LineHeightStyle, TextColor};
|
||||
use crate::{h_stack, Color, Icon, IconButton, IconElement, Label, LineHeightStyle};
|
||||
|
||||
/// Provides the flexibility to use either a standard
|
||||
/// button or an icon button in a given context.
|
||||
|
@ -73,7 +73,7 @@ pub struct Button {
|
|||
label: SharedString,
|
||||
variant: ButtonVariant,
|
||||
width: Option<DefiniteLength>,
|
||||
color: Option<TextColor>,
|
||||
color: Option<Color>,
|
||||
}
|
||||
|
||||
impl Component for Button {
|
||||
|
@ -81,9 +81,9 @@ impl Component for Button {
|
|||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
let (icon_color, label_color) = match (self.disabled, self.color) {
|
||||
(true, _) => (TextColor::Disabled, TextColor::Disabled),
|
||||
(_, None) => (TextColor::Default, TextColor::Default),
|
||||
(_, Some(color)) => (TextColor::from(color), color),
|
||||
(true, _) => (Color::Disabled, Color::Disabled),
|
||||
(_, None) => (Color::Default, Color::Default),
|
||||
(_, Some(color)) => (Color::from(color), color),
|
||||
};
|
||||
|
||||
let mut button = h_stack()
|
||||
|
@ -181,14 +181,14 @@ impl Button {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: Option<TextColor>) -> Self {
|
||||
pub fn color(mut self, color: Option<Color>) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn label_color(&self, color: Option<TextColor>) -> TextColor {
|
||||
pub fn label_color(&self, color: Option<Color>) -> Color {
|
||||
if self.disabled {
|
||||
TextColor::Disabled
|
||||
Color::Disabled
|
||||
} else if let Some(color) = color {
|
||||
color
|
||||
} else {
|
||||
|
@ -196,13 +196,13 @@ impl Button {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_label(&self, color: TextColor) -> Label {
|
||||
fn render_label(&self, color: Color) -> Label {
|
||||
Label::new(self.label.clone())
|
||||
.color(color)
|
||||
.line_height_style(LineHeightStyle::UILabel)
|
||||
}
|
||||
|
||||
fn render_icon(&self, icon_color: TextColor) -> Option<IconElement> {
|
||||
fn render_icon(&self, icon_color: Color) -> Option<IconElement> {
|
||||
self.icon.map(|i| IconElement::new(i).color(icon_color))
|
||||
}
|
||||
}
|
||||
|
@ -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.")),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use gpui::{div, prelude::*, Div, Element, ElementId, RenderOnce, Styled, WindowC
|
|||
|
||||
use theme2::ActiveTheme;
|
||||
|
||||
use crate::{Icon, IconElement, Selection, TextColor};
|
||||
use crate::{Color, Icon, IconElement, Selection};
|
||||
|
||||
pub type CheckHandler = Box<dyn Fn(&Selection, &mut WindowContext) + 'static>;
|
||||
|
||||
|
@ -34,9 +34,9 @@ impl Component for Checkbox {
|
|||
.color(
|
||||
// If the checkbox is disabled we change the color of the icon.
|
||||
if self.disabled {
|
||||
TextColor::Disabled
|
||||
Color::Disabled
|
||||
} else {
|
||||
TextColor::Selected
|
||||
Color::Selected
|
||||
},
|
||||
),
|
||||
)
|
||||
|
@ -49,9 +49,9 @@ impl Component for Checkbox {
|
|||
.color(
|
||||
// If the checkbox is disabled we change the color of the icon.
|
||||
if self.disabled {
|
||||
TextColor::Disabled
|
||||
Color::Disabled
|
||||
} else {
|
||||
TextColor::Selected
|
||||
Color::Selected
|
||||
},
|
||||
),
|
||||
)
|
||||
|
@ -176,9 +176,9 @@ impl Checkbox {
|
|||
.color(
|
||||
// If the checkbox is disabled we change the color of the icon.
|
||||
if self.disabled {
|
||||
TextColor::Disabled
|
||||
Color::Disabled
|
||||
} else {
|
||||
TextColor::Selected
|
||||
Color::Selected
|
||||
},
|
||||
),
|
||||
)
|
||||
|
@ -191,9 +191,9 @@ impl Checkbox {
|
|||
.color(
|
||||
// If the checkbox is disabled we change the color of the icon.
|
||||
if self.disabled {
|
||||
TextColor::Disabled
|
||||
Color::Disabled
|
||||
} else {
|
||||
TextColor::Selected
|
||||
Color::Selected
|
||||
},
|
||||
),
|
||||
)
|
||||
|
@ -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),
|
||||
])),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
19
crates/ui2/src/components/disclosure.rs
Normal file
19
crates/ui2/src/components/disclosure.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use gpui::{div, Element, ParentElement};
|
||||
|
||||
use crate::{Color, Icon, IconElement, IconSize, Toggle};
|
||||
|
||||
pub fn disclosure_control(toggle: Toggle) -> impl Element {
|
||||
match (toggle.is_toggleable(), toggle.is_toggled()) {
|
||||
(false, _) => div(),
|
||||
(_, true) => div().child(
|
||||
IconElement::new(Icon::ChevronDown)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
(_, false) => div().child(
|
||||
IconElement::new(Icon::ChevronRight)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
}
|
||||
}
|
|
@ -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};
|
|
@ -23,6 +23,7 @@ pub enum Icon {
|
|||
BellOff,
|
||||
BellRing,
|
||||
Bolt,
|
||||
CaseSensitive,
|
||||
Check,
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
|
@ -65,9 +66,8 @@ pub enum Icon {
|
|||
Split,
|
||||
SplitMessage,
|
||||
Terminal,
|
||||
XCircle,
|
||||
WholeWord,
|
||||
CaseSensitive,
|
||||
XCircle,
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
|
@ -84,6 +84,7 @@ impl Icon {
|
|||
Icon::BellOff => "icons/bell-off.svg",
|
||||
Icon::BellRing => "icons/bell-ring.svg",
|
||||
Icon::Bolt => "icons/bolt.svg",
|
||||
Icon::CaseSensitive => "icons/case_insensitive.svg",
|
||||
Icon::Check => "icons/check.svg",
|
||||
Icon::ChevronDown => "icons/chevron_down.svg",
|
||||
Icon::ChevronLeft => "icons/chevron_left.svg",
|
||||
|
@ -126,9 +127,8 @@ impl Icon {
|
|||
Icon::Split => "icons/split.svg",
|
||||
Icon::SplitMessage => "icons/split_message.svg",
|
||||
Icon::Terminal => "icons/terminal.svg",
|
||||
Icon::XCircle => "icons/error.svg",
|
||||
Icon::WholeWord => "icons/word_search.svg",
|
||||
Icon::CaseSensitive => "icons/case_insensitive.svg",
|
||||
Icon::XCircle => "icons/error.svg",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ impl Icon {
|
|||
#[derive(RenderOnce)]
|
||||
pub struct IconElement {
|
||||
path: SharedString,
|
||||
color: TextColor,
|
||||
color: Color,
|
||||
size: IconSize,
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@ impl IconElement {
|
|||
pub fn new(icon: Icon) -> Self {
|
||||
Self {
|
||||
path: icon.path().into(),
|
||||
color: TextColor::default(),
|
||||
color: Color::default(),
|
||||
size: IconSize::default(),
|
||||
}
|
||||
}
|
||||
|
@ -169,12 +169,12 @@ impl IconElement {
|
|||
pub fn from_path(path: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
path: path.into(),
|
||||
color: TextColor::default(),
|
||||
color: Color::default(),
|
||||
size: IconSize::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: TextColor) -> Self {
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use gpui::{prelude::*, Action, AnyView, Div, MouseButton, MouseDownEvent, Statef
|
|||
pub struct IconButton {
|
||||
id: ElementId,
|
||||
icon: Icon,
|
||||
color: TextColor,
|
||||
color: Color,
|
||||
variant: ButtonVariant,
|
||||
state: InteractionState,
|
||||
selected: bool,
|
||||
|
@ -18,8 +18,8 @@ impl Component for IconButton {
|
|||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
let icon_color = match (self.state, self.color) {
|
||||
(InteractionState::Disabled, _) => TextColor::Disabled,
|
||||
(InteractionState::Active, _) => TextColor::Selected,
|
||||
(InteractionState::Disabled, _) => Color::Disabled,
|
||||
(InteractionState::Active, _) => Color::Selected,
|
||||
_ => self.color,
|
||||
};
|
||||
|
||||
|
@ -76,7 +76,7 @@ impl IconButton {
|
|||
Self {
|
||||
id: id.into(),
|
||||
icon,
|
||||
color: TextColor::default(),
|
||||
color: Color::default(),
|
||||
variant: ButtonVariant::default(),
|
||||
state: InteractionState::default(),
|
||||
selected: false,
|
||||
|
@ -90,7 +90,7 @@ impl IconButton {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: TextColor) -> Self {
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -36,15 +36,15 @@ impl Component for Input {
|
|||
};
|
||||
|
||||
let placeholder_label = Label::new(self.placeholder.clone()).color(if self.disabled {
|
||||
TextColor::Disabled
|
||||
Color::Disabled
|
||||
} else {
|
||||
TextColor::Placeholder
|
||||
Color::Placeholder
|
||||
});
|
||||
|
||||
let label = Label::new(self.value.clone()).color(if self.disabled {
|
||||
TextColor::Disabled
|
||||
Color::Disabled
|
||||
} else {
|
||||
TextColor::Default
|
||||
Color::Default
|
||||
});
|
||||
|
||||
div()
|
||||
|
@ -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")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,48 +9,6 @@ pub enum LabelSize {
|
|||
Small,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum TextColor {
|
||||
#[default]
|
||||
Default,
|
||||
Accent,
|
||||
Created,
|
||||
Deleted,
|
||||
Disabled,
|
||||
Error,
|
||||
Hidden,
|
||||
Info,
|
||||
Modified,
|
||||
Muted,
|
||||
Placeholder,
|
||||
Player(u32),
|
||||
Selected,
|
||||
Success,
|
||||
Warning,
|
||||
}
|
||||
|
||||
impl TextColor {
|
||||
pub fn color(&self, cx: &WindowContext) -> Hsla {
|
||||
match self {
|
||||
TextColor::Default => cx.theme().colors().text,
|
||||
TextColor::Muted => cx.theme().colors().text_muted,
|
||||
TextColor::Created => cx.theme().status().created,
|
||||
TextColor::Modified => cx.theme().status().modified,
|
||||
TextColor::Deleted => cx.theme().status().deleted,
|
||||
TextColor::Disabled => cx.theme().colors().text_disabled,
|
||||
TextColor::Hidden => cx.theme().status().hidden,
|
||||
TextColor::Info => cx.theme().status().info,
|
||||
TextColor::Placeholder => cx.theme().colors().text_placeholder,
|
||||
TextColor::Accent => cx.theme().colors().text_accent,
|
||||
TextColor::Player(i) => cx.theme().styles.player.0[i.clone() as usize].cursor,
|
||||
TextColor::Error => cx.theme().status().error,
|
||||
TextColor::Selected => cx.theme().colors().text_accent,
|
||||
TextColor::Success => cx.theme().status().success,
|
||||
TextColor::Warning => cx.theme().status().warning,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum LineHeightStyle {
|
||||
#[default]
|
||||
|
@ -64,7 +22,7 @@ pub struct Label {
|
|||
label: SharedString,
|
||||
size: LabelSize,
|
||||
line_height_style: LineHeightStyle,
|
||||
color: TextColor,
|
||||
color: Color,
|
||||
strikethrough: bool,
|
||||
}
|
||||
|
||||
|
@ -80,7 +38,7 @@ impl Component for Label {
|
|||
.top_1_2()
|
||||
.w_full()
|
||||
.h_px()
|
||||
.bg(TextColor::Hidden.color(cx)),
|
||||
.bg(Color::Hidden.color(cx)),
|
||||
)
|
||||
})
|
||||
.map(|this| match self.size {
|
||||
|
@ -101,7 +59,7 @@ impl Label {
|
|||
label: label.into(),
|
||||
size: LabelSize::Default,
|
||||
line_height_style: LineHeightStyle::default(),
|
||||
color: TextColor::Default,
|
||||
color: Color::Default,
|
||||
strikethrough: false,
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +69,7 @@ impl Label {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: TextColor) -> Self {
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
@ -131,7 +89,7 @@ impl Label {
|
|||
pub struct HighlightedLabel {
|
||||
label: SharedString,
|
||||
size: LabelSize,
|
||||
color: TextColor,
|
||||
color: Color,
|
||||
highlight_indices: Vec<usize>,
|
||||
strikethrough: bool,
|
||||
}
|
||||
|
@ -185,7 +143,7 @@ impl Component for HighlightedLabel {
|
|||
.my_auto()
|
||||
.w_full()
|
||||
.h_px()
|
||||
.bg(TextColor::Hidden.color(cx)),
|
||||
.bg(Color::Hidden.color(cx)),
|
||||
)
|
||||
})
|
||||
.map(|this| match self.size {
|
||||
|
@ -203,7 +161,7 @@ impl HighlightedLabel {
|
|||
Self {
|
||||
label: label.into(),
|
||||
size: LabelSize::Default,
|
||||
color: TextColor::Default,
|
||||
color: Color::Default,
|
||||
highlight_indices,
|
||||
strikethrough: false,
|
||||
}
|
||||
|
@ -214,7 +172,7 @@ impl HighlightedLabel {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: TextColor) -> Self {
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
@ -230,35 +188,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],
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ impl Component for ListHeader {
|
|||
.items_center()
|
||||
.children(icons.into_iter().map(|i| {
|
||||
IconElement::new(i)
|
||||
.color(TextColor::Muted)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small)
|
||||
})),
|
||||
),
|
||||
|
@ -80,10 +80,10 @@ impl Component for ListHeader {
|
|||
.items_center()
|
||||
.children(self.left_icon.map(|i| {
|
||||
IconElement::new(i)
|
||||
.color(TextColor::Muted)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small)
|
||||
}))
|
||||
.child(Label::new(self.label.clone()).color(TextColor::Muted)),
|
||||
.child(Label::new(self.label.clone()).color(Color::Muted)),
|
||||
)
|
||||
.child(disclosure_control),
|
||||
)
|
||||
|
@ -222,10 +222,10 @@ impl Component for ListSubHeader {
|
|||
.items_center()
|
||||
.children(self.left_icon.map(|i| {
|
||||
IconElement::new(i)
|
||||
.color(TextColor::Muted)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small)
|
||||
}))
|
||||
.child(Label::new(self.label.clone()).color(TextColor::Muted)),
|
||||
.child(Label::new(self.label.clone()).color(Color::Muted)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ impl Component for ListItem {
|
|||
h_stack().child(
|
||||
IconElement::new(i)
|
||||
.size(IconSize::Small)
|
||||
.color(TextColor::Muted),
|
||||
.color(Color::Muted),
|
||||
),
|
||||
),
|
||||
Some(GraphicSlot::Avatar(src)) => Some(h_stack().child(Avatar::new(src))),
|
||||
|
@ -432,9 +432,7 @@ impl Component for List {
|
|||
let list_content = match (self.children.is_empty(), self.toggle) {
|
||||
(false, _) => div().children(self.children),
|
||||
(true, Toggle::Toggled(false)) => div(),
|
||||
(true, _) => {
|
||||
div().child(Label::new(self.empty_message.clone()).color(TextColor::Muted))
|
||||
}
|
||||
(true, _) => div().child(Label::new(self.empty_message.clone()).color(Color::Muted)),
|
||||
};
|
||||
|
||||
v_stack()
|
||||
|
|
|
@ -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, Color, Story};
|
||||
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],
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
1
crates/ui2/src/components/stories/mod.rs
Normal file
1
crates/ui2/src/components/stories/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
|
@ -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,7 +1,3 @@
|
|||
use gpui::{div, Element, ParentElement};
|
||||
|
||||
use crate::{Icon, IconElement, IconSize, TextColor};
|
||||
|
||||
/// Whether the entry is toggleable, and if so, whether it is currently toggled.
|
||||
///
|
||||
/// To make an element toggleable, simply add a `Toggle::Toggled(_)` and handle it's cases.
|
||||
|
@ -43,19 +39,3 @@ impl From<bool> for Toggle {
|
|||
Toggle::Toggled(toggled)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disclosure_control(toggle: Toggle) -> impl Element {
|
||||
match (toggle.is_toggleable(), toggle.is_toggled()) {
|
||||
(false, _) => div(),
|
||||
(_, true) => div().child(
|
||||
IconElement::new(Icon::ChevronDown)
|
||||
.color(TextColor::Muted)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
(_, false) => div().child(
|
||||
IconElement::new(Icon::ChevronRight)
|
||||
.color(TextColor::Muted)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ use settings2::Settings;
|
|||
use theme2::{ActiveTheme, ThemeSettings};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{h_stack, v_stack, KeyBinding, Label, LabelSize, StyledExt, TextColor};
|
||||
use crate::{h_stack, v_stack, Color, KeyBinding, Label, LabelSize, StyledExt};
|
||||
|
||||
pub struct Tooltip {
|
||||
title: SharedString,
|
||||
|
@ -90,11 +90,7 @@ impl Render for Tooltip {
|
|||
}),
|
||||
)
|
||||
.when_some(self.meta.clone(), |this, meta| {
|
||||
this.child(
|
||||
Label::new(meta)
|
||||
.size(LabelSize::Small)
|
||||
.color(TextColor::Muted),
|
||||
)
|
||||
this.child(Label::new(meta).size(LabelSize::Small).color(Color::Muted))
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -1,116 +1,14 @@
|
|||
use gpui::rems;
|
||||
use gpui::Rems;
|
||||
pub use gpui::{
|
||||
div, Component, Element, ElementId, InteractiveElement, ParentElement, SharedString, Styled,
|
||||
ViewContext, WindowContext,
|
||||
};
|
||||
|
||||
pub use crate::elevation::*;
|
||||
pub use crate::StyledExt;
|
||||
pub use crate::{ButtonVariant, TextColor};
|
||||
pub use crate::{ButtonVariant, Color};
|
||||
pub use theme2::ActiveTheme;
|
||||
|
||||
use gpui::Hsla;
|
||||
use strum::EnumIter;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum UITextSize {
|
||||
/// The default size for UI text.
|
||||
///
|
||||
/// `0.825rem` or `14px` at the default scale of `1rem` = `16px`.
|
||||
///
|
||||
/// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
|
||||
#[default]
|
||||
Default,
|
||||
/// The small size for UI text.
|
||||
///
|
||||
/// `0.75rem` or `12px` at the default scale of `1rem` = `16px`.
|
||||
///
|
||||
/// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
|
||||
Small,
|
||||
}
|
||||
|
||||
impl UITextSize {
|
||||
pub fn rems(self) -> Rems {
|
||||
match self {
|
||||
Self::Default => rems(0.875),
|
||||
Self::Small => rems(0.75),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum FileSystemStatus {
|
||||
#[default]
|
||||
None,
|
||||
Conflict,
|
||||
Deleted,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FileSystemStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::None => "None",
|
||||
Self::Conflict => "Conflict",
|
||||
Self::Deleted => "Deleted",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum GitStatus {
|
||||
#[default]
|
||||
None,
|
||||
Created,
|
||||
Modified,
|
||||
Deleted,
|
||||
Conflict,
|
||||
Renamed,
|
||||
}
|
||||
|
||||
impl GitStatus {
|
||||
pub fn hsla(&self, cx: &WindowContext) -> Hsla {
|
||||
match self {
|
||||
Self::None => cx.theme().system().transparent,
|
||||
Self::Created => cx.theme().status().created,
|
||||
Self::Modified => cx.theme().status().modified,
|
||||
Self::Deleted => cx.theme().status().deleted,
|
||||
Self::Conflict => cx.theme().status().conflict,
|
||||
Self::Renamed => cx.theme().status().renamed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GitStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::None => "None",
|
||||
Self::Created => "Created",
|
||||
Self::Modified => "Modified",
|
||||
Self::Deleted => "Deleted",
|
||||
Self::Conflict => "Conflict",
|
||||
Self::Renamed => "Renamed",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum DiagnosticStatus {
|
||||
#[default]
|
||||
None,
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum IconSide {
|
||||
#[default]
|
||||
|
@ -118,45 +16,6 @@ pub enum IconSide {
|
|||
Right,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum OrderMethod {
|
||||
#[default]
|
||||
Ascending,
|
||||
Descending,
|
||||
MostRecent,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum Shape {
|
||||
#[default]
|
||||
Circle,
|
||||
RoundedRectangle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum DisclosureControlVisibility {
|
||||
#[default]
|
||||
OnHover,
|
||||
Always,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum DisclosureControlStyle {
|
||||
/// Shows the disclosure control only when hovered where possible.
|
||||
///
|
||||
/// More compact, but not available everywhere.
|
||||
ChevronOnHover,
|
||||
/// Shows an icon where possible, otherwise shows a chevron.
|
||||
///
|
||||
/// For example, in a file tree a folder or file icon is shown
|
||||
/// instead of a chevron
|
||||
Icon,
|
||||
/// Always shows a chevron.
|
||||
Chevron,
|
||||
/// Completely hides the disclosure control where possible.
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumIter)]
|
||||
pub enum OverflowStyle {
|
||||
Hidden,
|
||||
|
|
|
@ -78,8 +78,6 @@ pub trait StyledExt: Styled + Sized {
|
|||
elevated(self, cx, ElevationIndex::ElevatedSurface)
|
||||
}
|
||||
|
||||
// There is no elevation 3, as the third elevation level is reserved for wash layers. See [`Elevation`](ui2::Elevation).
|
||||
|
||||
/// Modal Surfaces are used for elements that should appear above all other UI elements and are located above the wash layer. This is the maximum elevation at which UI elements can be rendered in their default state.
|
||||
///
|
||||
/// Elements rendered at this layer should have an enforced behavior: Any interaction outside of the modal will either dismiss the modal or prompt an action (Save your progress, etc) then dismiss the modal.
|
||||
|
@ -89,7 +87,7 @@ pub trait StyledExt: Styled + Sized {
|
|||
/// Sets `bg()`, `rounded_lg()`, `border()`, `border_color()`, `shadow()`
|
||||
///
|
||||
/// Examples: Settings Modal, Channel Management, Wizards/Setup UI, Dialogs
|
||||
fn elevation_4(self, cx: &mut WindowContext) -> Self {
|
||||
fn elevation_3(self, cx: &mut WindowContext) -> Self {
|
||||
elevated(self, cx, ElevationIndex::ModalSurface)
|
||||
}
|
||||
}
|
||||
|
|
7
crates/ui2/src/styles.rs
Normal file
7
crates/ui2/src/styles.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
mod color;
|
||||
mod elevation;
|
||||
mod typography;
|
||||
|
||||
pub use color::*;
|
||||
pub use elevation::*;
|
||||
pub use typography::*;
|
44
crates/ui2/src/styles/color.rs
Normal file
44
crates/ui2/src/styles/color.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use gpui::{Hsla, WindowContext};
|
||||
use theme2::ActiveTheme;
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum Color {
|
||||
#[default]
|
||||
Default,
|
||||
Accent,
|
||||
Created,
|
||||
Deleted,
|
||||
Disabled,
|
||||
Error,
|
||||
Hidden,
|
||||
Info,
|
||||
Modified,
|
||||
Muted,
|
||||
Placeholder,
|
||||
Player(u32),
|
||||
Selected,
|
||||
Success,
|
||||
Warning,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn color(&self, cx: &WindowContext) -> Hsla {
|
||||
match self {
|
||||
Color::Default => cx.theme().colors().text,
|
||||
Color::Muted => cx.theme().colors().text_muted,
|
||||
Color::Created => cx.theme().status().created,
|
||||
Color::Modified => cx.theme().status().modified,
|
||||
Color::Deleted => cx.theme().status().deleted,
|
||||
Color::Disabled => cx.theme().colors().text_disabled,
|
||||
Color::Hidden => cx.theme().status().hidden,
|
||||
Color::Info => cx.theme().status().info,
|
||||
Color::Placeholder => cx.theme().colors().text_placeholder,
|
||||
Color::Accent => cx.theme().colors().text_accent,
|
||||
Color::Player(i) => cx.theme().styles.player.0[i.clone() as usize].cursor,
|
||||
Color::Error => cx.theme().status().error,
|
||||
Color::Selected => cx.theme().colors().text_accent,
|
||||
Color::Success => cx.theme().status().success,
|
||||
Color::Warning => cx.theme().status().warning,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +1,10 @@
|
|||
TODO: Originally sourced from Material Design 3, Rewrite to be more Zed specific
|
||||
|
||||
# Elevation
|
||||
|
||||
Zed applies elevation to all surfaces and components, which are categorized into levels.
|
||||
|
||||
Elevation accomplishes the following:
|
||||
- Allows surfaces to move in front of or behind others, such as content scrolling beneath app top bars.
|
||||
- Reflects spatial relationships, for instance, how a floating action button’s shadow intimates its disconnection from a collection of cards.
|
||||
- Directs attention to structures at the highest elevation, like a temporary dialog arising in front of other surfaces.
|
||||
|
||||
Elevations are the initial elevation values assigned to components by default.
|
||||
|
||||
Components may transition to a higher elevation in some cases, like user interations.
|
||||
|
||||
On such occasions, components transition to predetermined dynamic elevation offsets. These are the typical elevations to which components move when they are not at rest.
|
||||
|
||||
## Understanding Elevation
|
||||
|
||||
Elevation can be thought of as the physical closeness of an element to the user. Elements with lower elevations are physically further away from the user on the z-axis and appear to be underneath elements with higher elevations.
|
||||
|
||||
Material Design 3 has a some great visualizations of elevation that may be helpful to understanding the mental modal of elevation. [Material Design – Elevation](https://m3.material.io/styles/elevation/overview)
|
||||
|
||||
## Elevation
|
||||
## Elevation Levels
|
||||
|
||||
1. App Background (e.x.: Workspace, system window)
|
||||
1. UI Surface (e.x.: Title Bar, Panel, Tab Bar)
|
||||
|
@ -59,27 +42,3 @@ Modal Surfaces are used for elements that should appear above all other UI eleme
|
|||
Elements rendered at this layer have an enforced behavior: Any interaction outside of the modal will either dismiss the modal or prompt an action (Save your progress, etc) then dismiss the modal.
|
||||
|
||||
If the element does not have this behavior, it should be rendered at the Elevated Surface layer.
|
||||
|
||||
## Layer
|
||||
Each elevation that can contain elements has its own set of layers that are nested within the elevations.
|
||||
|
||||
1. TBD (Z -1 layer)
|
||||
1. Element (Text, button, surface, etc)
|
||||
1. Elevated Element (Popover, Context Menu, Tooltip)
|
||||
999. Dragged Element -> Highest Elevation
|
||||
|
||||
Dragged elements jump to the highest elevation the app can render. An active drag should _always_ be the most foreground element in the app at any time.
|
||||
|
||||
🚧 Work in Progress 🚧
|
||||
|
||||
## Element
|
||||
Each elevation that can contain elements has it's own set of layers:
|
||||
|
||||
1. Effects
|
||||
1. Background
|
||||
1. Tint
|
||||
1. Highlight
|
||||
1. Content
|
||||
1. Overlay
|
||||
|
||||
🚧 Work in Progress 🚧
|
|
@ -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),
|
||||
|
@ -25,8 +25,8 @@ impl ElevationIndex {
|
|||
ElevationIndex::Background => 0,
|
||||
ElevationIndex::Surface => 100,
|
||||
ElevationIndex::ElevatedSurface => 200,
|
||||
ElevationIndex::Wash => 300,
|
||||
ElevationIndex::ModalSurface => 400,
|
||||
ElevationIndex::Wash => 250,
|
||||
ElevationIndex::ModalSurface => 300,
|
||||
ElevationIndex::DraggedElement => 900,
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ impl ElevationIndex {
|
|||
spread_radius: px(0.),
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0., 0., 0., 0.16),
|
||||
color: hsla(0., 0., 0., 0.20),
|
||||
offset: point(px(3.), px(1.)),
|
||||
blur_radius: px(12.),
|
||||
spread_radius: px(0.),
|
27
crates/ui2/src/styles/typography.rs
Normal file
27
crates/ui2/src/styles/typography.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use gpui::{rems, Rems};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub enum UITextSize {
|
||||
/// The default size for UI text.
|
||||
///
|
||||
/// `0.825rem` or `14px` at the default scale of `1rem` = `16px`.
|
||||
///
|
||||
/// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
|
||||
#[default]
|
||||
Default,
|
||||
/// The small size for UI text.
|
||||
///
|
||||
/// `0.75rem` or `12px` at the default scale of `1rem` = `16px`.
|
||||
///
|
||||
/// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
|
||||
Small,
|
||||
}
|
||||
|
||||
impl UITextSize {
|
||||
pub fn rems(self) -> Rems {
|
||||
match self {
|
||||
Self::Default => rems(0.875),
|
||||
Self::Small => rems(0.75),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -26,7 +26,7 @@ use std::{
|
|||
};
|
||||
|
||||
use ui::v_stack;
|
||||
use ui::{prelude::*, Icon, IconButton, IconElement, TextColor, Tooltip};
|
||||
use ui::{prelude::*, Color, Icon, IconButton, IconElement, Tooltip};
|
||||
use util::truncate_and_remove_front;
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
|
||||
|
@ -1425,12 +1425,12 @@ impl Pane {
|
|||
.then(|| {
|
||||
IconElement::new(Icon::ExclamationTriangle)
|
||||
.size(ui::IconSize::Small)
|
||||
.color(TextColor::Warning)
|
||||
.color(Color::Warning)
|
||||
})
|
||||
.or(item.is_dirty(cx).then(|| {
|
||||
IconElement::new(Icon::ExclamationTriangle)
|
||||
.size(ui::IconSize::Small)
|
||||
.color(TextColor::Info)
|
||||
.color(Color::Info)
|
||||
})),
|
||||
)
|
||||
.children((!close_right).then(|| close_icon()))
|
||||
|
|
|
@ -4,7 +4,7 @@ use gpui::{
|
|||
ViewContext, WindowContext,
|
||||
};
|
||||
use theme2::ActiveTheme;
|
||||
use ui::{h_stack, v_stack, Button, Icon, IconButton, Label, TextColor};
|
||||
use ui::{h_stack, v_stack, Button, Color, Icon, IconButton, Label};
|
||||
|
||||
pub enum ToolbarItemEvent {
|
||||
ChangeLocation(ToolbarItemLocation),
|
||||
|
@ -92,7 +92,7 @@ impl Render for Toolbar {
|
|||
h_stack()
|
||||
.p_1()
|
||||
.child(Button::new("crates"))
|
||||
.child(Label::new("/").color(TextColor::Muted))
|
||||
.child(Label::new("/").color(Color::Muted))
|
||||
.child(Button::new("workspace2")),
|
||||
)
|
||||
// Toolbar right side
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue