status_bar.rs

  1use std::any::TypeId;
  2
  3use crate::{ItemHandle, Pane};
  4use gpui::{
  5    div, AnyView, Div, ParentElement, Render, RenderOnce, 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;
 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 RenderOnce {
 57        h_stack()
 58            .items_center()
 59            .gap_2()
 60            .children(self.left_items.iter().map(|item| item.to_any()))
 61    }
 62
 63    fn render_right_tools(&self, cx: &mut ViewContext<Self>) -> impl RenderOnce {
 64        h_stack()
 65            .items_center()
 66            .gap_2()
 67            .children(self.right_items.iter().map(|item| item.to_any()))
 68    }
 69}
 70
 71impl StatusBar {
 72    pub fn new(active_pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Self {
 73        let mut this = Self {
 74            left_items: Default::default(),
 75            right_items: Default::default(),
 76            active_pane: active_pane.clone(),
 77            _observe_active_pane: cx
 78                .observe(active_pane, |this, _, cx| this.update_active_pane_item(cx)),
 79        };
 80        this.update_active_pane_item(cx);
 81        this
 82    }
 83
 84    pub fn add_left_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
 85    where
 86        T: 'static + StatusItemView,
 87    {
 88        self.left_items.push(Box::new(item));
 89        cx.notify();
 90    }
 91
 92    pub fn item_of_type<T: StatusItemView>(&self) -> Option<View<T>> {
 93        self.left_items
 94            .iter()
 95            .chain(self.right_items.iter())
 96            .find_map(|item| item.to_any().clone().downcast().log_err())
 97    }
 98
 99    pub fn position_of_item<T>(&self) -> Option<usize>
100    where
101        T: StatusItemView,
102    {
103        for (index, item) in self.left_items.iter().enumerate() {
104            if item.item_type() == TypeId::of::<T>() {
105                return Some(index);
106            }
107        }
108        for (index, item) in self.right_items.iter().enumerate() {
109            if item.item_type() == TypeId::of::<T>() {
110                return Some(index + self.left_items.len());
111            }
112        }
113        return None;
114    }
115
116    pub fn insert_item_after<T>(
117        &mut self,
118        position: usize,
119        item: View<T>,
120        cx: &mut ViewContext<Self>,
121    ) where
122        T: 'static + StatusItemView,
123    {
124        if position < self.left_items.len() {
125            self.left_items.insert(position + 1, Box::new(item))
126        } else {
127            self.right_items
128                .insert(position + 1 - self.left_items.len(), Box::new(item))
129        }
130        cx.notify()
131    }
132
133    pub fn remove_item_at(&mut self, position: usize, cx: &mut ViewContext<Self>) {
134        if position < self.left_items.len() {
135            self.left_items.remove(position);
136        } else {
137            self.right_items.remove(position - self.left_items.len());
138        }
139        cx.notify();
140    }
141
142    pub fn add_right_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
143    where
144        T: 'static + StatusItemView,
145    {
146        self.right_items.push(Box::new(item));
147        cx.notify();
148    }
149
150    pub fn set_active_pane(&mut self, active_pane: &View<Pane>, cx: &mut ViewContext<Self>) {
151        self.active_pane = active_pane.clone();
152        self._observe_active_pane =
153            cx.observe(active_pane, |this, _, cx| this.update_active_pane_item(cx));
154        self.update_active_pane_item(cx);
155    }
156
157    fn update_active_pane_item(&mut self, cx: &mut ViewContext<Self>) {
158        let active_pane_item = self.active_pane.read(cx).active_item();
159        for item in self.left_items.iter().chain(&self.right_items) {
160            item.set_active_pane_item(active_pane_item.as_deref(), cx);
161        }
162    }
163}
164
165impl<T: StatusItemView> StatusItemViewHandle for View<T> {
166    fn to_any(&self) -> AnyView {
167        self.clone().into()
168    }
169
170    fn set_active_pane_item(
171        &self,
172        active_pane_item: Option<&dyn ItemHandle>,
173        cx: &mut WindowContext,
174    ) {
175        self.update(cx, |this, cx| {
176            this.set_active_pane_item(active_pane_item, cx)
177        });
178    }
179
180    fn item_type(&self) -> TypeId {
181        TypeId::of::<T>()
182    }
183}
184
185impl From<&dyn StatusItemViewHandle> for AnyView {
186    fn from(val: &dyn StatusItemViewHandle) -> Self {
187        val.to_any().clone()
188    }
189}