Simplify toggle, do some reorganization

Nate Butler created

Change summary

crates/ui2/src/components.rs                     |   4 
crates/ui2/src/components/button.rs              |  21 +
crates/ui2/src/components/context_menu.rs        |  17 
crates/ui2/src/components/list.rs                | 324 +++--------------
crates/ui2/src/components/player.rs              |  18 +
crates/ui2/src/components/slot.rs                |  14 
crates/ui2/src/components/toggle.rs              |  61 +++
crates/ui2/src/prelude.rs                        |  76 ----
crates/ui2/src/static_data.rs                    | 125 +-----
crates/ui2/src/to_extract/collab_panel.rs        |  21 
crates/ui2/src/to_extract/notifications_panel.rs |  29 -
crates/ui2/src/to_extract/project_panel.rs       |  13 
12 files changed, 224 insertions(+), 499 deletions(-)

Detailed changes

crates/ui2/src/components.rs 🔗

@@ -16,9 +16,11 @@ mod palette;
 mod panel;
 mod player;
 mod player_stack;
+mod slot;
 mod stack;
 mod tab;
 mod toast;
+mod toggle;
 mod tool_divider;
 
 pub use avatar::*;
@@ -39,7 +41,9 @@ 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 toggle::*;
 pub use tool_divider::*;

crates/ui2/src/components/button.rs 🔗

@@ -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 {

crates/ui2/src/components/context_menu.rs 🔗

@@ -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)
             .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(),
+            ))
     }
 }
 

crates/ui2/src/components/list.rs 🔗

@@ -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,7 @@ impl ListEntry {
             .relative()
             .group("")
             .bg(cx.theme().colors().surface)
-            .when(self.state == InteractionState::Focused, |this| {
-                this.border()
-                    .border_color(cx.theme().colors().border_focused)
-            })
+            // TODO: Add focus state
             .child(
                 sized_item
                     .when(self.variant == ListItemVariant::Inset, |this| this.px_2())
@@ -425,131 +330,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,
-            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)
-                        .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 +351,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 +380,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 +397,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)
     }
 }

crates/ui2/src/components/player.rs 🔗

@@ -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]

crates/ui2/src/components/slot.rs 🔗

@@ -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),
+}

crates/ui2/src/components/toggle.rs 🔗

@@ -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),
+        ),
+    }
+}

crates/ui2/src/prelude.rs 🔗

@@ -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]
@@ -179,61 +161,3 @@ pub enum SelectedState {
     PartiallySelected,
     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
-        }
-    }
-}

crates/ui2/src/static_data.rs 🔗

@@ -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())

crates/ui2/src/to_extract/collab_panel.rs 🔗

@@ -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)),
                         ),
                     ),
             )

crates/ui2/src/to_extract/notifications_panel.rs 🔗

@@ -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(

crates/ui2/src/to_extract/project_panel.rs 🔗

@@ -18,8 +18,7 @@ impl ProjectPanel {
             .id(self.id.clone())
             .flex()
             .flex_col()
-            .w_full()
-            .h_full()
+            .size_full()
             .bg(cx.theme().colors().surface)
             .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(