1use gpui::{
2 AnyElement, App, Context, EventEmitter, Font, Global, IntoElement, Render, Subscription, Window,
3};
4use ui::prelude::*;
5use workspace::{
6 ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
7 item::{HighlightedText, ItemEvent, ItemHandle},
8};
9
10type RenderBreadcrumbTextFn = fn(
11 Vec<HighlightedText>,
12 Option<Font>,
13 Option<AnyElement>,
14 &dyn ItemHandle,
15 bool,
16 &mut Window,
17 &App,
18) -> AnyElement;
19
20pub struct RenderBreadcrumbText(pub RenderBreadcrumbTextFn);
21
22impl Global for RenderBreadcrumbText {}
23
24pub struct Breadcrumbs {
25 pane_focused: bool,
26 active_item: Option<Box<dyn ItemHandle>>,
27 subscription: Option<Subscription>,
28}
29
30impl Default for Breadcrumbs {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl Breadcrumbs {
37 pub fn new() -> Self {
38 Self {
39 pane_focused: false,
40 active_item: Default::default(),
41 subscription: Default::default(),
42 }
43 }
44}
45
46impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
47
48impl Render for Breadcrumbs {
49 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
50 let element = h_flex()
51 .id("breadcrumb-container")
52 .flex_grow()
53 .h_8()
54 .overflow_x_scroll()
55 .text_ui(cx);
56
57 let Some(active_item) = self.active_item.as_ref() else {
58 return element.into_any_element();
59 };
60
61 let Some((segments, breadcrumb_font)) = active_item.breadcrumbs(cx) else {
62 return element.into_any_element();
63 };
64
65 let prefix_element = active_item.breadcrumb_prefix(window, cx);
66
67 if let Some(render_fn) = cx.try_global::<RenderBreadcrumbText>() {
68 (render_fn.0)(
69 segments,
70 breadcrumb_font,
71 prefix_element,
72 active_item.as_ref(),
73 false,
74 window,
75 cx,
76 )
77 } else {
78 element.into_any_element()
79 }
80 }
81}
82
83impl ToolbarItemView for Breadcrumbs {
84 fn set_active_pane_item(
85 &mut self,
86 active_pane_item: Option<&dyn ItemHandle>,
87 window: &mut Window,
88 cx: &mut Context<Self>,
89 ) -> ToolbarItemLocation {
90 cx.notify();
91 self.active_item = None;
92
93 let Some(item) = active_pane_item else {
94 return ToolbarItemLocation::Hidden;
95 };
96
97 let this = cx.entity().downgrade();
98 self.subscription = Some(item.subscribe_to_item_events(
99 window,
100 cx,
101 Box::new(move |event, _, cx| {
102 if let ItemEvent::UpdateBreadcrumbs = event {
103 this.update(cx, |this, cx| {
104 cx.notify();
105 if let Some(active_item) = this.active_item.as_ref() {
106 cx.emit(ToolbarItemEvent::ChangeLocation(
107 active_item.breadcrumb_location(cx),
108 ))
109 }
110 })
111 .ok();
112 }
113 }),
114 ));
115 self.active_item = Some(item.boxed_clone());
116 item.breadcrumb_location(cx)
117 }
118
119 fn pane_focus_update(
120 &mut self,
121 pane_focused: bool,
122 _window: &mut Window,
123 _: &mut Context<Self>,
124 ) {
125 self.pane_focused = pane_focused;
126 }
127}