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 enum Event {
 15    UpdateLocation,
 16}
 17
 18pub struct Breadcrumbs {
 19    pane_focused: bool,
 20    active_item: Option<Box<dyn ItemHandle>>,
 21    subscription: Option<Subscription>,
 22}
 23
 24impl Breadcrumbs {
 25    pub fn new() -> Self {
 26        Self {
 27            pane_focused: false,
 28            active_item: Default::default(),
 29            subscription: Default::default(),
 30        }
 31    }
 32}
 33
 34impl EventEmitter<Event> for Breadcrumbs {}
 35impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
 36
 37impl Render for Breadcrumbs {
 38    type Element = Div;
 39
 40    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
 41        let element = h_stack().text_ui();
 42
 43        let Some(active_item) = &self
 44            .active_item
 45            .as_ref()
 46            .filter(|item| item.downcast::<editor::Editor>().is_some())
 47        else {
 48            return element;
 49        };
 50
 51        let Some(segments) = active_item.breadcrumbs(cx.theme(), cx) else {
 52            return element;
 53        };
 54
 55        let highlighted_segments = segments.into_iter().map(|segment| {
 56            StyledText::new(segment.text)
 57                .with_highlights(&cx.text_style(), segment.highlights.unwrap_or_default())
 58                .into_any()
 59        });
 60        let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {
 61            Label::new("").into_any_element()
 62        });
 63
 64        let editor = active_item
 65            .downcast::<Editor>()
 66            .map(|editor| editor.downgrade());
 67
 68        element.child(
 69            ButtonLike::new("toggle outline view")
 70                .style(ButtonStyle::Subtle)
 71                .child(h_stack().gap_1().children(breadcrumbs))
 72                .on_click(move |_, cx| {
 73                    if let Some(editor) = editor.as_ref().and_then(|editor| editor.upgrade()) {
 74                        outline::toggle(editor, &outline::Toggle, cx)
 75                    }
 76                })
 77                .tooltip(|cx| Tooltip::for_action("Show symbol outline", &outline::Toggle, cx)),
 78        )
 79    }
 80}
 81
 82impl ToolbarItemView for Breadcrumbs {
 83    fn set_active_pane_item(
 84        &mut self,
 85        active_pane_item: Option<&dyn ItemHandle>,
 86        cx: &mut ViewContext<Self>,
 87    ) -> ToolbarItemLocation {
 88        cx.notify();
 89        self.active_item = None;
 90        if let Some(item) = active_pane_item {
 91            let this = cx.view().downgrade();
 92            self.subscription = Some(item.subscribe_to_item_events(
 93                cx,
 94                Box::new(move |event, cx| {
 95                    if let ItemEvent::UpdateBreadcrumbs = event {
 96                        this.update(cx, |_, cx| {
 97                            cx.emit(Event::UpdateLocation);
 98                            cx.notify();
 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 location_for_event(
112    //     &self,
113    //     _: &Event,
114    //     current_location: ToolbarItemLocation,
115    //     cx: &AppContext,
116    // ) -> ToolbarItemLocation {
117    //     if let Some(active_item) = self.active_item.as_ref() {
118    //         active_item.breadcrumb_location(cx)
119    //     } else {
120    //         current_location
121    //     }
122    // }
123
124    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
125        self.pane_focused = pane_focused;
126    }
127}