breadcrumbs.rs

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