Merge branch 'main' into collab-panel2

This commit is contained in:
Conrad Irwin 2023-11-29 13:09:57 -07:00
commit 87cf0cf5ac
12 changed files with 135 additions and 85 deletions

View file

@ -179,8 +179,8 @@ use project::Fs;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
use ui::{ use ui::{
h_stack, v_stack, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize, h_stack, v_stack, Avatar, Button, Color, ContextMenu, Icon, IconButton, IconElement, IconSize, Label, List,
Label, List, ListHeader, ListItem, Toggle, Tooltip, ListHeader, ListItem, Toggle, Tooltip,
}; };
use util::{maybe, ResultExt, TryFutureExt}; use util::{maybe, ResultExt, TryFutureExt};
use workspace::{ use workspace::{

View file

@ -16,6 +16,7 @@ pub enum ComponentStory {
Button, Button,
Checkbox, Checkbox,
ContextMenu, ContextMenu,
Disclosure,
Focus, Focus,
Icon, Icon,
IconButton, IconButton,
@ -36,6 +37,7 @@ impl ComponentStory {
Self::Button => cx.build_view(|_| ui::ButtonStory).into(), Self::Button => cx.build_view(|_| ui::ButtonStory).into(),
Self::Checkbox => cx.build_view(|_| ui::CheckboxStory).into(), Self::Checkbox => cx.build_view(|_| ui::CheckboxStory).into(),
Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into(), Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into(),
Self::Disclosure => cx.build_view(|_| ui::DisclosureStory).into(),
Self::Focus => FocusStory::view(cx).into(), Self::Focus => FocusStory::view(cx).into(),
Self::Icon => cx.build_view(|_| ui::IconStory).into(), Self::Icon => cx.build_view(|_| ui::IconStory).into(),
Self::IconButton => cx.build_view(|_| ui::IconButtonStory).into(), Self::IconButton => cx.build_view(|_| ui::IconButtonStory).into(),

View file

@ -13,7 +13,6 @@ mod list;
mod popover; mod popover;
mod slot; mod slot;
mod stack; mod stack;
mod toggle;
mod tooltip; mod tooltip;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
@ -34,7 +33,6 @@ pub use list::*;
pub use popover::*; pub use popover::*;
pub use slot::*; pub use slot::*;
pub use stack::*; pub use stack::*;
pub use toggle::*;
pub use tooltip::*; pub use tooltip::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]

View file

@ -1,30 +1,55 @@
use std::rc::Rc; use std::rc::Rc;
use gpui::{div, ClickEvent, Element, IntoElement, ParentElement, WindowContext}; use gpui::ClickEvent;
use crate::{Color, Icon, IconButton, IconSize, Toggle}; use crate::prelude::*;
use crate::{Color, Icon, IconButton, IconSize, ToggleState, Toggleable};
pub fn disclosure_control( #[derive(IntoElement)]
toggle: Toggle, pub struct Disclosure {
state: ToggleState,
on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>, on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
) -> impl Element { }
match (toggle.is_toggleable(), toggle.is_toggled()) {
(false, _) => div(), impl Disclosure {
(_, true) => div().child( pub fn new(state: ToggleState) -> Self {
IconButton::new("toggle", Icon::ChevronDown) Self {
.color(Color::Muted) state,
.size(IconSize::Small) on_toggle: None,
.when_some(on_toggle, move |el, on_toggle| { }
el.on_click(move |e, cx| on_toggle(e, cx)) }
}),
), pub fn from_toggleable(toggleable: Toggleable) -> Option<Self> {
(_, false) => div().child( match toggleable {
IconButton::new("toggle", Icon::ChevronRight) Toggleable::Toggleable(state) => Some(Self::new(state)),
.color(Color::Muted) Toggleable::NotToggleable => None,
.size(IconSize::Small) }
.when_some(on_toggle, move |el, on_toggle| { }
el.on_click(move |e, cx| on_toggle(e, cx))
}), pub fn on_toggle(
), mut self,
handler: impl Into<Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>>,
) -> Self {
self.on_toggle = handler.into();
self
}
}
impl RenderOnce for Disclosure {
type Rendered = IconButton;
fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
IconButton::new(
"toggle",
match self.state {
ToggleState::Toggled => Icon::ChevronDown,
ToggleState::NotToggled => Icon::ChevronRight,
},
)
.color(Color::Muted)
.size(IconSize::Small)
.when_some(self.on_toggle, move |this, on_toggle| {
this.on_click(move |event, cx| on_toggle(event, cx))
})
} }
} }

View file

@ -7,7 +7,7 @@ use gpui::{AnyElement, Div};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::prelude::*; use crate::prelude::*;
use crate::{v_stack, Label, Toggle}; use crate::{v_stack, Label, ToggleState, Toggleable};
pub use list_header::*; pub use list_header::*;
pub use list_item::*; pub use list_item::*;
@ -20,7 +20,7 @@ pub struct List {
/// Defaults to "No items" /// Defaults to "No items"
empty_message: SharedString, empty_message: SharedString,
header: Option<ListHeader>, header: Option<ListHeader>,
toggle: Toggle, toggle: Toggleable,
children: SmallVec<[AnyElement; 2]>, children: SmallVec<[AnyElement; 2]>,
} }
@ -29,7 +29,7 @@ impl List {
Self { Self {
empty_message: "No items".into(), empty_message: "No items".into(),
header: None, header: None,
toggle: Toggle::NotToggleable, toggle: Toggleable::NotToggleable,
children: SmallVec::new(), children: SmallVec::new(),
} }
} }
@ -44,7 +44,7 @@ impl List {
self self
} }
pub fn toggle(mut self, toggle: Toggle) -> Self { pub fn toggle(mut self, toggle: Toggleable) -> Self {
self.toggle = toggle; self.toggle = toggle;
self self
} }
@ -66,7 +66,7 @@ impl RenderOnce for List {
.children(self.header.map(|header| header)) .children(self.header.map(|header| header))
.map(|this| match (self.children.is_empty(), self.toggle) { .map(|this| match (self.children.is_empty(), self.toggle) {
(false, _) => this.children(self.children), (false, _) => this.children(self.children),
(true, Toggle::Toggled(false)) => this, (true, Toggleable::Toggleable(ToggleState::NotToggled)) => this,
(true, _) => this.child(Label::new(self.empty_message.clone()).color(Color::Muted)), (true, _) => this.child(Label::new(self.empty_message.clone()).color(Color::Muted)),
}) })
} }

