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.child(breadcrumbs_stack),
 71        }
 72    }
 73}
 74
 75impl ToolbarItemView for Breadcrumbs {
 76    fn set_active_pane_item(
 77        &mut self,
 78        active_pane_item: Option<&dyn ItemHandle>,
 79        cx: &mut ViewContext<Self>,
 80    ) -> ToolbarItemLocation {
 81        cx.notify();
 82        self.active_item = None;
 83        if let Some(item) = active_pane_item {
 84            let this = cx.view().downgrade();
 85            self.subscription = Some(item.subscribe_to_item_events(
 86                cx,
 87                Box::new(move |event, cx| {
 88                    if let ItemEvent::UpdateBreadcrumbs = event {
 89                        this.update(cx, |this, cx| {
 90                            cx.notify();
 91                            if let Some(active_item) = this.active_item.as_ref() {
 92                                cx.emit(ToolbarItemEvent::ChangeLocation(
 93                                    active_item.breadcrumb_location(cx),
 94                                ))
 95                            }
 96                        })
 97                        .ok();
 98                    }
 99                }),
100            ));
101            self.active_item = Some(item.boxed_clone());
102            item.breadcrumb_location(cx)
103        } else {
104            ToolbarItemLocation::Hidden
105        }
106    }
107
108    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
109        self.pane_focused = pane_focused;
110    }
111}