1use gpui::{
2 elements::*, platform::MouseButton, AppContext, Entity, RenderContext, Subscription, View,
3 ViewContext, ViewHandle,
4};
5use itertools::Itertools;
6use search::ProjectSearchView;
7use settings::Settings;
8use workspace::{
9 item::{ItemEvent, ItemHandle},
10 ToolbarItemLocation, ToolbarItemView,
11};
12
13pub enum Event {
14 UpdateLocation,
15}
16
17pub struct Breadcrumbs {
18 pane_focused: bool,
19 active_item: Option<Box<dyn ItemHandle>>,
20 project_search: Option<ViewHandle<ProjectSearchView>>,
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 project_search: Default::default(),
31 }
32 }
33}
34
35impl Entity for Breadcrumbs {
36 type Event = Event;
37}
38
39impl View for Breadcrumbs {
40 fn ui_name() -> &'static str {
41 "Breadcrumbs"
42 }
43
44 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
45 let active_item = match &self.active_item {
46 Some(active_item) => active_item,
47 None => return Empty::new().boxed(),
48 };
49 let not_editor = active_item.downcast::<editor::Editor>().is_none();
50
51 let theme = cx.global::<Settings>().theme.clone();
52 let style = &theme.workspace.breadcrumbs;
53
54 let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
55 Some(breadcrumbs) => breadcrumbs,
56 None => return Empty::new().boxed(),
57 };
58
59 let crumbs = Flex::row()
60 .with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || {
61 Label::new(" 〉 ", style.default.text.clone()).boxed()
62 }))
63 .constrained()
64 .with_height(theme.workspace.breadcrumb_height)
65 .contained();
66
67 if not_editor || !self.pane_focused {
68 return crumbs
69 .with_style(style.default.container)
70 .aligned()
71 .left()
72 .boxed();
73 }
74
75 MouseEventHandler::<Breadcrumbs>::new(0, cx, |state, _| {
76 let style = style.style_for(state, false);
77 crumbs.with_style(style.container).boxed()
78 })
79 .on_click(MouseButton::Left, |_, cx| {
80 cx.dispatch_action(outline::Toggle);
81 })
82 .with_tooltip::<Breadcrumbs, _>(
83 0,
84 "Show symbol outline".to_owned(),
85 Some(Box::new(outline::Toggle)),
86 theme.tooltip.clone(),
87 cx,
88 )
89 .aligned()
90 .left()
91 .boxed()
92 }
93}
94
95impl ToolbarItemView for Breadcrumbs {
96 fn set_active_pane_item(
97 &mut self,
98 active_pane_item: Option<&dyn ItemHandle>,
99 cx: &mut ViewContext<Self>,
100 ) -> ToolbarItemLocation {
101 cx.notify();
102 self.active_item = None;
103 self.project_search = None;
104 if let Some(item) = active_pane_item {
105 let this = cx.weak_handle();
106 self.subscription = Some(item.subscribe_to_item_events(
107 cx,
108 Box::new(move |event, cx| {
109 if let Some(this) = this.upgrade(cx) {
110 if let ItemEvent::UpdateBreadcrumbs = event {
111 this.update(cx, |_, cx| {
112 cx.emit(Event::UpdateLocation);
113 cx.notify();
114 });
115 }
116 }
117 }),
118 ));
119 self.active_item = Some(item.boxed_clone());
120 item.breadcrumb_location(cx)
121 } else {
122 ToolbarItemLocation::Hidden
123 }
124 }
125
126 fn location_for_event(
127 &self,
128 _: &Event,
129 current_location: ToolbarItemLocation,
130 cx: &AppContext,
131 ) -> ToolbarItemLocation {
132 if let Some(active_item) = self.active_item.as_ref() {
133 active_item.breadcrumb_location(cx)
134 } else {
135 current_location
136 }
137 }
138
139 fn pane_focus_update(&mut self, pane_focused: bool, _: &mut gpui::AppContext) {
140 self.pane_focused = pane_focused;
141 }
142}