Merge branch 'main' into collab-panel2
This commit is contained in:
commit
87cf0cf5ac
12 changed files with 135 additions and 85 deletions
|
@ -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::{
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
|
@ -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))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
20
crates/ui2/src/components/stories/disclosure.rs
Normal file
20
crates/ui2/src/components/stories/disclosure.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
38
crates/ui2/src/toggleable.rs
Normal file
38
crates/ui2/src/toggleable.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::*;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue