breadcrumbs.rs

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