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}