1use editor::Editor;
2use gpui::{
3 Div, Element, EventEmitter, IntoElement, ParentElement, Render, StyledText, Subscription,
4 ViewContext,
5};
6use itertools::Itertools;
7use theme::ActiveTheme;
8use ui::{prelude::*, ButtonLike, ButtonStyle, Label, Tooltip};
9use workspace::{
10 item::{ItemEvent, ItemHandle},
11 ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
12};
13
14pub enum Event {
15 UpdateLocation,
16}
17
18pub struct Breadcrumbs {
19 pane_focused: bool,
20 active_item: Option<Box<dyn ItemHandle>>,
21 subscription: Option<Subscription>,
22}
23
24impl Breadcrumbs {
25 pub fn new() -> Self {
26 Self {
27 pane_focused: false,
28 active_item: Default::default(),
29 subscription: Default::default(),
30 }
31 }
32}
33
34impl EventEmitter<Event> for Breadcrumbs {}
35impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
36
37impl Render for Breadcrumbs {
38 type Element = Div;
39
40 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
41 let element = h_stack().text_ui();
42
43 let Some(active_item) = &self
44 .active_item
45 .as_ref()
46 .filter(|item| item.downcast::<editor::Editor>().is_some())
47 else {
48 return element;
49 };
50
51 let Some(segments) = active_item.breadcrumbs(cx.theme(), cx) else {
52 return element;
53 };
54
55 let highlighted_segments = segments.into_iter().map(|segment| {
56 StyledText::new(segment.text)
57 .with_highlights(&cx.text_style(), segment.highlights.unwrap_or_default())
58 .into_any()
59 });
60 let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {
61 Label::new("›").into_any_element()
62 });
63
64 let editor = active_item
65 .downcast::<Editor>()
66 .map(|editor| editor.downgrade());
67
68 element.child(
69 ButtonLike::new("toggle outline view")
70 .style(ButtonStyle::Subtle)
71 .child(h_stack().gap_1().children(breadcrumbs))
72 .on_click(move |_, cx| {
73 if let Some(editor) = editor.as_ref().and_then(|editor| editor.upgrade()) {
74 outline::toggle(editor, &outline::Toggle, cx)
75 }
76 })
77 .tooltip(|cx| Tooltip::for_action("Show symbol outline", &outline::Toggle, cx)),
78 )
79 }
80}
81
82impl ToolbarItemView for Breadcrumbs {
83 fn set_active_pane_item(
84 &mut self,
85 active_pane_item: Option<&dyn ItemHandle>,
86 cx: &mut ViewContext<Self>,
87 ) -> ToolbarItemLocation {
88 cx.notify();
89 self.active_item = None;
90 if let Some(item) = active_pane_item {
91 let this = cx.view().downgrade();
92 self.subscription = Some(item.subscribe_to_item_events(
93 cx,
94 Box::new(move |event, cx| {
95 if let ItemEvent::UpdateBreadcrumbs = event {
96 this.update(cx, |_, cx| {
97 cx.emit(Event::UpdateLocation);
98 cx.notify();
99 })
100 .ok();
101 }
102 }),
103 ));
104 self.active_item = Some(item.boxed_clone());
105 item.breadcrumb_location(cx)
106 } else {
107 ToolbarItemLocation::Hidden
108 }
109 }
110
111 // fn location_for_event(
112 // &self,
113 // _: &Event,
114 // current_location: ToolbarItemLocation,
115 // cx: &AppContext,
116 // ) -> ToolbarItemLocation {
117 // if let Some(active_item) = self.active_item.as_ref() {
118 // active_item.breadcrumb_location(cx)
119 // } else {
120 // current_location
121 // }
122 // }
123
124 fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
125 self.pane_focused = pane_focused;
126 }
127}