breadcrumb.rs

  1use std::marker::PhantomData;
  2use std::path::PathBuf;
  3
  4use gpui3::Div;
  5
  6use crate::prelude::*;
  7use crate::{h_stack, HighlightedText};
  8
  9#[derive(Clone)]
 10pub struct Symbol(pub Vec<HighlightedText>);
 11
 12#[derive(Element)]
 13pub struct Breadcrumb<S: 'static + Send + Sync + Clone> {
 14    state_type: PhantomData<S>,
 15    path: PathBuf,
 16    symbols: Vec<Symbol>,
 17}
 18
 19impl<S: 'static + Send + Sync + Clone> Breadcrumb<S> {
 20    pub fn new(path: PathBuf, symbols: Vec<Symbol>) -> Self {
 21        Self {
 22            state_type: PhantomData,
 23            path,
 24            symbols,
 25        }
 26    }
 27
 28    fn render_separator(&self, theme: &Theme) -> Div<S> {
 29        div()
 30            .child("")
 31            .text_color(HighlightColor::Default.hsla(theme))
 32    }
 33
 34    fn render(&mut self, view_state: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 35        let theme = theme(cx);
 36
 37        let symbols_len = self.symbols.len();
 38
 39        h_stack()
 40            .px_1()
 41            // TODO: Read font from theme (or settings?).
 42            .font("Zed Mono Extended")
 43            .text_sm()
 44            .text_color(theme.middle.base.default.foreground)
 45            .rounded_md()
 46            .hover()
 47            .fill(theme.highest.base.hovered.background)
 48            .child(self.path.clone().to_str().unwrap().to_string())
 49            .child(if !self.symbols.is_empty() {
 50                self.render_separator(&theme)
 51            } else {
 52                div()
 53            })
 54            .child(
 55                div().flex().children(
 56                    self.symbols
 57                        .iter()
 58                        .enumerate()
 59                        // TODO: Could use something like `intersperse` here instead.
 60                        .flat_map(|(ix, symbol)| {
 61                            let mut items =
 62                                vec![div().flex().children(symbol.0.iter().map(|segment| {
 63                                    div().child(segment.text.clone()).text_color(segment.color)
 64                                }))];
 65
 66                            let is_last_segment = ix == symbols_len - 1;
 67                            if !is_last_segment {
 68                                items.push(self.render_separator(&theme));
 69                            }
 70
 71                            items
 72                        })
 73                        .collect::<Vec<_>>(),
 74                ),
 75            )
 76    }
 77}
 78
 79#[cfg(feature = "stories")]
 80pub use stories::*;
 81
 82#[cfg(feature = "stories")]
 83mod stories {
 84    use std::str::FromStr;
 85
 86    use crate::Story;
 87
 88    use super::*;
 89
 90    #[derive(Element)]
 91    pub struct BreadcrumbStory<S: 'static + Send + Sync + Clone> {
 92        state_type: PhantomData<S>,
 93    }
 94
 95    impl<S: 'static + Send + Sync + Clone> BreadcrumbStory<S> {
 96        pub fn new() -> Self {
 97            Self {
 98                state_type: PhantomData,
 99            }
100        }
101
102        fn render(&mut self, view_state: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
103            let theme = theme(cx);
104
105            Story::container(cx)
106                .child(Story::title_for::<_, Breadcrumb<S>>(cx))
107                .child(Story::label(cx, "Default"))
108                .child(Breadcrumb::new(
109                    PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
110                    vec![
111                        Symbol(vec![
112                            HighlightedText {
113                                text: "impl ".to_string(),
114                                color: HighlightColor::Keyword.hsla(&theme),
115                            },
116                            HighlightedText {
117                                text: "BreadcrumbStory".to_string(),
118                                color: HighlightColor::Function.hsla(&theme),
119                            },
120                        ]),
121                        Symbol(vec![
122                            HighlightedText {
123                                text: "fn ".to_string(),
124                                color: HighlightColor::Keyword.hsla(&theme),
125                            },
126                            HighlightedText {
127                                text: "render".to_string(),
128                                color: HighlightColor::Function.hsla(&theme),
129                            },
130                        ]),
131                    ],
132                ))
133        }
134    }
135}