Make clicking `ListHeader` labels toggle the disclosure (#4140)

Marshall Bowers created

This PR makes clicking the label inside of a `ListHeader` with a
disclosure also toggle the disclosure.

Release Notes:

- Added support for clicking the "Online", "Offline", and "Requests"
headers in the contact list to toggle their expansion.

Change summary

crates/ui/src/components/disclosure.rs       |  6 ++++--
crates/ui/src/components/list/list_header.rs | 14 ++++++++++----
crates/ui/src/components/list/list_item.rs   |  6 ++++--
3 files changed, 18 insertions(+), 8 deletions(-)

Detailed changes

crates/ui/src/components/disclosure.rs 🔗

@@ -1,3 +1,5 @@
+use std::sync::Arc;
+
 use gpui::ClickEvent;
 
 use crate::{prelude::*, Color, IconButton, IconName, IconSize};
@@ -6,7 +8,7 @@ use crate::{prelude::*, Color, IconButton, IconName, IconSize};
 pub struct Disclosure {
     id: ElementId,
     is_open: bool,
-    on_toggle: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
 }
 
 impl Disclosure {
@@ -20,7 +22,7 @@ impl Disclosure {
 
     pub fn on_toggle(
         mut self,
-        handler: impl Into<Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>>,
+        handler: impl Into<Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>>,
     ) -> Self {
         self.on_toggle = handler.into();
         self

crates/ui/src/components/list/list_header.rs 🔗

@@ -1,3 +1,5 @@
+use std::sync::Arc;
+
 use crate::{h_flex, prelude::*, Disclosure, Label};
 use gpui::{AnyElement, ClickEvent};
 
@@ -14,7 +16,7 @@ pub struct ListHeader {
     /// It will obscure the `end_slot` when visible.
     end_hover_slot: Option<AnyElement>,
     toggle: Option<bool>,
-    on_toggle: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
     inset: bool,
     selected: bool,
 }
@@ -42,7 +44,7 @@ impl ListHeader {
         mut self,
         on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.on_toggle = Some(Box::new(on_toggle));
+        self.on_toggle = Some(Arc::new(on_toggle));
         self
     }
 
@@ -98,15 +100,19 @@ impl RenderOnce for ListHeader {
                         h_flex()
                             .gap_1()
                             .children(self.toggle.map(|is_open| {
-                                Disclosure::new("toggle", is_open).on_toggle(self.on_toggle)
+                                Disclosure::new("toggle", is_open).on_toggle(self.on_toggle.clone())
                             }))
                             .child(
                                 div()
+                                    .id("label_container")
                                     .flex()
                                     .gap_1()
                                     .items_center()
                                     .children(self.start_slot)
-                                    .child(Label::new(self.label.clone()).color(Color::Muted)),
+                                    .child(Label::new(self.label.clone()).color(Color::Muted))
+                                    .when_some(self.on_toggle, |this, on_toggle| {
+                                        this.on_click(move |event, cx| on_toggle(event, cx))
+                                    }),
                             ),
                     )
                     .child(h_flex().children(self.end_slot))

crates/ui/src/components/list/list_item.rs 🔗

@@ -1,3 +1,5 @@
+use std::sync::Arc;
+
 use gpui::{px, AnyElement, AnyView, ClickEvent, MouseButton, MouseDownEvent, Pixels};
 use smallvec::SmallVec;
 
@@ -29,7 +31,7 @@ pub struct ListItem {
     toggle: Option<bool>,
     inset: bool,
     on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
-    on_toggle: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
     tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
     on_secondary_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
     children: SmallVec<[AnyElement; 2]>,
@@ -104,7 +106,7 @@ impl ListItem {
         mut self,
         on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.on_toggle = Some(Box::new(on_toggle));
+        self.on_toggle = Some(Arc::new(on_toggle));
         self
     }