status_bar.rs

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