View file

@ -3,7 +3,7 @@ use std::rc::Rc;
use gpui::{ClickEvent, Div}; use gpui::{ClickEvent, Div};
use crate::prelude::*; use crate::prelude::*;
use crate::{disclosure_control, h_stack, Icon, IconButton, IconElement, IconSize, Label, Toggle}; use crate::{h_stack, Disclosure, Icon, IconButton, IconElement, IconSize, Label, Toggleable};
pub enum ListHeaderMeta { pub enum ListHeaderMeta {
Tools(Vec<IconButton>), Tools(Vec<IconButton>),
@ -17,7 +17,7 @@ pub struct ListHeader {
label: SharedString, label: SharedString,
left_icon: Option<Icon>, left_icon: Option<Icon>,
meta: Option<ListHeaderMeta>, meta: Option<ListHeaderMeta>,
toggle: Toggle, toggle: Toggleable,
on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>, on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
inset: bool, inset: bool,
selected: bool, selected: bool,
@ -30,13 +30,13 @@ impl ListHeader {
left_icon: None, left_icon: None,
meta: None, meta: None,
inset: false, inset: false,
toggle: Toggle::NotToggleable, toggle: Toggleable::NotToggleable,
on_toggle: None, on_toggle: None,
selected: false, selected: false,
} }
} }
pub fn toggle(mut self, toggle: Toggle) -> Self { pub fn toggle(mut self, toggle: Toggleable) -> Self {
self.toggle = toggle; self.toggle = toggle;
self self
} }
@ -73,8 +73,6 @@ impl RenderOnce for ListHeader {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let disclosure_control = disclosure_control(self.toggle, self.on_toggle);
let meta = match self.meta { let meta = match self.meta {
Some(ListHeaderMeta::Tools(icons)) => div().child( Some(ListHeaderMeta::Tools(icons)) => div().child(
h_stack() h_stack()
@ -115,7 +113,10 @@ impl RenderOnce for ListHeader {
})) }))
.child(Label::new(self.label.clone()).color(Color::Muted)), .child(Label::new(self.label.clone()).color(Color::Muted)),
) )
.child(disclosure_control), .children(
Disclosure::from_toggleable(self.toggle)
.map(|disclosure| disclosure.on_toggle(self.on_toggle)),
),
) )
.child(meta), .child(meta),
) )

View file

