list_item.rs

 1use crate::prelude::{InteractionState, ToggleState};
 2use crate::theme::theme;
 3use crate::ui::{icon, IconAsset, Label};
 4use gpui2::geometry::rems;
 5use gpui2::style::{StyleHelpers, Styleable};
 6use gpui2::{elements::div, IntoElement};
 7use gpui2::{Element, ParentElement, ViewContext};
 8
 9#[derive(Element)]
10pub struct ListItem {
11    label: Label,
12    left_icon: Option<IconAsset>,
13    indent_level: u32,
14    state: InteractionState,
15    toggle: Option<ToggleState>,
16}
17
18pub fn list_item(label: Label) -> ListItem {
19    ListItem {
20        label,
21        indent_level: 0,
22        left_icon: None,
23        state: InteractionState::default(),
24        toggle: None,
25    }
26}
27
28impl ListItem {
29    pub fn indent_level(mut self, indent_level: u32) -> Self {
30        self.indent_level = indent_level;
31        self
32    }
33
34    pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
35        self.toggle = Some(toggle);
36        self
37    }
38
39    pub fn left_icon(mut self, left_icon: Option<IconAsset>) -> Self {
40        self.left_icon = left_icon;
41        self
42    }
43
44    pub fn state(mut self, state: InteractionState) -> Self {
45        self.state = state;
46        self
47    }
48
49    fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
50        let theme = theme(cx);
51
52        div()
53            .fill(theme.middle.base.default.background)
54            .hover()
55            .fill(theme.middle.base.hovered.background)
56            .active()
57            .fill(theme.middle.base.pressed.background)
58            .relative()
59            .child(
60                div()
61                    .h_7()
62                    .px_2()
63                    // .ml(rems(0.75 * self.indent_level as f32))
64                    .children((0..self.indent_level).map(|_| {
65                        div().w(rems(0.75)).h_full().flex().justify_center().child(
66                            div()
67                                .w_px()
68                                .h_full()
69                                .fill(theme.middle.base.default.border)
70                                .hover()
71                                .fill(theme.middle.warning.default.border)
72                                .active()
73                                .fill(theme.middle.negative.default.border),
74                        )
75                    }))
76                    .flex()
77                    .gap_2()
78                    .items_center()
79                    .children(match self.toggle {
80                        Some(ToggleState::NotToggled) => Some(icon(IconAsset::ChevronRight)),
81                        Some(ToggleState::Toggled) => Some(icon(IconAsset::ChevronDown)),
82                        None => None,
83                    })
84                    .children(self.left_icon.map(|i| icon(i)))
85                    .child(self.label.clone()),
86            )
87    }
88}