1use std::any::TypeId;
2
3use crate::{ItemHandle, Pane};
4use gpui2::{AnyView, Render, Subscription, View, ViewContext, WindowContext};
5use util::ResultExt;
6
7pub trait StatusItemView: Render {
8 fn set_active_pane_item(
9 &mut self,
10 active_pane_item: Option<&dyn crate::ItemHandle>,
11 cx: &mut ViewContext<Self>,
12 );
13}
14
15trait StatusItemViewHandle: Send {
16 fn to_any(&self) -> AnyView;
17 fn set_active_pane_item(
18 &self,
19 active_pane_item: Option<&dyn ItemHandle>,
20 cx: &mut WindowContext,
21 );
22 fn item_type(&self) -> TypeId;
23}
24
25pub struct StatusBar {
26 left_items: Vec<Box<dyn StatusItemViewHandle>>,
27 right_items: Vec<Box<dyn StatusItemViewHandle>>,
28 active_pane: View<Pane>,
29 _observe_active_pane: Subscription,
30}
31
32// todo!()
33// impl View for StatusBar {
34// fn ui_name() -> &'static str {
35// "StatusBar"
36// }
37
38// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
39// let theme = &theme::current(cx).workspace.status_bar;
40
41// StatusBarElement {
42// left: Flex::row()
43// .with_children(self.left_items.iter().map(|i| {
44// ChildView::new(i.as_any(), cx)
45// .aligned()
46// .contained()
47// .with_margin_right(theme.item_spacing)
48// }))
49// .into_any(),
50// right: Flex::row()
51// .with_children(self.right_items.iter().rev().map(|i| {
52// ChildView::new(i.as_any(), cx)
53// .aligned()
54// .contained()
55// .with_margin_left(theme.item_spacing)
56// }))
57// .into_any(),
58// }
59// .contained()
60// .with_style(theme.container)
61// .constrained()
62// .with_height(theme.height)
63// .into_any()
64// }
65// }
66
67impl StatusBar {
68 pub fn new(active_pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Self {
69 let mut this = Self {
70 left_items: Default::default(),
71 right_items: Default::default(),
72 active_pane: active_pane.clone(),
73 _observe_active_pane: cx
74 .observe(active_pane, |this, _, cx| this.update_active_pane_item(cx)),
75 };
76 this.update_active_pane_item(cx);
77 this
78 }
79
80 pub fn add_left_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
81 where
82 T: 'static + StatusItemView,
83 {
84 self.left_items.push(Box::new(item));
85 cx.notify();
86 }
87
88 pub fn item_of_type<T: StatusItemView>(&self) -> Option<View<T>> {
89 self.left_items
90 .iter()
91 .chain(self.right_items.iter())
92 .find_map(|item| item.to_any().clone().downcast().log_err())
93 }
94
95 pub fn position_of_item<T>(&self) -> Option<usize>
96 where
97 T: StatusItemView,
98 {
99 for (index, item) in self.left_items.iter().enumerate() {
100 if item.item_type() == TypeId::of::<T>() {
101 return Some(index);
102 }
103 }
104 for (index, item) in self.right_items.iter().enumerate() {
105 if item.item_type() == TypeId::of::<T>() {
106 return Some(index + self.left_items.len());
107 }
108 }
109 return None;
110 }
111
112 pub fn insert_item_after<T>(
113 &mut self,
114 position: usize,
115 item: View<T>,
116 cx: &mut ViewContext<Self>,
117 ) where
118 T: 'static + StatusItemView,
119 {
120 if position < self.left_items.len() {
121 self.left_items.insert(position + 1, Box::new(item))
122 } else {
123 self.right_items
124 .insert(position + 1 - self.left_items.len(), Box::new(item))
125 }
126 cx.notify()
127 }
128
129 pub fn remove_item_at(&mut self, position: usize, cx: &mut ViewContext<Self>) {
130 if position < self.left_items.len() {
131 self.left_items.remove(position);
132 } else {
133 self.right_items.remove(position - self.left_items.len());
134 }
135 cx.notify();
136 }
137
138 pub fn add_right_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
139 where
140 T: 'static + StatusItemView,
141 {
142 self.right_items.push(Box::new(item));
143 cx.notify();
144 }
145
146 pub fn set_active_pane(&mut self, active_pane: &View<Pane>, cx: &mut ViewContext<Self>) {
147 self.active_pane = active_pane.clone();
148 self._observe_active_pane =
149 cx.observe(active_pane, |this, _, cx| this.update_active_pane_item(cx));
150 self.update_active_pane_item(cx);
151 }
152
153 fn update_active_pane_item(&mut self, cx: &mut ViewContext<Self>) {
154 let active_pane_item = self.active_pane.read(cx).active_item();
155 for item in self.left_items.iter().chain(&self.right_items) {
156 item.set_active_pane_item(active_pane_item.as_deref(), cx);
157 }
158 }
159}
160
161impl<T: StatusItemView> StatusItemViewHandle for View<T> {
162 fn to_any(&self) -> AnyView {
163 self.clone().into_any()
164 }
165
166 fn set_active_pane_item(
167 &self,
168 active_pane_item: Option<&dyn ItemHandle>,
169 cx: &mut WindowContext,
170 ) {
171 self.update(cx, |this, cx| {
172 this.set_active_pane_item(active_pane_item, cx)
173 });
174 }
175
176 fn item_type(&self) -> TypeId {
177 TypeId::of::<T>()
178 }
179}
180
181impl From<&dyn StatusItemViewHandle> for AnyView {
182 fn from(val: &dyn StatusItemViewHandle) -> Self {
183 val.to_any().clone()
184 }
185}
186
187// todo!()
188// struct StatusBarElement {
189// left: AnyElement<StatusBar>,
190// right: AnyElement<StatusBar>,
191// }
192
193// todo!()
194// impl Element<StatusBar> for StatusBarElement {
195// type LayoutState = ();
196// type PaintState = ();
197
198// fn layout(
199// &mut self,
200// mut constraint: SizeConstraint,
201// view: &mut StatusBar,
202// cx: &mut ViewContext<StatusBar>,
203// ) -> (Vector2F, Self::LayoutState) {
204// let max_width = constraint.max.x();
205// constraint.min = vec2f(0., constraint.min.y());
206
207// let right_size = self.right.layout(constraint, view, cx);
208// let constraint = SizeConstraint::new(
209// vec2f(0., constraint.min.y()),
210// vec2f(max_width - right_size.x(), constraint.max.y()),
211// );
212
213// self.left.layout(constraint, view, cx);
214
215// (vec2f(max_width, right_size.y()), ())
216// }
217
218// fn paint(
219// &mut self,
220// bounds: RectF,
221// visible_bounds: RectF,
222// _: &mut Self::LayoutState,
223// view: &mut StatusBar,
224// cx: &mut ViewContext<StatusBar>,
225// ) -> Self::PaintState {
226// let origin_y = bounds.upper_right().y();
227// let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
228
229// let left_origin = vec2f(bounds.lower_left().x(), origin_y);
230// self.left.paint(left_origin, visible_bounds, view, cx);
231
232// let right_origin = vec2f(bounds.upper_right().x() - self.right.size().x(), origin_y);
233// self.right.paint(right_origin, visible_bounds, view, cx);
234// }
235
236// fn rect_for_text_range(
237// &self,
238// _: Range<usize>,
239// _: RectF,
240// _: RectF,
241// _: &Self::LayoutState,
242// _: &Self::PaintState,
243// _: &StatusBar,
244// _: &ViewContext<StatusBar>,
245// ) -> Option<RectF> {
246// None
247// }
248
249// fn debug(
250// &self,
251// bounds: RectF,
252// _: &Self::LayoutState,
253// _: &Self::PaintState,
254// _: &StatusBar,
255// _: &ViewContext<StatusBar>,
256// ) -> serde_json::Value {
257// json!({
258// "type": "StatusBarElement",
259// "bounds": bounds.to_json()
260// })
261// }
262// }