breadcrumbs.rs

  1use gpui::{
  2    elements::*, platform::MouseButton, AppContext, Entity, Subscription, View, ViewContext,
  3    ViewHandle,
  4};
  5use itertools::Itertools;
  6use search::ProjectSearchView;
  7use settings::Settings;
  8use workspace::{
  9    item::{ItemEvent, ItemHandle},
 10    ToolbarItemLocation, ToolbarItemView,
 11};
 12
 13pub enum Event {
 14    UpdateLocation,
 15}
 16
 17pub struct Breadcrumbs {
 18    pane_focused: bool,
 19    active_item: Option<Box<dyn ItemHandle>>,
 20    project_search: Option<ViewHandle<ProjectSearchView>>,
 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            project_search: Default::default(),
 31        }
 32    }
 33}
 34
 35impl Entity for Breadcrumbs {
 36    type Event = Event;
 37}
 38
 39impl View for Breadcrumbs {
 40    fn ui_name() -> &'static str {
 41        "Breadcrumbs"
 42    }
 43
 44    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
 45        let active_item = match &self.active_item {
 46            Some(active_item) => active_item,
 47            None => return Empty::new().into_any(),
 48        };
 49        let not_editor = active_item.downcast::<editor::Editor>().is_none();
 50
 51        let theme = cx.global::<Settings>().theme.clone();
 52        let style = &theme.workspace.breadcrumbs;
 53
 54        let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
 55            Some(breadcrumbs) => breadcrumbs,
 56            None => return Empty::new().into_any(),
 57        }
 58        .into_iter()
 59        .map(|breadcrumb| {
 60            Text::new(
 61                breadcrumb.text,
 62                theme.workspace.breadcrumbs.default.text.clone(),
 63            )
 64            .with_highlights(breadcrumb.highlights.unwrap_or_default())
 65            .into_any()
 66        });
 67
 68        let crumbs = Flex::row()
 69            .with_children(Itertools::intersperse_with(breadcrumbs, || {
 70                Label::new("", style.default.text.clone()).into_any()
 71            }))
 72            .constrained()
 73            .with_height(theme.workspace.breadcrumb_height)
 74            .contained();
 75
 76        if not_editor || !self.pane_focused {
 77            return crumbs
 78                .with_style(style.default.container)
 79                .aligned()
 80                .left()
 81                .into_any();
 82        }
 83
 84        MouseEventHandler::<Breadcrumbs, Breadcrumbs>::new(0, cx, |state, _| {
 85            let style = style.style_for(state, false);
 86            crumbs.with_style(style.container)
 87        })
 88        .on_click(MouseButton::Left, |_, _, cx| {
 89            cx.dispatch_action(outline::Toggle);
 90        })
 91        .with_tooltip::<Breadcrumbs>(
 92            0,
 93            "Show symbol outline".to_owned(),
 94            Some(Box::new(outline::Toggle)),
 95            theme.tooltip.clone(),
 96            cx,
 97        )
 98        .aligned()
 99        .left()
100        .into_any()
101    }
102}
103
104impl ToolbarItemView for Breadcrumbs {
105    fn set_active_pane_item(
106        &mut self,
107        active_pane_item: Option<&dyn ItemHandle>,
108        cx: &mut ViewContext<Self>,
109    ) -> ToolbarItemLocation {
110        cx.notify();
111        self.active_item = None;
112        self.project_search = None;
113        if let Some(item) = active_pane_item {
114            let this = cx.weak_handle();
115            self.subscription = Some(item.subscribe_to_item_events(
116                cx,
117                Box::new(move |event, cx| {
118                    if let Some(this) = this.upgrade(cx) {
119                        if let ItemEvent::UpdateBreadcrumbs = event {
120                            this.update(cx, |_, cx| {
121                                cx.emit(Event::UpdateLocation);
122                                cx.notify();
123                            });
124                        }
125                    }
126                }),
127            ));
128            self.active_item = Some(item.boxed_clone());
129            item.breadcrumb_location(cx)
130        } else {
131            ToolbarItemLocation::Hidden
132        }
133    }
134
135    fn location_for_event(
136        &self,
137        _: &Event,
138        current_location: ToolbarItemLocation,
139        cx: &AppContext,
140    ) -> ToolbarItemLocation {
141        if let Some(active_item) = self.active_item.as_ref() {
142            active_item.breadcrumb_location(cx)
143        } else {
144            current_location
145        }
146    }
147
148    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
149        self.pane_focused = pane_focused;
150    }
151}