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}