breadcrumbs.rs

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