@ -6,7 +6,7 @@ use gpui::{
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::prelude::*; use crate::prelude::*;
use crate::{disclosure_control, Avatar, GraphicSlot, Icon, IconElement, IconSize, Toggle}; use crate::{Avatar, Disclosure, GraphicSlot, Icon, IconElement, IconSize, Toggleable};
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct ListItem { pub struct ListItem {
@ -17,7 +17,7 @@ pub struct ListItem {
indent_level: usize, indent_level: usize,
indent_step_size: Pixels, indent_step_size: Pixels,
left_slot: Option<GraphicSlot>, left_slot: Option<GraphicSlot>,
toggle: Toggle, toggle: Toggleable,
inset: bool, inset: bool,
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>, on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>, on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
@ -33,7 +33,7 @@ impl ListItem {
indent_level: 0, indent_level: 0,
indent_step_size: px(12.), indent_step_size: px(12.),
left_slot: None, left_slot: None,
toggle: Toggle::NotToggleable, toggle: Toggleable::NotToggleable,
inset: false, inset: false,
on_click: None, on_click: None,
on_secondary_mouse_down: None, on_secondary_mouse_down: None,
@ -70,7 +70,7 @@ impl ListItem {
self self
} }
pub fn toggle(mut self, toggle: Toggle) -> Self { pub fn toggle(mut self, toggle: Toggleable) -> Self {
self.toggle = toggle; self.toggle = toggle;
self self
} }
@ -150,7 +150,10 @@ impl RenderOnce for ListItem {
.gap_1() .gap_1()
.items_center() .items_center()
.relative() .relative()
.child(disclosure_control(self.toggle, self.on_toggle)) .children(
Disclosure::from_toggleable(self.toggle)
.map(|disclosure| disclosure.on_toggle(self.on_toggle)),
)
.map(|this| match self.left_slot { .map(|this| match self.left_slot {
Some(GraphicSlot::Icon(i)) => this.child( Some(GraphicSlot::Icon(i)) => this.child(
IconElement::new(i) IconElement::new(i)

View file

@ -2,6 +2,7 @@ mod avatar;
mod button; mod button;
mod checkbox; mod checkbox;
mod context_menu; mod context_menu;
mod disclosure;
mod icon; mod icon;
mod icon_button; mod icon_button;
mod keybinding; mod keybinding;
@ -13,6 +14,7 @@ pub use avatar::*;
pub use button::*; pub use button::*;
pub use checkbox::*; pub use checkbox::*;
pub use context_menu::*; pub use context_menu::*;
pub use disclosure::*;
pub use icon::*; pub use icon::*;
pub use icon_button::*; pub use icon_button::*;
pub use keybinding::*; pub use keybinding::*;

View file

@ -0,0 +1,20 @@
use gpui::{Div, Render};
use story::Story;
use crate::prelude::*;
use crate::{Disclosure, ToggleState};
pub struct DisclosureStory;
impl Render for DisclosureStory {
type Element = Div;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
Story::container()
.child(Story::title_for::<Disclosure>())
.child(Story::label("Toggled"))
.child(Disclosure::new(ToggleState::Toggled))
.child(Story::label("Not Toggled"))
.child(Disclosure::new(ToggleState::NotToggled))
}
}

View file

@ -1,41 +0,0 @@
/// 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)
}
}

View file

@ -0,0 +1,38 @@
/// Whether an element is able to be toggled.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum Toggleable {
Toggleable(ToggleState),
NotToggleable,
}
/// The current state of a [`Toggleable`] element.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum ToggleState {
Toggled,
NotToggled,
}
impl ToggleState {
/// Returns whether an entry is toggled.
pub fn is_toggled(&self) -> bool {
match self {
ToggleState::Toggled => true,
ToggleState::NotToggled => false,
}
}
}
impl From<bool> for ToggleState {
fn from(toggled: bool) -> Self {
match toggled {
true => Self::Toggled,
false => Self::NotToggled,
}
}
}
impl From<ToggleState> for bool {
fn from(value: ToggleState) -> Self {
value.is_toggled()
}
}

View file

@ -20,6 +20,7 @@ pub mod prelude;
mod selectable; mod selectable;
mod styled_ext; mod styled_ext;
mod styles; mod styles;
mod toggleable;
pub mod utils; pub mod utils;
pub use clickable::*; pub use clickable::*;
@ -30,3 +31,4 @@ pub use prelude::*;
pub use selectable::*; pub use selectable::*;
pub use styled_ext::*; pub use styled_ext::*;
pub use styles::*; pub use styles::*;
pub use toggleable::*;