status_bar.rs

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