Merge branch 'main' into import-theme
This commit is contained in:
@@ -1,71 +1,51 @@
|
||||
mod assistant_panel;
|
||||
mod breadcrumb;
|
||||
mod buffer;
|
||||
mod buffer_search;
|
||||
mod chat_panel;
|
||||
mod collab_panel;
|
||||
mod command_palette;
|
||||
mod avatar;
|
||||
mod button;
|
||||
mod checkbox;
|
||||
mod context_menu;
|
||||
mod copilot;
|
||||
mod editor_pane;
|
||||
mod details;
|
||||
mod facepile;
|
||||
mod icon;
|
||||
mod icon_button;
|
||||
mod indicator;
|
||||
mod input;
|
||||
mod keybinding;
|
||||
mod language_selector;
|
||||
mod label;
|
||||
mod list;
|
||||
mod modal;
|
||||
mod multi_buffer;
|
||||
mod notification_toast;
|
||||
mod notifications_panel;
|
||||
mod palette;
|
||||
mod panel;
|
||||
mod panes;
|
||||
mod player;
|
||||
mod player_stack;
|
||||
mod project_panel;
|
||||
mod recent_projects;
|
||||
mod status_bar;
|
||||
mod slot;
|
||||
mod stack;
|
||||
mod tab;
|
||||
mod tab_bar;
|
||||
mod terminal;
|
||||
mod theme_selector;
|
||||
mod title_bar;
|
||||
mod toast;
|
||||
mod toolbar;
|
||||
mod traffic_lights;
|
||||
mod workspace;
|
||||
mod toggle;
|
||||
mod tool_divider;
|
||||
|
||||
pub use assistant_panel::*;
|
||||
pub use breadcrumb::*;
|
||||
pub use buffer::*;
|
||||
pub use buffer_search::*;
|
||||
pub use chat_panel::*;
|
||||
pub use collab_panel::*;
|
||||
pub use command_palette::*;
|
||||
pub use avatar::*;
|
||||
pub use button::*;
|
||||
pub use checkbox::*;
|
||||
pub use context_menu::*;
|
||||
pub use copilot::*;
|
||||
pub use editor_pane::*;
|
||||
pub use details::*;
|
||||
pub use facepile::*;
|
||||
pub use icon::*;
|
||||
pub use icon_button::*;
|
||||
pub use indicator::*;
|
||||
pub use input::*;
|
||||
pub use keybinding::*;
|
||||
pub use language_selector::*;
|
||||
pub use label::*;
|
||||
pub use list::*;
|
||||
pub use modal::*;
|
||||
pub use multi_buffer::*;
|
||||
pub use notification_toast::*;
|
||||
pub use notifications_panel::*;
|
||||
pub use palette::*;
|
||||
pub use panel::*;
|
||||
pub use panes::*;
|
||||
pub use player::*;
|
||||
pub use player_stack::*;
|
||||
pub use project_panel::*;
|
||||
pub use recent_projects::*;
|
||||
pub use status_bar::*;
|
||||
pub use slot::*;
|
||||
pub use stack::*;
|
||||
pub use tab::*;
|
||||
pub use tab_bar::*;
|
||||
pub use terminal::*;
|
||||
pub use theme_selector::*;
|
||||
pub use title_bar::*;
|
||||
pub use toast::*;
|
||||
pub use toolbar::*;
|
||||
pub use traffic_lights::*;
|
||||
pub use workspace::*;
|
||||
pub use toggle::*;
|
||||
pub use tool_divider::*;
|
||||
|
||||
@@ -2,8 +2,27 @@ use std::sync::Arc;
|
||||
|
||||
use gpui2::{div, rems, DefiniteLength, Hsla, MouseButton, WindowContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{h_stack, Icon, IconColor, IconElement, Label, LabelColor, LineHeightStyle};
|
||||
use crate::{prelude::*, IconButton};
|
||||
|
||||
/// Provides the flexibility to use either a standard
|
||||
/// button or an icon button in a given context.
|
||||
pub enum ButtonOrIconButton<V: 'static> {
|
||||
Button(Button<V>),
|
||||
IconButton(IconButton<V>),
|
||||
}
|
||||
|
||||
impl<V: 'static> From<Button<V>> for ButtonOrIconButton<V> {
|
||||
fn from(value: Button<V>) -> Self {
|
||||
Self::Button(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> From<IconButton<V>> for ButtonOrIconButton<V> {
|
||||
fn from(value: IconButton<V>) -> Self {
|
||||
Self::IconButton(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Clone, Copy)]
|
||||
pub enum IconPosition {
|
||||
@@ -0,0 +1,220 @@
|
||||
///! # Checkbox
|
||||
///!
|
||||
///! Checkboxes are used for multiple choices, not for mutually exclusive choices.
|
||||
///! Each checkbox works independently from other checkboxes in the list,
|
||||
///! therefore checking an additional box does not affect any other selections.
|
||||
use gpui2::{
|
||||
div, Component, ParentElement, SharedString, StatelessInteractive, Styled, ViewContext,
|
||||
};
|
||||
use theme2::ActiveTheme;
|
||||
|
||||
use crate::{Icon, IconColor, IconElement, Selected};
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Checkbox {
|
||||
id: SharedString,
|
||||
checked: Selected,
|
||||
disabled: bool,
|
||||
}
|
||||
|
||||
impl Checkbox {
|
||||
pub fn new(id: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
checked: Selected::Unselected,
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle(mut self) -> Self {
|
||||
self.checked = match self.checked {
|
||||
Selected::Selected => Selected::Unselected,
|
||||
Selected::Unselected => Selected::Selected,
|
||||
Selected::Indeterminate => Selected::Selected,
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_indeterminate(mut self) -> Self {
|
||||
self.checked = Selected::Indeterminate;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_disabled(mut self, disabled: bool) -> Self {
|
||||
self.disabled = disabled;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let group_id = format!("checkbox_group_{}", self.id);
|
||||
|
||||
// The icon is different depending on the state of the checkbox.
|
||||
//
|
||||
// We need the match to return all the same type,
|
||||
// so we wrap the eatch result in a div.
|
||||
//
|
||||
// We are still exploring the best way to handle this.
|
||||
let icon = match self.checked {
|
||||
// When selected, we show a checkmark.
|
||||
Selected::Selected => {
|
||||
div().child(
|
||||
IconElement::new(Icon::Check)
|
||||
.size(crate::IconSize::Small)
|
||||
.color(
|
||||
// If the checkbox is disabled we change the color of the icon.
|
||||
if self.disabled {
|
||||
IconColor::Disabled
|
||||
} else {
|
||||
IconColor::Selected
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
// In an indeterminate state, we show a dash.
|
||||
Selected::Indeterminate => {
|
||||
div().child(
|
||||
IconElement::new(Icon::Dash)
|
||||
.size(crate::IconSize::Small)
|
||||
.color(
|
||||
// If the checkbox is disabled we change the color of the icon.
|
||||
if self.disabled {
|
||||
IconColor::Disabled
|
||||
} else {
|
||||
IconColor::Selected
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
// When unselected, we show nothing.
|
||||
Selected::Unselected => div(),
|
||||
};
|
||||
|
||||
// A checkbox could be in an indeterminate state,
|
||||
// for example the indeterminate state could represent:
|
||||
// - a group of options of which only some are selected
|
||||
// - an enabled option that is no longer available
|
||||
// - a previously agreed to license that has been updated
|
||||
//
|
||||
// For the sake of styles we treat the indeterminate state as selected,
|
||||
// but it's icon will be different.
|
||||
let selected =
|
||||
self.checked == Selected::Selected || self.checked == Selected::Indeterminate;
|
||||
|
||||
// We could use something like this to make the checkbox background when selected:
|
||||
//
|
||||
// ~~~rust
|
||||
// ...
|
||||
// .when(selected, |this| {
|
||||
// this.bg(cx.theme().colors().element_selected)
|
||||
// })
|
||||
// ~~~
|
||||
//
|
||||
// But we use a match instead here because the checkbox might be disabled,
|
||||
// and it could be disabled _while_ it is selected, as well as while it is not selected.
|
||||
let (bg_color, border_color) = match (self.disabled, selected) {
|
||||
(true, _) => (
|
||||
cx.theme().colors().ghost_element_disabled,
|
||||
cx.theme().colors().border_disabled,
|
||||
),
|
||||
(false, true) => (
|
||||
cx.theme().colors().element_selected,
|
||||
cx.theme().colors().border,
|
||||
),
|
||||
(false, false) => (
|
||||
cx.theme().colors().element_background,
|
||||
cx.theme().colors().border,
|
||||
),
|
||||
};
|
||||
|
||||
div()
|
||||
// Rather than adding `px_1()` to add some space around the checkbox,
|
||||
// we use a larger parent element to create a slightly larger
|
||||
// click area for the checkbox.
|
||||
.size_5()
|
||||
// Because we've enlarged the click area, we need to create a
|
||||
// `group` to pass down interaction events to the checkbox.
|
||||
.group(group_id.clone())
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
// This prevent the flex element from growing
|
||||
// or shrinking in response to any size changes
|
||||
.flex_none()
|
||||
// The combo of `justify_center()` and `items_center()`
|
||||
// is used frequently to center elements in a flex container.
|
||||
//
|
||||
// We use this to center the icon in the checkbox.
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.m_1()
|
||||
.size_4()
|
||||
.rounded_sm()
|
||||
.bg(bg_color)
|
||||
.border()
|
||||
.border_color(border_color)
|
||||
// We only want the interaction states to fire when we
|
||||
// are in a checkbox that isn't disabled.
|
||||
.when(!self.disabled, |this| {
|
||||
// Here instead of `hover()` we use `group_hover()`
|
||||
// to pass it the group id.
|
||||
this.group_hover(group_id.clone(), |el| {
|
||||
el.bg(cx.theme().colors().element_hover)
|
||||
})
|
||||
})
|
||||
.child(icon),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{h_stack, Story};
|
||||
use gpui2::{Div, Render};
|
||||
|
||||
pub struct CheckboxStory;
|
||||
|
||||
impl Render for CheckboxStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
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"))
|
||||
.child(Checkbox::new("checkbox-intermediate").set_indeterminate())
|
||||
.child(Checkbox::new("checkbox-selected").toggle()),
|
||||
)
|
||||
.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").set_disabled(true))
|
||||
.child(
|
||||
Checkbox::new("checkbox-disabled-intermediate")
|
||||
.set_disabled(true)
|
||||
.set_indeterminate(),
|
||||
)
|
||||
.child(
|
||||
Checkbox::new("checkbox-disabled-selected")
|
||||
.set_disabled(true)
|
||||
.toggle(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ pub enum ContextMenuItem {
|
||||
}
|
||||
|
||||
impl ContextMenuItem {
|
||||
fn to_list_item<V: 'static>(self) -> ListItem<V> {
|
||||
fn to_list_item<V: 'static>(self) -> ListItem {
|
||||
match self {
|
||||
ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
|
||||
ContextMenuItem::Entry(label) => {
|
||||
@@ -49,15 +49,12 @@ impl ContextMenu {
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.border()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(
|
||||
List::new(
|
||||
self.items
|
||||
.into_iter()
|
||||
.map(ContextMenuItem::to_list_item)
|
||||
.collect(),
|
||||
)
|
||||
.toggle(ToggleState::Toggled),
|
||||
)
|
||||
.child(List::new(
|
||||
self.items
|
||||
.into_iter()
|
||||
.map(ContextMenuItem::to_list_item::<V>)
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ pub enum IconColor {
|
||||
Warning,
|
||||
Success,
|
||||
Info,
|
||||
Selected,
|
||||
}
|
||||
|
||||
impl IconColor {
|
||||
@@ -36,6 +37,7 @@ impl IconColor {
|
||||
IconColor::Warning => cx.theme().status().warning,
|
||||
IconColor::Success => cx.theme().status().success,
|
||||
IconColor::Info => cx.theme().status().info,
|
||||
IconColor::Selected => cx.theme().colors().icon_accent,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,6 +57,7 @@ pub enum Icon {
|
||||
ChevronRight,
|
||||
ChevronUp,
|
||||
Close,
|
||||
Dash,
|
||||
Exit,
|
||||
ExclamationTriangle,
|
||||
File,
|
||||
@@ -112,6 +115,7 @@ impl Icon {
|
||||
Icon::ChevronRight => "icons/chevron_right.svg",
|
||||
Icon::ChevronUp => "icons/chevron_up.svg",
|
||||
Icon::Close => "icons/x.svg",
|
||||
Icon::Dash => "icons/dash.svg",
|
||||
Icon::Exit => "icons/exit.svg",
|
||||
Icon::ExclamationTriangle => "icons/warning.svg",
|
||||
Icon::File => "icons/file.svg",
|
||||
@@ -1,11 +1,11 @@
|
||||
use gpui2::{div, px, relative, Div};
|
||||
use gpui2::div;
|
||||
|
||||
use crate::settings::user_settings;
|
||||
use crate::{
|
||||
h_stack, v_stack, Avatar, ClickHandler, Icon, IconColor, IconElement, IconSize, Label,
|
||||
LabelColor,
|
||||
disclosure_control, h_stack, v_stack, Avatar, Icon, IconColor, IconElement, IconSize, Label,
|
||||
LabelColor, Toggle,
|
||||
};
|
||||
use crate::{prelude::*, Button};
|
||||
use crate::{prelude::*, GraphicSlot};
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug, PartialEq)]
|
||||
pub enum ListItemVariant {
|
||||
@@ -29,7 +29,7 @@ pub struct ListHeader {
|
||||
left_icon: Option<Icon>,
|
||||
meta: Option<ListHeaderMeta>,
|
||||
variant: ListItemVariant,
|
||||
toggleable: Toggleable,
|
||||
toggle: Toggle,
|
||||
}
|
||||
|
||||
impl ListHeader {
|
||||
@@ -39,17 +39,12 @@ impl ListHeader {
|
||||
left_icon: None,
|
||||
meta: None,
|
||||
variant: ListItemVariant::default(),
|
||||
toggleable: Toggleable::NotToggleable,
|
||||
toggle: Toggle::NotToggleable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle(mut self, toggle: ToggleState) -> Self {
|
||||
self.toggleable = toggle.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn toggleable(mut self, toggleable: Toggleable) -> Self {
|
||||
self.toggleable = toggleable;
|
||||
pub fn toggle(mut self, toggle: Toggle) -> Self {
|
||||
self.toggle = toggle;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -63,30 +58,8 @@ impl ListHeader {
|
||||
self
|
||||
}
|
||||
|
||||
fn disclosure_control<V: 'static>(&self) -> Div<V> {
|
||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
||||
let is_toggled = Toggleable::is_toggled(&self.toggleable);
|
||||
|
||||
match (is_toggleable, is_toggled) {
|
||||
(false, _) => div(),
|
||||
(_, true) => div().child(
|
||||
IconElement::new(Icon::ChevronDown)
|
||||
.color(IconColor::Muted)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
(_, false) => div().child(
|
||||
IconElement::new(Icon::ChevronRight)
|
||||
.color(IconColor::Muted)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
||||
let is_toggled = self.toggleable.is_toggled();
|
||||
|
||||
let disclosure_control = self.disclosure_control();
|
||||
let disclosure_control = disclosure_control(self.toggle);
|
||||
|
||||
let meta = match self.meta {
|
||||
Some(ListHeaderMeta::Tools(icons)) => div().child(
|
||||
@@ -193,12 +166,6 @@ impl ListSubHeader {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum LeftContent {
|
||||
Icon(Icon),
|
||||
Avatar(SharedString),
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum ListEntrySize {
|
||||
#[default]
|
||||
@@ -207,44 +174,36 @@ pub enum ListEntrySize {
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub enum ListItem<V: 'static> {
|
||||
pub enum ListItem {
|
||||
Entry(ListEntry),
|
||||
Details(ListDetailsEntry<V>),
|
||||
Separator(ListSeparator),
|
||||
Header(ListSubHeader),
|
||||
}
|
||||
|
||||
impl<V: 'static> From<ListEntry> for ListItem<V> {
|
||||
impl From<ListEntry> for ListItem {
|
||||
fn from(entry: ListEntry) -> Self {
|
||||
Self::Entry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> From<ListDetailsEntry<V>> for ListItem<V> {
|
||||
fn from(entry: ListDetailsEntry<V>) -> Self {
|
||||
Self::Details(entry)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> From<ListSeparator> for ListItem<V> {
|
||||
impl From<ListSeparator> for ListItem {
|
||||
fn from(entry: ListSeparator) -> Self {
|
||||
Self::Separator(entry)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> From<ListSubHeader> for ListItem<V> {
|
||||
impl From<ListSubHeader> for ListItem {
|
||||
fn from(entry: ListSubHeader) -> Self {
|
||||
Self::Header(entry)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ListItem<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
impl ListItem {
|
||||
fn render<V: 'static>(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
match self {
|
||||
ListItem::Entry(entry) => div().child(entry.render(view, cx)),
|
||||
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
|
||||
ListItem::Header(header) => div().child(header.render(view, cx)),
|
||||
ListItem::Details(details) => div().child(details.render(view, cx)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,31 +222,29 @@ impl<V: 'static> ListItem<V> {
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct ListEntry {
|
||||
disclosure_control_style: DisclosureControlVisibility,
|
||||
disabled: bool,
|
||||
// TODO: Reintroduce this
|
||||
// disclosure_control_style: DisclosureControlVisibility,
|
||||
indent_level: u32,
|
||||
label: Label,
|
||||
left_content: Option<LeftContent>,
|
||||
variant: ListItemVariant,
|
||||
size: ListEntrySize,
|
||||
state: InteractionState,
|
||||
toggle: Option<ToggleState>,
|
||||
left_slot: Option<GraphicSlot>,
|
||||
overflow: OverflowStyle,
|
||||
size: ListEntrySize,
|
||||
toggle: Toggle,
|
||||
variant: ListItemVariant,
|
||||
}
|
||||
|
||||
impl ListEntry {
|
||||
pub fn new(label: Label) -> Self {
|
||||
Self {
|
||||
disclosure_control_style: DisclosureControlVisibility::default(),
|
||||
disabled: false,
|
||||
indent_level: 0,
|
||||
label,
|
||||
variant: ListItemVariant::default(),
|
||||
left_content: None,
|
||||
size: ListEntrySize::default(),
|
||||
state: InteractionState::default(),
|
||||
// TODO: Should use Toggleable::NotToggleable
|
||||
// or remove Toggleable::NotToggleable from the system
|
||||
toggle: None,
|
||||
left_slot: None,
|
||||
overflow: OverflowStyle::Hidden,
|
||||
size: ListEntrySize::default(),
|
||||
toggle: Toggle::NotToggleable,
|
||||
variant: ListItemVariant::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,28 +258,23 @@ impl ListEntry {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn toggle(mut self, toggle: ToggleState) -> Self {
|
||||
self.toggle = Some(toggle);
|
||||
pub fn toggle(mut self, toggle: Toggle) -> Self {
|
||||
self.toggle = toggle;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_content(mut self, left_content: LeftContent) -> Self {
|
||||
self.left_content = Some(left_content);
|
||||
pub fn left_content(mut self, left_content: GraphicSlot) -> Self {
|
||||
self.left_slot = Some(left_content);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_icon(mut self, left_icon: Icon) -> Self {
|
||||
self.left_content = Some(LeftContent::Icon(left_icon));
|
||||
self.left_slot = Some(GraphicSlot::Icon(left_icon));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_avatar(mut self, left_avatar: impl Into<SharedString>) -> Self {
|
||||
self.left_content = Some(LeftContent::Avatar(left_avatar.into()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn state(mut self, state: InteractionState) -> Self {
|
||||
self.state = state;
|
||||
self.left_slot = Some(GraphicSlot::Avatar(left_avatar.into()));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -331,63 +283,19 @@ impl ListEntry {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disclosure_control_style(
|
||||
mut self,
|
||||
disclosure_control_style: DisclosureControlVisibility,
|
||||
) -> Self {
|
||||
self.disclosure_control_style = disclosure_control_style;
|
||||
self
|
||||
}
|
||||
|
||||
fn label_color(&self) -> LabelColor {
|
||||
match self.state {
|
||||
InteractionState::Disabled => LabelColor::Disabled,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn icon_color(&self) -> IconColor {
|
||||
match self.state {
|
||||
InteractionState::Disabled => IconColor::Disabled,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn disclosure_control<V: 'static>(
|
||||
&mut self,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Option<impl Component<V>> {
|
||||
let disclosure_control_icon = if let Some(ToggleState::Toggled) = self.toggle {
|
||||
IconElement::new(Icon::ChevronDown)
|
||||
} else {
|
||||
IconElement::new(Icon::ChevronRight)
|
||||
}
|
||||
.color(IconColor::Muted)
|
||||
.size(IconSize::Small);
|
||||
|
||||
match (self.toggle, self.disclosure_control_style) {
|
||||
(Some(_), DisclosureControlVisibility::OnHover) => {
|
||||
Some(div().absolute().neg_left_5().child(disclosure_control_icon))
|
||||
}
|
||||
(Some(_), DisclosureControlVisibility::Always) => {
|
||||
Some(div().child(disclosure_control_icon))
|
||||
}
|
||||
(None, _) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(mut self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let settings = user_settings(cx);
|
||||
|
||||
let left_content = match self.left_content.clone() {
|
||||
Some(LeftContent::Icon(i)) => Some(
|
||||
let left_content = match self.left_slot.clone() {
|
||||
Some(GraphicSlot::Icon(i)) => Some(
|
||||
h_stack().child(
|
||||
IconElement::new(i)
|
||||
.size(IconSize::Small)
|
||||
.color(IconColor::Muted),
|
||||
),
|
||||
),
|
||||
Some(LeftContent::Avatar(src)) => Some(h_stack().child(Avatar::new(src))),
|
||||
Some(GraphicSlot::Avatar(src)) => Some(h_stack().child(Avatar::new(src))),
|
||||
Some(GraphicSlot::PublicActor(src)) => Some(h_stack().child(Avatar::new(src))),
|
||||
None => None,
|
||||
};
|
||||
|
||||
@@ -400,10 +308,11 @@ impl ListEntry {
|
||||
.relative()
|
||||
.group("")
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.when(self.state == InteractionState::Focused, |this| {
|
||||
this.border()
|
||||
.border_color(cx.theme().colors().border_focused)
|
||||
})
|
||||
// TODO: Add focus state
|
||||
// .when(self.state == InteractionState::Focused, |this| {
|
||||
// this.border()
|
||||
// .border_color(cx.theme().colors().border_focused)
|
||||
// })
|
||||
.child(
|
||||
sized_item
|
||||
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
||||
@@ -425,131 +334,13 @@ impl ListEntry {
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.relative()
|
||||
.children(self.disclosure_control(cx))
|
||||
.child(disclosure_control(self.toggle))
|
||||
.children(left_content)
|
||||
.child(self.label),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct ListDetailsEntryHandlers<V: 'static> {
|
||||
click: Option<ClickHandler<V>>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Default for ListDetailsEntryHandlers<V> {
|
||||
fn default() -> Self {
|
||||
Self { click: None }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct ListDetailsEntry<V: 'static> {
|
||||
label: SharedString,
|
||||
meta: Option<SharedString>,
|
||||
left_content: Option<LeftContent>,
|
||||
handlers: ListDetailsEntryHandlers<V>,
|
||||
actions: Option<Vec<Button<V>>>,
|
||||
// TODO: make this more generic instead of
|
||||
// specifically for notifications
|
||||
seen: bool,
|
||||
}
|
||||
|
||||
impl<V: 'static> ListDetailsEntry<V> {
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
label: label.into(),
|
||||
meta: None,
|
||||
left_content: None,
|
||||
handlers: ListDetailsEntryHandlers::default(),
|
||||
actions: None,
|
||||
seen: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta(mut self, meta: impl Into<SharedString>) -> Self {
|
||||
self.meta = Some(meta.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn seen(mut self, seen: bool) -> Self {
|
||||
self.seen = seen;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_click(mut self, handler: ClickHandler<V>) -> Self {
|
||||
self.handlers.click = Some(handler);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn actions(mut self, actions: Vec<Button<V>>) -> Self {
|
||||
self.actions = Some(actions);
|
||||
self
|
||||
}
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let settings = user_settings(cx);
|
||||
|
||||
let (item_bg, item_bg_hover, item_bg_active) = (
|
||||
cx.theme().colors().ghost_element_background,
|
||||
cx.theme().colors().ghost_element_hover,
|
||||
cx.theme().colors().ghost_element_active,
|
||||
);
|
||||
|
||||
let label_color = match self.seen {
|
||||
true => LabelColor::Muted,
|
||||
false => LabelColor::Default,
|
||||
};
|
||||
|
||||
div()
|
||||
.relative()
|
||||
.group("")
|
||||
.bg(item_bg)
|
||||
.px_2()
|
||||
.py_1p5()
|
||||
.w_full()
|
||||
.z_index(1)
|
||||
.when(!self.seen, |this| {
|
||||
this.child(
|
||||
div()
|
||||
.absolute()
|
||||
.left(px(3.0))
|
||||
.top_3()
|
||||
.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),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
v_stack()
|
||||
.w_full()
|
||||
.line_height(relative(1.2))
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.w_5()
|
||||
.h_5()
|
||||
.rounded_full()
|
||||
.bg(cx.theme().colors().icon_accent),
|
||||
)
|
||||
.child(Label::new(self.label.clone()).color(label_color))
|
||||
.children(
|
||||
self.meta
|
||||
.map(|meta| Label::new(meta).color(LabelColor::Muted)),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_1()
|
||||
.justify_end()
|
||||
.children(self.actions.unwrap_or_default()),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Component)]
|
||||
pub struct ListSeparator;
|
||||
|
||||
@@ -564,20 +355,22 @@ impl ListSeparator {
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct List<V: 'static> {
|
||||
items: Vec<ListItem<V>>,
|
||||
pub struct List {
|
||||
items: Vec<ListItem>,
|
||||
/// Message to display when the list is empty
|
||||
/// Defaults to "No items"
|
||||
empty_message: SharedString,
|
||||
header: Option<ListHeader>,
|
||||
toggleable: Toggleable,
|
||||
toggle: Toggle,
|
||||
}
|
||||
|
||||
impl<V: 'static> List<V> {
|
||||
pub fn new(items: Vec<ListItem<V>>) -> Self {
|
||||
impl List {
|
||||
pub fn new(items: Vec<ListItem>) -> Self {
|
||||
Self {
|
||||
items,
|
||||
empty_message: "No items".into(),
|
||||
header: None,
|
||||
toggleable: Toggleable::default(),
|
||||
toggle: Toggle::NotToggleable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,19 +384,16 @@ impl<V: 'static> List<V> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn toggle(mut self, toggle: ToggleState) -> Self {
|
||||
self.toggleable = toggle.into();
|
||||
pub fn toggle(mut self, toggle: Toggle) -> Self {
|
||||
self.toggle = toggle;
|
||||
self
|
||||
}
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
||||
let is_toggled = Toggleable::is_toggled(&self.toggleable);
|
||||
|
||||
let list_content = match (self.items.is_empty(), is_toggled) {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let list_content = match (self.items.is_empty(), self.toggle) {
|
||||
(false, _) => div().children(self.items),
|
||||
(true, false) => div(),
|
||||
(true, true) => {
|
||||
(true, Toggle::Toggled(false)) => div(),
|
||||
(true, _) => {
|
||||
div().child(Label::new(self.empty_message.clone()).color(LabelColor::Muted))
|
||||
}
|
||||
};
|
||||
@@ -611,7 +401,7 @@ impl<V: 'static> List<V> {
|
||||
v_stack()
|
||||
.w_full()
|
||||
.py_1()
|
||||
.children(self.header.map(|header| header.toggleable(self.toggleable)))
|
||||
.children(self.header.map(|header| header))
|
||||
.child(list_content)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,24 @@ use gpui2::{Hsla, ViewContext};
|
||||
|
||||
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]
|
||||
@@ -0,0 +1,14 @@
|
||||
use gpui2::SharedString;
|
||||
|
||||
use crate::Icon;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A slot utility that provides a way to to pass either
|
||||
/// an icon or an image to a component.
|
||||
///
|
||||
/// Can be filled with a []
|
||||
pub enum GraphicSlot {
|
||||
Icon(Icon),
|
||||
Avatar(SharedString),
|
||||
PublicActor(SharedString),
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use gpui2::{div, Component, ParentElement};
|
||||
|
||||
use crate::{Icon, IconColor, IconElement, IconSize};
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// You can check if an element is toggleable with `.is_toggleable()`
|
||||
///
|
||||
/// Possible values:
|
||||
/// - `Toggle::NotToggleable` - The entry is not toggleable
|
||||
/// - `Toggle::Toggled(true)` - The entry is toggleable and toggled
|
||||
/// - `Toggle::Toggled(false)` - The entry is toggleable and not toggled
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Toggle {
|
||||
NotToggleable,
|
||||
Toggled(bool),
|
||||
}
|
||||
|
||||
impl Toggle {
|
||||
/// Returns true if the entry is toggled (or is not toggleable.)
|
||||
///
|
||||
/// As element that isn't toggleable is always "expanded" or "enabled"
|
||||
/// returning true in that case makes sense.
|
||||
pub fn is_toggled(&self) -> bool {
|
||||
match self {
|
||||
Self::Toggled(false) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_toggleable(&self) -> bool {
|
||||
match self {
|
||||
Self::Toggled(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Toggle {
|
||||
fn from(toggled: bool) -> Self {
|
||||
Toggle::Toggled(toggled)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disclosure_control<V: 'static>(toggle: Toggle) -> impl Component<V> {
|
||||
match (toggle.is_toggleable(), toggle.is_toggled()) {
|
||||
(false, _) => div(),
|
||||
(_, true) => div().child(
|
||||
IconElement::new(Icon::ChevronDown)
|
||||
.color(IconColor::Muted)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
(_, false) => div().child(
|
||||
IconElement::new(Icon::ChevronRight)
|
||||
.color(IconColor::Muted)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
mod avatar;
|
||||
mod button;
|
||||
mod details;
|
||||
mod icon;
|
||||
mod indicator;
|
||||
mod input;
|
||||
mod label;
|
||||
mod player;
|
||||
mod stack;
|
||||
mod tool_divider;
|
||||
|
||||
pub use avatar::*;
|
||||
pub use button::*;
|
||||
pub use details::*;
|
||||
pub use icon::*;
|
||||
pub use indicator::*;
|
||||
pub use input::*;
|
||||
pub use label::*;
|
||||
pub use player::*;
|
||||
pub use stack::*;
|
||||
pub use tool_divider::*;
|
||||
@@ -7,28 +7,25 @@
|
||||
//! This crate is still a work in progress. The initial primitives and components are built for getting all the UI on the screen,
|
||||
//! much of the state and functionality is mocked or hard codeded, and performance has not been a focus.
|
||||
//!
|
||||
//! Expect some inconsistencies from component to component as we work out the best way to build these components.
|
||||
//!
|
||||
//! ## Design Philosophy
|
||||
//!
|
||||
//! Work in Progress!
|
||||
//!
|
||||
|
||||
#![doc = include_str!("../docs/hello-world.md")]
|
||||
#![doc = include_str!("../docs/building-ui.md")]
|
||||
#![doc = include_str!("../docs/todo.md")]
|
||||
// TODO: Fix warnings instead of supressing.
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
mod components;
|
||||
mod elements;
|
||||
mod elevation;
|
||||
pub mod prelude;
|
||||
pub mod settings;
|
||||
mod static_data;
|
||||
mod to_extract;
|
||||
pub mod utils;
|
||||
|
||||
pub use components::*;
|
||||
pub use elements::*;
|
||||
pub use prelude::*;
|
||||
pub use static_data::*;
|
||||
pub use to_extract::*;
|
||||
|
||||
// This needs to be fully qualified with `crate::` otherwise we get a panic
|
||||
// at:
|
||||
|
||||
@@ -10,24 +10,6 @@ pub use theme2::ActiveTheme;
|
||||
use gpui2::Hsla;
|
||||
use strum::EnumIter;
|
||||
|
||||
/// Represents a person with a Zed account's public profile.
|
||||
/// All data in this struct should be considered public.
|
||||
pub struct PublicActor {
|
||||
pub username: SharedString,
|
||||
pub avatar: SharedString,
|
||||
pub is_contact: bool,
|
||||
}
|
||||
|
||||
impl PublicActor {
|
||||
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, EnumIter)]
|
||||
pub enum FileSystemStatus {
|
||||
#[default]
|
||||
@@ -172,68 +154,10 @@ impl InteractionState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub enum SelectedState {
|
||||
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub enum Selected {
|
||||
#[default]
|
||||
Unselected,
|
||||
PartiallySelected,
|
||||
Indeterminate,
|
||||
Selected,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Toggleable {
|
||||
Toggleable(ToggleState),
|
||||
#[default]
|
||||
NotToggleable,
|
||||
}
|
||||
|
||||
impl Toggleable {
|
||||
pub fn is_toggled(&self) -> bool {
|
||||
match self {
|
||||
Self::Toggleable(ToggleState::Toggled) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ToggleState> for Toggleable {
|
||||
fn from(state: ToggleState) -> Self {
|
||||
Self::Toggleable(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ToggleState {
|
||||
/// The "on" state of a toggleable element.
|
||||
///
|
||||
/// Example:
|
||||
/// - A collasable list that is currently expanded
|
||||
/// - A toggle button that is currently on.
|
||||
Toggled,
|
||||
/// The "off" state of a toggleable element.
|
||||
///
|
||||
/// Example:
|
||||
/// - A collasable list that is currently collapsed
|
||||
/// - A toggle button that is currently off.
|
||||
#[default]
|
||||
NotToggled,
|
||||
}
|
||||
|
||||
impl From<Toggleable> for ToggleState {
|
||||
fn from(toggleable: Toggleable) -> Self {
|
||||
match toggleable {
|
||||
Toggleable::Toggleable(state) => state,
|
||||
Toggleable::NotToggleable => ToggleState::NotToggled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for ToggleState {
|
||||
fn from(toggled: bool) -> Self {
|
||||
if toggled {
|
||||
ToggleState::Toggled
|
||||
} else {
|
||||
ToggleState::NotToggled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-101
@@ -7,13 +7,13 @@ use gpui2::{AppContext, ViewContext};
|
||||
use rand::Rng;
|
||||
use theme2::ActiveTheme;
|
||||
|
||||
use crate::HighlightedText;
|
||||
use crate::{
|
||||
Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus,
|
||||
HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListSubHeader,
|
||||
Livestream, MicStatus, ModifierKeys, Notification, PaletteItem, Player, PlayerCallStatus,
|
||||
PlayerWithCallStatus, PublicActor, ScreenShareStatus, Symbol, Tab, ToggleState, VideoStatus,
|
||||
HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, Livestream,
|
||||
MicStatus, ModifierKeys, Notification, PaletteItem, Player, PlayerCallStatus,
|
||||
PlayerWithCallStatus, PublicPlayer, ScreenShareStatus, Symbol, Tab, Toggle, VideoStatus,
|
||||
};
|
||||
use crate::{HighlightedText, ListDetailsEntry};
|
||||
use crate::{ListItem, NotificationAction};
|
||||
|
||||
pub fn static_tabs_example() -> Vec<Tab> {
|
||||
@@ -345,7 +345,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
||||
DateTime::parse_from_rfc3339("2023-11-02T12:09:07Z")
|
||||
.unwrap()
|
||||
.naive_local(),
|
||||
PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
[
|
||||
NotificationAction::new(
|
||||
Button::new("Decline"),
|
||||
@@ -374,7 +374,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
||||
DateTime::parse_from_rfc3339("2023-11-01T12:09:07Z")
|
||||
.unwrap()
|
||||
.naive_local(),
|
||||
PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
[
|
||||
NotificationAction::new(
|
||||
Button::new("Decline"),
|
||||
@@ -403,7 +403,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
||||
DateTime::parse_from_rfc3339("2022-10-25T12:09:07Z")
|
||||
.unwrap()
|
||||
.naive_local(),
|
||||
PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
[
|
||||
NotificationAction::new(
|
||||
Button::new("Decline"),
|
||||
@@ -432,7 +432,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
||||
DateTime::parse_from_rfc3339("2021-10-12T12:09:07Z")
|
||||
.unwrap()
|
||||
.naive_local(),
|
||||
PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
[
|
||||
NotificationAction::new(
|
||||
Button::new("Decline"),
|
||||
@@ -461,7 +461,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
||||
DateTime::parse_from_rfc3339("1969-07-20T00:00:00Z")
|
||||
.unwrap()
|
||||
.naive_local(),
|
||||
PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||
[
|
||||
NotificationAction::new(
|
||||
Button::new("Decline"),
|
||||
@@ -478,89 +478,12 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
||||
]
|
||||
}
|
||||
|
||||
pub fn static_new_notification_items<V: 'static>() -> Vec<ListItem<V>> {
|
||||
vec![
|
||||
ListItem::Header(ListSubHeader::new("New")),
|
||||
ListItem::Details(
|
||||
ListDetailsEntry::new("maxdeviant invited you to join a stream in #design.")
|
||||
.meta("4 people in stream."),
|
||||
),
|
||||
ListItem::Details(ListDetailsEntry::new(
|
||||
"nathansobo accepted your contact request.",
|
||||
)),
|
||||
ListItem::Header(ListSubHeader::new("Earlier")),
|
||||
ListItem::Details(
|
||||
ListDetailsEntry::new("mikaylamaki added you as a contact.").actions(vec![
|
||||
Button::new("Decline"),
|
||||
Button::new("Accept").variant(crate::ButtonVariant::Filled),
|
||||
]),
|
||||
),
|
||||
ListItem::Details(
|
||||
ListDetailsEntry::new("maxdeviant invited you to a stream in #design.")
|
||||
.seen(true)
|
||||
.meta("This stream has ended."),
|
||||
),
|
||||
ListItem::Details(ListDetailsEntry::new(
|
||||
"as-cii accepted your contact request.",
|
||||
)),
|
||||
ListItem::Details(
|
||||
ListDetailsEntry::new("You were added as an admin on the #gpui2 channel.").seen(true),
|
||||
),
|
||||
ListItem::Details(ListDetailsEntry::new(
|
||||
"osiewicz accepted your contact request.",
|
||||
)),
|
||||
ListItem::Details(ListDetailsEntry::new(
|
||||
"ConradIrwin accepted your contact request.",
|
||||
)),
|
||||
ListItem::Details(
|
||||
ListDetailsEntry::new("nathansobo invited you to a stream in #gpui2.")
|
||||
.seen(true)
|
||||
.meta("This stream has ended."),
|
||||
),
|
||||
ListItem::Details(ListDetailsEntry::new(
|
||||
"nathansobo accepted your contact request.",
|
||||
)),
|
||||
ListItem::Header(ListSubHeader::new("Earlier")),
|
||||
ListItem::Details(
|
||||
ListDetailsEntry::new("mikaylamaki added you as a contact.").actions(vec![
|
||||
Button::new("Decline"),
|
||||
Button::new("Accept").variant(crate::ButtonVariant::Filled),
|
||||
]),
|
||||
),
|
||||
ListItem::Details(
|
||||
ListDetailsEntry::new("maxdeviant invited you to a stream in #design.")
|
||||
.seen(true)
|
||||
.meta("This stream has ended."),
|
||||
),
|
||||
ListItem::Details(ListDetailsEntry::new(
|
||||
"as-cii accepted your contact request.",
|
||||
)),
|
||||
ListItem::Details(
|
||||
ListDetailsEntry::new("You were added as an admin on the #gpui2 channel.").seen(true),
|
||||
),
|
||||
ListItem::Details(ListDetailsEntry::new(
|
||||
"osiewicz accepted your contact request.",
|
||||
)),
|
||||
ListItem::Details(ListDetailsEntry::new(
|
||||
"ConradIrwin accepted your contact request.",
|
||||
)),
|
||||
ListItem::Details(
|
||||
ListDetailsEntry::new("nathansobo invited you to a stream in #gpui2.")
|
||||
.seen(true)
|
||||
.meta("This stream has ended."),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn static_project_panel_project_items<V: 'static>() -> Vec<ListItem<V>> {
|
||||
pub fn static_project_panel_project_items() -> Vec<ListItem> {
|
||||
vec![
|
||||
ListEntry::new(Label::new("zed"))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(0)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new(".cargo"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1),
|
||||
@@ -579,14 +502,14 @@ pub fn static_project_panel_project_items<V: 'static>() -> Vec<ListItem<V>> {
|
||||
ListEntry::new(Label::new("assets"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(1),
|
||||
ListEntry::new(Label::new("crates"))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(1)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new("activity_indicator"))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2),
|
||||
@@ -608,38 +531,38 @@ pub fn static_project_panel_project_items<V: 'static>() -> Vec<ListItem<V>> {
|
||||
ListEntry::new(Label::new("sqlez").color(LabelColor::Modified))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2)
|
||||
.toggle(ToggleState::NotToggled),
|
||||
.toggle(Toggle::Toggled(false)),
|
||||
ListEntry::new(Label::new("gpui2"))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(2)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new("src"))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(3)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new("derive_element.rs"))
|
||||
.left_icon(Icon::FileRust.into())
|
||||
.indent_level(4),
|
||||
ListEntry::new(Label::new("storybook").color(LabelColor::Modified))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(1)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new("docs").color(LabelColor::Default))
|
||||
.left_icon(Icon::Folder.into())
|
||||
.indent_level(2)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new("src").color(LabelColor::Modified))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(3)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new("ui").color(LabelColor::Modified))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(4)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new("component").color(LabelColor::Created))
|
||||
.left_icon(Icon::FolderOpen.into())
|
||||
.indent_level(5)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default))
|
||||
.left_icon(Icon::FileRust.into())
|
||||
.indent_level(6),
|
||||
@@ -682,7 +605,7 @@ pub fn static_project_panel_project_items<V: 'static>() -> Vec<ListItem<V>> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn static_project_panel_single_items<V: 'static>() -> Vec<ListItem<V>> {
|
||||
pub fn static_project_panel_single_items() -> Vec<ListItem> {
|
||||
vec![
|
||||
ListEntry::new(Label::new("todo.md"))
|
||||
.left_icon(Icon::FileDoc.into())
|
||||
@@ -699,7 +622,7 @@ pub fn static_project_panel_single_items<V: 'static>() -> Vec<ListItem<V>> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn static_collab_panel_current_call<V: 'static>() -> Vec<ListItem<V>> {
|
||||
pub fn static_collab_panel_current_call() -> Vec<ListItem> {
|
||||
vec![
|
||||
ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"),
|
||||
ListEntry::new(Label::new("nathansobo"))
|
||||
@@ -712,7 +635,7 @@ pub fn static_collab_panel_current_call<V: 'static>() -> Vec<ListItem<V>> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn static_collab_panel_channels<V: 'static>() -> Vec<ListItem<V>> {
|
||||
pub fn static_collab_panel_channels() -> Vec<ListItem> {
|
||||
vec![
|
||||
ListEntry::new(Label::new("zed"))
|
||||
.left_icon(Icon::Hash.into())
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
mod assistant_panel;
|
||||
mod breadcrumb;
|
||||
mod buffer;
|
||||
mod buffer_search;
|
||||
mod chat_panel;
|
||||
mod collab_panel;
|
||||
mod command_palette;
|
||||
mod copilot;
|
||||
mod editor_pane;
|
||||
mod language_selector;
|
||||
mod multi_buffer;
|
||||
mod notifications_panel;
|
||||
mod panes;
|
||||
mod project_panel;
|
||||
mod recent_projects;
|
||||
mod status_bar;
|
||||
mod tab_bar;
|
||||
mod terminal;
|
||||
mod theme_selector;
|
||||
mod title_bar;
|
||||
mod toolbar;
|
||||
mod traffic_lights;
|
||||
mod workspace;
|
||||
|
||||
pub use assistant_panel::*;
|
||||
pub use breadcrumb::*;
|
||||
pub use buffer::*;
|
||||
pub use buffer_search::*;
|
||||
pub use chat_panel::*;
|
||||
pub use collab_panel::*;
|
||||
pub use command_palette::*;
|
||||
pub use copilot::*;
|
||||
pub use editor_pane::*;
|
||||
pub use language_selector::*;
|
||||
pub use multi_buffer::*;
|
||||
pub use notifications_panel::*;
|
||||
pub use panes::*;
|
||||
pub use project_panel::*;
|
||||
pub use recent_projects::*;
|
||||
pub use status_bar::*;
|
||||
pub use tab_bar::*;
|
||||
pub use terminal::*;
|
||||
pub use theme_selector::*;
|
||||
pub use title_bar::*;
|
||||
pub use toolbar::*;
|
||||
pub use traffic_lights::*;
|
||||
pub use workspace::*;
|
||||
+10
-11
@@ -1,7 +1,6 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{prelude::*, Toggle};
|
||||
use crate::{
|
||||
static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List,
|
||||
ListHeader, ToggleState,
|
||||
static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, ListHeader,
|
||||
};
|
||||
|
||||
#[derive(Component)]
|
||||
@@ -34,17 +33,17 @@ impl CollabPanel {
|
||||
.header(
|
||||
ListHeader::new("CRDB")
|
||||
.left_icon(Icon::Hash.into())
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_stack().id("channels").py_1().child(
|
||||
List::new(static_collab_panel_channels())
|
||||
.header(ListHeader::new("CHANNELS").toggle(ToggleState::Toggled))
|
||||
.header(ListHeader::new("CHANNELS").toggle(Toggle::Toggled(true)))
|
||||
.empty_message("No channels yet. Add a channel to get started.")
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
@@ -52,9 +51,9 @@ impl CollabPanel {
|
||||
List::new(static_collab_panel_current_call())
|
||||
.header(
|
||||
ListHeader::new("CONTACTS – ONLINE")
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
)
|
||||
.toggle(ToggleState::Toggled),
|
||||
.toggle(Toggle::Toggled(true)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
@@ -62,9 +61,9 @@ impl CollabPanel {
|
||||
List::new(static_collab_panel_current_call())
|
||||
.header(
|
||||
ListHeader::new("CONTACTS – OFFLINE")
|
||||
.toggle(ToggleState::NotToggled),
|
||||
.toggle(Toggle::Toggled(false)),
|
||||
)
|
||||
.toggle(ToggleState::NotToggled),
|
||||
.toggle(Toggle::Toggled(false)),
|
||||
),
|
||||
),
|
||||
)
|
||||
+6
-23
@@ -1,8 +1,8 @@
|
||||
use crate::utils::naive_format_distance_from_now;
|
||||
use crate::{
|
||||
h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, Button, Icon,
|
||||
IconButton, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator,
|
||||
UnreadIndicator,
|
||||
h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, ButtonOrIconButton,
|
||||
Icon, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator,
|
||||
PublicPlayer, UnreadIndicator,
|
||||
};
|
||||
use crate::{ClickHandler, ListHeader};
|
||||
|
||||
@@ -57,23 +57,6 @@ impl NotificationsPanel {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ButtonOrIconButton<V: 'static> {
|
||||
Button(Button<V>),
|
||||
IconButton(IconButton<V>),
|
||||
}
|
||||
|
||||
impl<V: 'static> From<Button<V>> for ButtonOrIconButton<V> {
|
||||
fn from(value: Button<V>) -> Self {
|
||||
Self::Button(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> From<IconButton<V>> for ButtonOrIconButton<V> {
|
||||
fn from(value: IconButton<V>) -> Self {
|
||||
Self::IconButton(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NotificationAction<V: 'static> {
|
||||
button: ButtonOrIconButton<V>,
|
||||
tooltip: SharedString,
|
||||
@@ -102,7 +85,7 @@ impl<V: 'static> NotificationAction<V> {
|
||||
}
|
||||
|
||||
pub enum ActorOrIcon {
|
||||
Actor(PublicActor),
|
||||
Actor(PublicPlayer),
|
||||
Icon(Icon),
|
||||
}
|
||||
|
||||
@@ -171,7 +154,7 @@ impl<V> Notification<V> {
|
||||
id: impl Into<ElementId>,
|
||||
message: impl Into<SharedString>,
|
||||
date_received: NaiveDateTime,
|
||||
actor: PublicActor,
|
||||
actor: PublicPlayer,
|
||||
click_action: ClickHandler<V>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
@@ -210,7 +193,7 @@ impl<V> Notification<V> {
|
||||
id: impl Into<ElementId>,
|
||||
message: impl Into<SharedString>,
|
||||
date_received: NaiveDateTime,
|
||||
actor: PublicActor,
|
||||
actor: PublicPlayer,
|
||||
actions: [NotificationAction<V>; 2],
|
||||
) -> Self {
|
||||
Self::new(
|
||||
+5
-8
@@ -18,8 +18,7 @@ impl ProjectPanel {
|
||||
.id(self.id.clone())
|
||||
.flex()
|
||||
.flex_col()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.size_full()
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.child(
|
||||
div()
|
||||
@@ -30,15 +29,13 @@ impl ProjectPanel {
|
||||
.overflow_y_scroll()
|
||||
.child(
|
||||
List::new(static_project_panel_single_items())
|
||||
.header(ListHeader::new("FILES").toggle(ToggleState::Toggled))
|
||||
.empty_message("No files in directory")
|
||||
.toggle(ToggleState::Toggled),
|
||||
.header(ListHeader::new("FILES"))
|
||||
.empty_message("No files in directory"),
|
||||
)
|
||||
.child(
|
||||
List::new(static_project_panel_project_items())
|
||||
.header(ListHeader::new("PROJECT").toggle(ToggleState::Toggled))
|
||||
.empty_message("No folders in directory")
|
||||
.toggle(ToggleState::Toggled),
|
||||
.header(ListHeader::new("PROJECT"))
|
||||
.empty_message("No folders in directory"),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Reference in New Issue
Block a user