breadcrumbs.rs

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