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