1use gpui::{
2 elements::*, platform::MouseButton, AppContext, Entity, Subscription, View, ViewContext,
3 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 ViewContext<Self>) -> AnyElement<Self> {
45 let active_item = match &self.active_item {
46 Some(active_item) => active_item,
47 None => return Empty::new().into_any(),
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().into_any(),
57 }
58 .into_iter()
59 .map(|breadcrumb| {
60 Text::new(
61 breadcrumb.text,
62 theme.workspace.breadcrumbs.default.text.clone(),
63 )
64 .with_highlights(breadcrumb.highlights.unwrap_or_default())
65 .into_any()
66 });
67
68 let crumbs = Flex::row()
69 .with_children(Itertools::intersperse_with(breadcrumbs, || {
70 Label::new(" 〉 ", style.default.text.clone()).into_any()
71 }))
72 .constrained()
73 .with_height(theme.workspace.breadcrumb_height)
74 .contained();
75
76 if not_editor || !self.pane_focused {
77 return crumbs
78 .with_style(style.default.container)
79 .aligned()
80 .left()
81 .into_any();
82 }
83
84 MouseEventHandler::<Breadcrumbs, Breadcrumbs>::new(0, cx, |state, _| {
85 let style = style.style_for(state, false);
86 crumbs.with_style(style.container)
87 })
88 .on_click(MouseButton::Left, |_, _, cx| {
89 cx.dispatch_action(outline::Toggle);
90 })
91 .with_tooltip::<Breadcrumbs>(
92 0,
93 "Show symbol outline".to_owned(),
94 Some(Box::new(outline::Toggle)),
95 theme.tooltip.clone(),
96 cx,
97 )
98 .aligned()
99 .left()
100 .into_any()
101 }
102}
103
104impl ToolbarItemView for Breadcrumbs {
105 fn set_active_pane_item(
106 &mut self,
107 active_pane_item: Option<&dyn ItemHandle>,
108 cx: &mut ViewContext<Self>,
109 ) -> ToolbarItemLocation {
110 cx.notify();
111 self.active_item = None;
112 self.project_search = None;
113 if let Some(item) = active_pane_item {
114 let this = cx.weak_handle();
115 self.subscription = Some(item.subscribe_to_item_events(
116 cx,
117 Box::new(move |event, cx| {
118 if let Some(this) = this.upgrade(cx) {
119 if let ItemEvent::UpdateBreadcrumbs = event {
120 this.update(cx, |_, cx| {
121 cx.emit(Event::UpdateLocation);
122 cx.notify();
123 });
124 }
125 }
126 }),
127 ));
128 self.active_item = Some(item.boxed_clone());
129 item.breadcrumb_location(cx)
130 } else {
131 ToolbarItemLocation::Hidden
132 }
133 }
134
135 fn location_for_event(
136 &self,
137 _: &Event,
138 current_location: ToolbarItemLocation,
139 cx: &AppContext,
140 ) -> ToolbarItemLocation {
141 if let Some(active_item) = self.active_item.as_ref() {
142 active_item.breadcrumb_location(cx)
143 } else {
144 current_location
145 }
146 }
147
148 fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
149 self.pane_focused = pane_focused;
150 }
151}