breadcrumbs.rs

  1use gpui::{
  2    Div, Element, EventEmitter, IntoElement, ParentElement, Render, StyledText, Subscription,
  3    ViewContext, WeakView,
  4};
  5use itertools::Itertools;
  6use theme::ActiveTheme;
  7use ui::{prelude::*, ButtonLike, ButtonStyle, Label};
  8use workspace::{
  9    item::{ItemEvent, ItemHandle},
 10    ToolbarItemEvent, 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    subscription: Option<Subscription>,
 21    _workspace: WeakView<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            _workspace: workspace.weak_handle(),
 31        }
 32    }
 33}
 34
 35impl EventEmitter<Event> for Breadcrumbs {}
 36impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
 37
 38impl Render for Breadcrumbs {
 39    type Element = Div;
 40
 41    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
 42        let element = h_stack().text_ui();
 43
 44        let Some(active_item) = &self
 45            .active_item
 46            .as_ref()
 47            .filter(|item| item.downcast::<editor::Editor>().is_some())
 48        else {
 49            return element;
 50        };
 51
 52        let Some(segments) = active_item.breadcrumbs(cx.theme(), cx) else {
 53            return element;
 54        };
 55
 56        let highlighted_segments = segments.into_iter().map(|segment| {
 57            StyledText::new(segment.text)
 58                .with_highlights(&cx.text_style(), segment.highlights.unwrap_or_default())
 59                .into_any()
 60        });
 61        let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {
 62            Label::new("").into_any_element()
 63        });
 64
 65        element.child(
 66            ButtonLike::new("toggle outline view")
 67                .style(ButtonStyle::Subtle)
 68                .child(h_stack().gap_1().children(breadcrumbs))
 69                // We disable the button when it is not focused
 70                // due to ... @julia what was the reason again?
 71                .disabled(!self.pane_focused)
 72                .on_click(move |_, _cx| {
 73                    todo!("outline::toggle");
 74                    // this.update(cx, |this, cx| {
 75                    //     if let Some(workspace) = this.workspace.upgrade() {
 76                    //         workspace.update(cx, |_workspace, _cx| {
 77                    //             outline::toggle(workspace, &Default::default(), cx)
 78                    //         })
 79                    //     }
 80                    // })
 81                    // .ok();
 82                }),
 83        )
 84    }
 85}
 86
 87// impl View for Breadcrumbs {
 88//     fn ui_name() -> &'static str {
 89//         "Breadcrumbs"
 90//     }
 91
 92//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
 93//         let active_item = match &self.active_item {
 94//             Some(active_item) => active_item,
 95//             None => return Empty::new().into_any(),
 96//         };
 97//         let not_editor = active_item.downcast::<editor::Editor>().is_none();
 98
 99//         let theme = theme::current(cx).clone();
100//         let style = &theme.workspace.toolbar.breadcrumbs;
101
102//         let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
103//             Some(breadcrumbs) => breadcrumbs,
104//             None => return Empty::new().into_any(),
105//         }
106//         .into_iter()
107//         .map(|breadcrumb| {
108//             Text::new(
109//                 breadcrumb.text,
110//                 theme.workspace.toolbar.breadcrumbs.default.text.clone(),
111//             )
112//             .with_highlights(breadcrumb.highlights.unwrap_or_default())
113//             .into_any()
114//         });
115
116//         let crumbs = Flex::row()
117//             .with_children(Itertools::intersperse_with(breadcrumbs, || {
118//                 Label::new(" › ", style.default.text.clone()).into_any()
119//             }))
120//             .constrained()
121//             .with_height(theme.workspace.toolbar.breadcrumb_height)
122//             .contained();
123
124//         if not_editor || !self.pane_focused {
125//             return crumbs
126//                 .with_style(style.default.container)
127//                 .aligned()
128//                 .left()
129//                 .into_any();
130//         }
131
132//         MouseEventHandler::new::<Breadcrumbs, _>(0, cx, |state, _| {
133//             let style = style.style_for(state);
134//             crumbs.with_style(style.container)
135//         })
136//         .on_click(MouseButton::Left, |_, this, cx| {
137//             if let Some(workspace) = this.workspace.upgrade(cx) {
138//                 workspace.update(cx, |workspace, cx| {
139//                     outline::toggle(workspace, &Default::default(), cx)
140//                 })
141//             }
142//         })
143//         .with_tooltip::<Breadcrumbs>(
144//             0,
145//             "Show symbol outline".to_owned(),
146//             Some(Box::new(outline::Toggle)),
147//             theme.tooltip.clone(),
148//             cx,
149//         )
150//         .aligned()
151//         .left()
152//         .into_any()
153//     }
154// }
155
156impl ToolbarItemView for Breadcrumbs {
157    fn set_active_pane_item(
158        &mut self,
159        active_pane_item: Option<&dyn ItemHandle>,
160        cx: &mut ViewContext<Self>,
161    ) -> ToolbarItemLocation {
162        cx.notify();
163        self.active_item = None;
164        if let Some(item) = active_pane_item {
165            let this = cx.view().downgrade();
166            self.subscription = Some(item.subscribe_to_item_events(
167                cx,
168                Box::new(move |event, cx| {
169                    if let ItemEvent::UpdateBreadcrumbs = event {
170                        this.update(cx, |_, cx| {
171                            cx.emit(Event::UpdateLocation);
172                            cx.notify();
173                        })
174                        .ok();
175                    }
176                }),
177            ));
178            self.active_item = Some(item.boxed_clone());
179            item.breadcrumb_location(cx)
180        } else {
181            ToolbarItemLocation::Hidden
182        }
183    }
184
185    // fn location_for_event(
186    //     &self,
187    //     _: &Event,
188    //     current_location: ToolbarItemLocation,
189    //     cx: &AppContext,
190    // ) -> ToolbarItemLocation {
191    //     if let Some(active_item) = self.active_item.as_ref() {
192    //         active_item.breadcrumb_location(cx)
193    //     } else {
194    //         current_location
195    //     }
196    // }
197
198    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
199        self.pane_focused = pane_focused;
200    }
201}