Skip to main content

guise/data/
list.rs

1//! `List` — an ordered or unordered list of text items.
2
3use 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/// A bulleted or numbered list.
11#[derive(IntoElement)]
12pub struct List {
13  items: Vec<SharedString>,
14  ordered: bool,
15  size: Size,
16  spacing: Size,
17  /// Custom bullet glyph for unordered lists (defaults to a dot).
18  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}