breadcrumbs.rs

  1use gpui::{
  2    elements::*, platform::MouseButton, AppContext, Entity, RenderContext, Subscription, View,
  3    ViewContext, 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 RenderContext<Self>) -> ElementBox {
 45        let active_item = match &self.active_item {
 46            Some(active_item) => active_item,
 47            None => return Empty::new().boxed(),
 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().boxed(),
 57        };
 58
 59        let crumbs = Flex::row()
 60            .with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || {
 61                Label::new("", style.default.text.clone()).boxed()
 62            }))
 63            .constrained()
 64            .with_height(theme.workspace.breadcrumb_height)
 65            .contained();
 66
 67        if not_editor || !self.pane_focused {
 68            return crumbs
 69                .with_style(style.default.container)
 70                .aligned()
 71                .left()
 72                .boxed();
 73        }
 74
 75        MouseEventHandler::<Breadcrumbs>::new(0, cx, |state, _| {
 76            let style = style.style_for(state, false);
 77            crumbs.with_style(style.container).boxed()
 78        })
 79        .on_click(MouseButton::Left, |_, cx| {
 80            cx.dispatch_action(outline::Toggle);
 81        })
 82        .with_tooltip::<Breadcrumbs, _>(
 83            0,
 84            "Show symbol outline".to_owned(),
 85            Some(Box::new(outline::Toggle)),
 86            theme.tooltip.clone(),
 87            cx,
 88        )
 89        .aligned()
 90        .left()
 91        .boxed()
 92    }
 93}
 94
 95impl ToolbarItemView for Breadcrumbs {
 96    fn set_active_pane_item(
 97        &mut self,
 98        active_pane_item: Option<&dyn ItemHandle>,
 99        cx: &mut ViewContext<Self>,
100    ) -> ToolbarItemLocation {
101        cx.notify();
102        self.active_item = None;
103        self.project_search = None;
104        if let Some(item) = active_pane_item {
105            let this = cx.weak_handle();
106            self.subscription = Some(item.subscribe_to_item_events(
107                cx,
108                Box::new(move |event, cx| {
109                    if let Some(this) = this.upgrade(cx) {
110                        if let ItemEvent::UpdateBreadcrumbs = event {
111                            this.update(cx, |_, cx| {
112                                cx.emit(Event::UpdateLocation);
113                                cx.notify();
114                            });
115                        }
116                    }
117                }),
118            ));
119            self.active_item = Some(item.boxed_clone());
120            item.breadcrumb_location(cx)
121        } else {
122            ToolbarItemLocation::Hidden
123        }
124    }
125
126    fn location_for_event(
127        &self,
128        _: &Event,
129        current_location: ToolbarItemLocation,
130        cx: &AppContext,
131    ) -> ToolbarItemLocation {
132        if let Some(active_item) = self.active_item.as_ref() {
133            active_item.breadcrumb_location(cx)
134        } else {
135            current_location
136        }
137    }
138
139    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut gpui::AppContext) {
140        self.pane_focused = pane_focused;
141    }
142}