breadcrumbs.rs

  1use gpui::{
  2    AnyElement, App, Context, EventEmitter, Font, Global, IntoElement, Render, Subscription, Window,
  3};
  4use ui::prelude::*;
  5use workspace::{
  6    ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
  7    item::{HighlightedText, ItemEvent, ItemHandle},
  8};
  9
 10type RenderBreadcrumbTextFn = fn(
 11    Vec<HighlightedText>,
 12    Option<Font>,
 13    Option<AnyElement>,
 14    &dyn ItemHandle,
 15    bool,
 16    &mut Window,
 17    &App,
 18) -> AnyElement;
 19
 20pub struct RenderBreadcrumbText(pub RenderBreadcrumbTextFn);
 21
 22impl Global for RenderBreadcrumbText {}
 23
 24pub struct Breadcrumbs {
 25    pane_focused: bool,
 26    active_item: Option<Box<dyn ItemHandle>>,
 27    subscription: Option<Subscription>,
 28}
 29
 30impl Default for Breadcrumbs {
 31    fn default() -> Self {
 32        Self::new()
 33    }
 34}
 35
 36impl Breadcrumbs {
 37    pub fn new() -> Self {
 38        Self {
 39            pane_focused: false,
 40            active_item: Default::default(),
 41            subscription: Default::default(),
 42        }
 43    }
 44}
 45
 46impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
 47
 48impl Render for Breadcrumbs {
 49    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 50        let element = h_flex()
 51            .id("breadcrumb-container")
 52            .flex_grow()
 53            .h_8()
 54            .overflow_x_scroll()
 55            .text_ui(cx);
 56
 57        let Some(active_item) = self.active_item.as_ref() else {
 58            return element.into_any_element();
 59        };
 60
 61        let Some((segments, breadcrumb_font)) = active_item.breadcrumbs(cx) else {
 62            return element.into_any_element();
 63        };
 64
 65        let prefix_element = active_item.breadcrumb_prefix(window, cx);
 66
 67        if let Some(render_fn) = cx.try_global::<RenderBreadcrumbText>() {
 68            (render_fn.0)(
 69                segments,
 70                breadcrumb_font,
 71                prefix_element,
 72                active_item.as_ref(),
 73                false,
 74                window,
 75                cx,
 76            )
 77        } else {
 78            element.into_any_element()
 79        }
 80    }
 81}
 82
 83impl ToolbarItemView for Breadcrumbs {
 84    fn set_active_pane_item(
 85        &mut self,
 86        active_pane_item: Option<&dyn ItemHandle>,
 87        window: &mut Window,
 88        cx: &mut Context<Self>,
 89    ) -> ToolbarItemLocation {
 90        cx.notify();
 91        self.active_item = None;
 92
 93        let Some(item) = active_pane_item else {
 94            return ToolbarItemLocation::Hidden;
 95        };
 96
 97        let this = cx.entity().downgrade();
 98        self.subscription = Some(item.subscribe_to_item_events(
 99            window,
100            cx,
101            Box::new(move |event, _, cx| {
102                if let ItemEvent::UpdateBreadcrumbs = event {
103                    this.update(cx, |this, cx| {
104                        cx.notify();
105                        if let Some(active_item) = this.active_item.as_ref() {
106                            cx.emit(ToolbarItemEvent::ChangeLocation(
107                                active_item.breadcrumb_location(cx),
108                            ))
109                        }
110                    })
111                    .ok();
112                }
113            }),
114        ));
115        self.active_item = Some(item.boxed_clone());
116        item.breadcrumb_location(cx)
117    }
118
119    fn pane_focus_update(
120        &mut self,
121        pane_focused: bool,
122        _window: &mut Window,
123        _: &mut Context<Self>,
124    ) {
125        self.pane_focused = pane_focused;
126    }
127}