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