1use std::rc::Rc;
2
3use gpui::{ClickEvent, Div};
4
5use crate::prelude::*;
6use crate::{h_stack, Disclosure, Icon, IconButton, IconElement, IconSize, Label, Toggleable};
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: Toggleable,
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: Toggleable::NotToggleable,
34 on_toggle: None,
35 selected: false,
36 }
37 }
38
39 pub fn toggle(mut self, toggle: Toggleable) -> 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 meta = match self.meta {
77 Some(ListHeaderMeta::Tools(icons)) => div().child(
78 h_stack()
79 .gap_2()
80 .items_center()
81 .children(icons.into_iter().map(|i| i.color(Color::Muted))),
82 ),
83 Some(ListHeaderMeta::Button(label)) => div().child(label),
84 Some(ListHeaderMeta::Text(label)) => div().child(label),
85 None => div(),
86 };
87
88 h_stack().w_full().relative().child(
89 div()
90 .h_5()
91 .when(self.inset, |this| this.px_2())
92 .when(self.selected, |this| {
93 this.bg(cx.theme().colors().ghost_element_selected)
94 })
95 .flex()
96 .flex_1()
97 .items_center()
98 .justify_between()
99 .w_full()
100 .gap_1()
101 .child(
102 h_stack()
103 .gap_1()
104 .child(
105 div()
106 .flex()
107 .gap_1()
108 .items_center()
109 .children(self.left_icon.map(|i| {
110 IconElement::new(i)
111 .color(Color::Muted)
112 .size(IconSize::Small)
113 }))
114 .child(Label::new(self.label.clone()).color(Color::Muted)),
115 )
116 .children(
117 Disclosure::from_toggleable(self.toggle)
118 .map(|disclosure| disclosure.on_toggle(self.on_toggle)),
119 ),
120 )
121 .child(meta),
122 )
123 }
124}