breadcrumb.rs

  1use std::marker::PhantomData;
  2use std::path::PathBuf;
  3
  4use gpui2::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> {
 14    state_type: PhantomData<S>,
 15    path: PathBuf,
 16    symbols: Vec<Symbol>,
 17}
 18
 19impl<S: 'static + Send + Sync> 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, cx: &WindowContext) -> Div<S> {
 29        let theme = theme(cx);
 30
 31        div().child("").text_color(theme.text_muted)
 32    }
 33
 34    fn render(&mut self, view_state: &mut S, cx: &mut ViewContext<S>) -> impl IntoAnyElement<S> {
 35        let theme = theme(cx);
 36
 37        let symbols_len = self.symbols.len();
 38
 39        h_stack()
 40            .id("breadcrumb")
 41            .px_1()
 42            .text_sm()
 43            .text_color(theme.text_muted)
 44            .rounded_md()
 45            .hover(|style| style.bg(theme.ghost_element_hover))
 46            .active(|style| style.bg(theme.ghost_element_active))
 47            .child(self.path.clone().to_str().unwrap().to_string())
 48            .child(if !self.symbols.is_empty() {
 49                self.render_separator(cx)
 50            } else {
 51                div()
 52            })
 53            .child(
 54                div().flex().children(
 55                    self.symbols
 56                        .iter()
 57                        .enumerate()
 58                        // TODO: Could use something like `intersperse` here instead.
 59                        .flat_map(|(ix, symbol)| {
 60                            let mut items =
 61                                vec![div().flex().children(symbol.0.iter().map(|segment| {
 62                                    div().child(segment.text.clone()).text_color(segment.color)
 63                                }))];
 64
 65                            let is_last_segment = ix == symbols_len - 1;
 66                            if !is_last_segment {
 67                                items.push(self.render_separator(cx));
 68                            }
 69
 70                            items
 71                        })
 72                        .collect::<Vec<_>>(),
 73                ),
 74            )
 75    }
 76}
 77
 78#[cfg(feature = "stories")]
 79pub use stories::*;
 80
 81#[cfg(feature = "stories")]
 82mod stories {
 83    use std::str::FromStr;
 84
 85    use crate::Story;
 86
 87    use super::*;
 88
 89    #[derive(Element)]
 90    pub struct BreadcrumbStory<S: 'static + Send + Sync> {
 91        state_type: PhantomData<S>,
 92    }
 93
 94    impl<S: 'static + Send + Sync> BreadcrumbStory<S> {
 95        pub fn new() -> Self {
 96            Self {
 97                state_type: PhantomData,
 98            }
 99        }
100
101        fn render(
102            &mut self,
103            view_state: &mut S,
104            cx: &mut ViewContext<S>,
105        ) -> impl IntoAnyElement<S> {
106            let theme = theme(cx);
107
108            Story::container(cx)
109                .child(Story::title_for::<_, Breadcrumb<S>>(cx))
110                .child(Story::label(cx, "Default"))
111                .child(Breadcrumb::new(
112                    PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
113                    vec![
114                        Symbol(vec![
115                            HighlightedText {
116                                text: "impl ".to_string(),
117                                color: theme.syntax.keyword,
118                            },
119                            HighlightedText {
120                                text: "BreadcrumbStory".to_string(),
121                                color: theme.syntax.function,
122                            },
123                        ]),
124                        Symbol(vec![
125                            HighlightedText {
126                                text: "fn ".to_string(),
127                                color: theme.syntax.keyword,
128                            },
129                            HighlightedText {
130                                text: "render".to_string(),
131                                color: theme.syntax.function,
132                            },
133                        ]),
134                    ],
135                ))
136        }
137    }
138}