breadcrumbs.rs

  1use editor::Editor;
  2use gpui::{
  3    Div, Element, EventEmitter, IntoElement, ParentElement, Render, StyledText, Subscription,
  4    ViewContext,
  5};
  6use itertools::Itertools;
  7use theme::ActiveTheme;
  8use ui::{prelude::*, ButtonLike, ButtonStyle, Label, Tooltip};
  9use workspace::{
 10    item::{ItemEvent, ItemHandle},
 11    ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
 12};
 13
 14pub struct Breadcrumbs {
 15    pane_focused: bool,
 16    active_item: Option<Box<dyn ItemHandle>>,
 17    subscription: Option<Subscription>,
 18}
 19
 20impl Breadcrumbs {
 21    pub fn new() -> Self {
 22        Self {
 23            pane_focused: false,
 24            active_item: Default::default(),
 25            subscription: Default::default(),
 26        }
 27    }
 28}
 29
 30impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
 31
 32impl Render for Breadcrumbs {
 33    type Element = Div;
 34
 35    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
 36        let element = h_stack().text_ui();
 37        let Some(active_item) = self.active_item.as_ref() else {
 38            return element;
 39        };
 40        let Some(segments) = active_item.breadcrumbs(cx.theme(), cx) else {
 41            return element;
 42        };
 43
 44        let highlighted_segments = segments.into_iter().map(|segment| {
 45            let mut text_style = cx.text_style();
 46            text_style.color = Color::Muted.color(cx);
 47
 48            StyledText::new(segment.text)
 49                .with_highlights(&text_style, segment.highlights.unwrap_or_default())
 50                .into_any()
 51        });
 52        let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {
 53            Label::new("").color(Color::Muted).into_any_element()
 54        });
 55
 56        let breadcrumbs_stack = h_stack().gap_1().children(breadcrumbs);
 57        match active_item
 58            .downcast::<Editor>()
 59            .map(|editor| editor.downgrade())
 60        {
 61            Some(editor) => element.child(
 62                ButtonLike::new("toggle outline view")
 63                    .child(breadcrumbs_stack)
 64                    .style(ButtonStyle::Subtle)
 65                    .on_click(move |_, cx| {
 66                        if let Some(editor) = editor.upgrade() {
 67                            outline::toggle(editor, &outline::Toggle, cx)
 68                        }
 69                    })
 70                    .tooltip(|cx| Tooltip::for_action("Show symbol outline", &outline::Toggle, cx)),
 71            ),
 72            None => element.child(breadcrumbs_stack),
 73        }
 74    }
 75}
 76
 77impl ToolbarItemView for Breadcrumbs {
 78    fn set_active_pane_item(
 79        &mut self,
 80        active_pane_item: Option<&dyn ItemHandle>,
 81        cx: &mut ViewContext<Self>,
 82    ) -> ToolbarItemLocation {
 83        cx.notify();
 84        self.active_item = None;
 85        if let Some(item) = active_pane_item {
 86            let this = cx.view().downgrade();
 87            self.subscription = Some(item.subscribe_to_item_events(
 88                cx,
 89                Box::new(move |event, cx| {
 90                    if let ItemEvent::UpdateBreadcrumbs = event {
 91                        this.update(cx, |_, cx| {
 92                            cx.notify();
 93                        })
 94                        .ok();
 95                    }
 96                }),
 97            ));
 98            self.active_item = Some(item.boxed_clone());
 99            item.breadcrumb_location(cx)
100        } else {
101            ToolbarItemLocation::Hidden
102        }
103    }
104
105    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
106        self.pane_focused = pane_focused;
107    }
108}