185 lines · 6.0 KB · Rust
RawDownload
use editor::Editor;use gpui::{    Context, Element, EventEmitter, Focusable, IntoElement, ParentElement, Render, StyledText,    Subscription, Window,};use itertools::Itertools;use std::cmp;use theme::ActiveTheme;use ui::{prelude::*, ButtonLike, ButtonStyle, Label, Tooltip};use workspace::{    item::{BreadcrumbText, ItemEvent, ItemHandle},    ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,}; pub struct Breadcrumbs {    pane_focused: bool,    active_item: Option<Box<dyn ItemHandle>>,    subscription: Option<Subscription>,} impl Default for Breadcrumbs {    fn default() -> Self {        Self::new()    }} impl Breadcrumbs {    pub fn new() -> Self {        Self {            pane_focused: false,            active_item: Default::default(),            subscription: Default::default(),        }    }} impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {} impl Render for Breadcrumbs {    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        const MAX_SEGMENTS: usize = 12;         let element = h_flex()            .id("breadcrumb-container")            .flex_grow()            .overflow_x_scroll()            .text_ui(cx);         let Some(active_item) = self.active_item.as_ref() else {            return element;        };         let Some(mut segments) = active_item.breadcrumbs(cx.theme(), cx) else {            return element;        };         let prefix_end_ix = cmp::min(segments.len(), MAX_SEGMENTS / 2);        let suffix_start_ix = cmp::max(            prefix_end_ix,            segments.len().saturating_sub(MAX_SEGMENTS / 2),        );         if suffix_start_ix > prefix_end_ix {            segments.splice(                prefix_end_ix..suffix_start_ix,                Some(BreadcrumbText {                    text: "⋯".into(),                    highlights: None,                    font: None,                }),            );        }         let highlighted_segments = segments.into_iter().map(|segment| {            let mut text_style = window.text_style();            if let Some(font) = segment.font {                text_style.font_family = font.family;                text_style.font_features = font.features;                text_style.font_style = font.style;                text_style.font_weight = font.weight;            }            text_style.color = Color::Muted.color(cx);             StyledText::new(segment.text.replace('\n', "⏎"))                .with_highlights(&text_style, segment.highlights.unwrap_or_default())                .into_any()        });        let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {            Label::new("›").color(Color::Placeholder).into_any_element()        });         let breadcrumbs_stack = h_flex().gap_1().children(breadcrumbs);         match active_item            .downcast::<Editor>()            .map(|editor| editor.downgrade())        {            Some(editor) => element.child(                ButtonLike::new("toggle outline view")                    .child(breadcrumbs_stack)                    .style(ButtonStyle::Transparent)                    .on_click({                        let editor = editor.clone();                        move |_, window, cx| {                            if let Some((editor, callback)) = editor                                .upgrade()                                .zip(zed_actions::outline::TOGGLE_OUTLINE.get())                            {                                callback(editor.to_any(), window, cx);                            }                        }                    })                    .tooltip(move |window, cx| {                        if let Some(editor) = editor.upgrade() {                            let focus_handle = editor.read(cx).focus_handle(cx);                            Tooltip::for_action_in(                                "Show Symbol Outline",                                &zed_actions::outline::ToggleOutline,                                &focus_handle,                                window,                                cx,                            )                        } else {                            Tooltip::for_action(                                "Show Symbol Outline",                                &zed_actions::outline::ToggleOutline,                                window,                                cx,                            )                        }                    }),            ),            None => element                // Match the height of the `ButtonLike` in the other arm.                .h(rems_from_px(22.))                .child(breadcrumbs_stack),        }    }} impl ToolbarItemView for Breadcrumbs {    fn set_active_pane_item(        &mut self,        active_pane_item: Option<&dyn ItemHandle>,        window: &mut Window,        cx: &mut Context<Self>,    ) -> ToolbarItemLocation {        cx.notify();        self.active_item = None;         let Some(item) = active_pane_item else {            return ToolbarItemLocation::Hidden;        };         let this = cx.entity().downgrade();        self.subscription = Some(item.subscribe_to_item_events(            window,            cx,            Box::new(move |event, _, cx| {                if let ItemEvent::UpdateBreadcrumbs = event {                    this.update(cx, |this, cx| {                        cx.notify();                        if let Some(active_item) = this.active_item.as_ref() {                            cx.emit(ToolbarItemEvent::ChangeLocation(                                active_item.breadcrumb_location(cx),                            ))                        }                    })                    .ok();                }            }),        ));        self.active_item = Some(item.boxed_clone());        item.breadcrumb_location(cx)    }     fn pane_focus_update(        &mut self,        pane_focused: bool,        _window: &mut Window,        _: &mut Context<Self>,    ) {        self.pane_focused = pane_focused;    }}