list_header.rs

  1use std::rc::Rc;
  2
  3use gpui::{AnyElement, ClickEvent, Div};
  4use smallvec::SmallVec;
  5
  6use crate::prelude::*;
  7use crate::{h_stack, Disclosure, Icon, IconElement, IconSize, Label};
  8
  9#[derive(IntoElement)]
 10pub struct ListHeader {
 11    label: SharedString,
 12    left_icon: Option<Icon>,
 13    meta: SmallVec<[AnyElement; 2]>,
 14    toggle: Option<bool>,
 15    on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
 16    inset: bool,
 17    selected: bool,
 18}
 19
 20impl ListHeader {
 21    pub fn new(label: impl Into<SharedString>) -> Self {
 22        Self {
 23            label: label.into(),
 24            left_icon: None,
 25            meta: SmallVec::new(),
 26            inset: false,
 27            toggle: None,
 28            on_toggle: None,
 29            selected: false,
 30        }
 31    }
 32
 33    pub fn toggle(mut self, toggle: impl Into<Option<bool>>) -> Self {
 34        self.toggle = toggle.into();
 35        self
 36    }
 37
 38    pub fn on_toggle(
 39        mut self,
 40        on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
 41    ) -> Self {
 42        self.on_toggle = Some(Rc::new(on_toggle));
 43        self
 44    }
 45
 46    pub fn left_icon(mut self, left_icon: impl Into<Option<Icon>>) -> Self {
 47        self.left_icon = left_icon.into();
 48        self
 49    }
 50
 51    pub fn meta(mut self, meta: impl IntoElement) -> Self {
 52        self.meta.push(meta.into_any_element());
 53        self
 54    }
 55}
 56
 57impl Selectable for ListHeader {
 58    fn selected(mut self, selected: bool) -> Self {
 59        self.selected = selected;
 60        self
 61    }
 62}
 63
 64impl RenderOnce for ListHeader {
 65    type Rendered = Div;
 66
 67    fn render(self, cx: &mut WindowContext) -> Self::Rendered {
 68        h_stack().w_full().relative().child(
 69            div()
 70                .h_5()
 71                .when(self.inset, |this| this.px_2())
 72                .when(self.selected, |this| {
 73                    this.bg(cx.theme().colors().ghost_element_selected)
 74                })
 75                .flex()
 76                .flex_1()
 77                .items_center()
 78                .justify_between()
 79                .w_full()
 80                .gap_1()
 81                .child(
 82                    h_stack()
 83                        .gap_1()
 84                        .child(
 85                            div()
 86                                .flex()
 87                                .gap_1()
 88                                .items_center()
 89                                .children(self.left_icon.map(|i| {
 90                                    IconElement::new(i)
 91                                        .color(Color::Muted)
 92                                        .size(IconSize::Small)
 93                                }))
 94                                .child(Label::new(self.label.clone()).color(Color::Muted)),
 95                        )
 96                        .children(
 97                            self.toggle
 98                                .map(|is_open| Disclosure::new(is_open).on_toggle(self.on_toggle)),
 99                        ),
100                )
101                .child(h_stack().gap_2().items_center().children(self.meta)),
102        )
103    }
104}