list_header.rs

  1use std::rc::Rc;
  2
  3use gpui::{ClickEvent, Div};
  4
  5use crate::prelude::*;
  6use crate::{disclosure_control, h_stack, Icon, IconButton, IconElement, IconSize, Label, Toggle};
  7
  8pub enum ListHeaderMeta {
  9    Tools(Vec<IconButton>),
 10    // TODO: This should be a button
 11    Button(Label),
 12    Text(Label),
 13}
 14
 15#[derive(IntoElement)]
 16pub struct ListHeader {
 17    label: SharedString,
 18    left_icon: Option<Icon>,
 19    meta: Option<ListHeaderMeta>,
 20    toggle: Toggle,
 21    on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
 22    inset: bool,
 23    selected: bool,
 24}
 25
 26impl ListHeader {
 27    pub fn new(label: impl Into<SharedString>) -> Self {
 28        Self {
 29            label: label.into(),
 30            left_icon: None,
 31            meta: None,
 32            inset: false,
 33            toggle: Toggle::NotToggleable,
 34            on_toggle: None,
 35            selected: false,
 36        }
 37    }
 38
 39    pub fn toggle(mut self, toggle: Toggle) -> Self {
 40        self.toggle = toggle;
 41        self
 42    }
 43
 44    pub fn on_toggle(
 45        mut self,
 46        on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
 47    ) -> Self {
 48        self.on_toggle = Some(Rc::new(on_toggle));
 49        self
 50    }
 51
 52    pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
 53        self.left_icon = left_icon;
 54        self
 55    }
 56
 57    pub fn right_button(self, button: IconButton) -> Self {
 58        self.meta(Some(ListHeaderMeta::Tools(vec![button])))
 59    }
 60
 61    pub fn meta(mut self, meta: Option<ListHeaderMeta>) -> Self {
 62        self.meta = meta;
 63        self
 64    }
 65
 66    pub fn selected(mut self, selected: bool) -> Self {
 67        self.selected = selected;
 68        self
 69    }
 70}
 71
 72impl RenderOnce for ListHeader {
 73    type Rendered = Div;
 74
 75    fn render(self, cx: &mut WindowContext) -> Self::Rendered {
 76        let disclosure_control = disclosure_control(self.toggle, self.on_toggle);
 77
 78        let meta = match self.meta {
 79            Some(ListHeaderMeta::Tools(icons)) => div().child(
 80                h_stack()
 81                    .gap_2()
 82                    .items_center()
 83                    .children(icons.into_iter().map(|i| i.color(Color::Muted))),
 84            ),
 85            Some(ListHeaderMeta::Button(label)) => div().child(label),
 86            Some(ListHeaderMeta::Text(label)) => div().child(label),
 87            None => div(),
 88        };
 89
 90        h_stack().w_full().relative().child(
 91            div()
 92                .h_5()
 93                .when(self.inset, |this| this.px_2())
 94                .when(self.selected, |this| {
 95                    this.bg(cx.theme().colors().ghost_element_selected)
 96                })
 97                .flex()
 98                .flex_1()
 99                .items_center()
100                .justify_between()
101                .w_full()
102                .gap_1()
103                .child(
104                    h_stack()
105                        .gap_1()
106                        .child(
107                            div()
108                                .flex()
109                                .gap_1()
110                                .items_center()
111                                .children(self.left_icon.map(|i| {
112                                    IconElement::new(i)
113                                        .color(Color::Muted)
114                                        .size(IconSize::Small)
115                                }))
116                                .child(Label::new(self.label.clone()).color(Color::Muted)),
117                        )
118                        .child(disclosure_control),
119                )
120                .child(meta),
121        )
122    }
123}