breadcrumbs.rs

  1use editor::Editor;
  2use gpui::{
  3    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    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
 34        let element = h_stack().text_ui();
 35        let Some(active_item) = self.active_item.as_ref() else {
 36            return element;
 37        };
 38        let Some(segments) = active_item.breadcrumbs(cx.theme(), cx) else {
 39            return element;
 40        };
 41
 42        let highlighted_segments = segments.into_iter().map(|segment| {
 43            let mut text_style = cx.text_style();
 44            text_style.color = Color::Muted.color(cx);
 45
 46            StyledText::new(segment.text)
 47                .with_highlights(&text_style, segment.highlights.unwrap_or_default())
 48                .into_any()
 49        });
 50        let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {
 51            Label::new("").color(Color::Muted).into_any_element()
 52        });
 53
 54        let breadcrumbs_stack = h_stack().gap_1().children(breadcrumbs);
 55        match active_item
 56            .downcast::<Editor>()
 57            .map(|editor| editor.downgrade())
 58        {
 59            Some(editor) => element.child(
 60                ButtonLike::new("toggle outline view")
 61                    .child(breadcrumbs_stack)
 62                    .style(ButtonStyle::Subtle)
 63                    .on_click(move |_, cx| {
 64                        if let Some(editor) = editor.upgrade() {
 65                            outline::toggle(editor, &outline::Toggle, cx)
 66                        }
 67                    })
 68                    .tooltip(|cx| Tooltip::for_action("Show symbol outline", &outline::Toggle, cx)),
 69            ),
 70            None => element
 71                // Match the height of the `ButtonLike` in the other arm.
 72                .h(rems(22. / 16.))
 73                .child(breadcrumbs_stack),
 74        }
 75    }
 76}
 77
 78impl ToolbarItemView for Breadcrumbs {
 79    fn set_active_pane_item(
 80        &mut self,
 81        active_pane_item: Option<&dyn ItemHandle>,
 82        cx: &mut ViewContext<Self>,
 83    ) -> ToolbarItemLocation {
 84        cx.notify();
 85        self.active_item = None;
 86        if let Some(item) = active_pane_item {
 87            let this = cx.view().downgrade();
 88            self.subscription = Some(item.subscribe_to_item_events(
 89                cx,
 90                Box::new(move |event, cx| {
 91                    if let ItemEvent::UpdateBreadcrumbs = event {
 92                        this.update(cx, |this, cx| {
 93                            cx.notify();
 94                            if let Some(active_item) = this.active_item.as_ref() {
 95                                cx.emit(ToolbarItemEvent::ChangeLocation(
 96                                    active_item.breadcrumb_location(cx),
 97                                ))
 98                            }
 99                        })
100                        .ok();
101                    }
102                }),
103            ));
104            self.active_item = Some(item.boxed_clone());
105            item.breadcrumb_location(cx)
106        } else {
107            ToolbarItemLocation::Hidden
108        }
109    }
110
111    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
112        self.pane_focused = pane_focused;
113    }
114}