status_bar.rs

  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// }