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