1use gpui::prelude::*;
4use gpui::{div, px, App, IntoElement, SharedString, Window};
5
6use crate::devtools::Probed;
7use crate::icon::Glyph;
8use crate::theme::{theme, Size};
9
10#[derive(IntoElement)]
12pub struct List {
13 items: Vec<SharedString>,
14 ordered: bool,
15 size: Size,
16 spacing: Size,
17 icon: Option<Glyph>,
19}
20
21impl List {
22 pub fn new() -> Self {
23 List {
24 items: Vec::new(),
25 ordered: false,
26 size: Size::Md,
27 spacing: Size::Xs,
28 icon: None,
29 }
30 }
31
32 pub fn item(mut self, item: impl Into<SharedString>) -> Self {
33 self.items.push(item.into());
34 self
35 }
36
37 pub fn items<I, S>(mut self, items: I) -> Self
38 where
39 I: IntoIterator<Item = S>,
40 S: Into<SharedString>,
41 {
42 self.items.extend(items.into_iter().map(Into::into));
43 self
44 }
45
46 pub fn ordered(mut self, ordered: bool) -> Self {
47 self.ordered = ordered;
48 self
49 }
50
51 pub fn size(mut self, size: Size) -> Self {
52 self.size = size;
53 self
54 }
55
56 pub fn spacing(mut self, spacing: Size) -> Self {
57 self.spacing = spacing;
58 self
59 }
60
61 pub fn icon(mut self, icon: impl Into<Glyph>) -> Self {
62 self.icon = Some(icon.into());
63 self
64 }
65}
66
67impl Default for List {
68 fn default() -> Self {
69 List::new()
70 }
71}
72
73impl RenderOnce for List {
74 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
75 let t = theme(cx);
76 let font = t.font_size(self.size);
77 let gap = t.spacing(self.spacing);
78 let text = t.text().hsla();
79 let marker = t.dimmed().hsla();
80 let ordered = self.ordered;
81 let icon = self.icon.clone();
82
83 let rows = self.items.into_iter().enumerate().map(move |(i, item)| {
84 let bullet: Glyph = if ordered {
85 Glyph::from(format!("{}.", i + 1))
86 } else if let Some(glyph) = icon.clone() {
87 glyph
88 } else {
89 Glyph::from("\u{2022}")
90 };
91 div()
92 .flex()
93 .items_start()
94 .gap(px(8.0))
95 .text_size(px(font))
96 .child(div().min_w(px(font * 1.2)).text_color(marker).child(bullet))
97 .child(div().text_color(text).child(item))
98 });
99
100 div()
101 .flex()
102 .flex_col()
103 .gap(px(gap))
104 .children(rows)
105 .probe("List")
106 }
107}