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