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