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