1use crate::{ItemHandle, Pane};
2use gpui::{
3 div, AnyView, Div, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
4 WindowContext,
5};
6use std::any::TypeId;
7use ui::{h_stack, prelude::*};
8use util::ResultExt;
9
10pub trait StatusItemView: Render {
11 fn set_active_pane_item(
12 &mut self,
13 active_pane_item: Option<&dyn crate::ItemHandle>,
14 cx: &mut ViewContext<Self>,
15 );
16}
17
18trait StatusItemViewHandle: Send {
19 fn to_any(&self) -> AnyView;
20 fn set_active_pane_item(
21 &self,
22 active_pane_item: Option<&dyn ItemHandle>,
23 cx: &mut WindowContext,
24 );
25 fn item_type(&self) -> TypeId;
26}
27
28pub struct StatusBar {
29 left_items: Vec<Box<dyn StatusItemViewHandle>>,
30 right_items: Vec<Box<dyn StatusItemViewHandle>>,
31 active_pane: View<Pane>,
32 _observe_active_pane: Subscription,
33}
34
35impl Render for StatusBar {
36 type Element = Div;
37
38 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
39 div()
40 .py_0p5()
41 .px_1()
42 .flex()
43 .items_center()
44 .justify_between()
45 .w_full()
46 .h_8()
47 .bg(cx.theme().colors().status_bar_background)
48 .child(self.render_left_tools(cx))
49 .child(self.render_right_tools(cx))
50 }
51}
52
53impl StatusBar {
54 fn render_left_tools(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
55 h_stack()
56 .items_center()
57 .gap_2()
58 .children(self.left_items.iter().map(|item| item.to_any()))
59 }
60
61 fn render_right_tools(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
62 h_stack()
63 .items_center()
64 .gap_2()
65 .children(self.right_items.iter().rev().map(|item| item.to_any()))
66 }
67}
68
69impl StatusBar {
70 pub fn new(active_pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Self {
71 let mut this = Self {
72 left_items: Default::default(),
73 right_items: Default::default(),
74 active_pane: active_pane.clone(),
75 _observe_active_pane: cx
76 .observe(active_pane, |this, _, cx| this.update_active_pane_item(cx)),
77 };
78 this.update_active_pane_item(cx);
79 this
80 }
81
82 pub fn add_left_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
83 where
84 T: 'static + StatusItemView,
85 {
86 self.left_items.push(Box::new(item));
87 cx.notify();
88 }
89
90 pub fn item_of_type<T: StatusItemView>(&self) -> Option<View<T>> {
91 self.left_items
92 .iter()
93 .chain(self.right_items.iter())
94 .find_map(|item| item.to_any().clone().downcast().log_err())
95 }
96
97 pub fn position_of_item<T>(&self) -> Option<usize>
98 where
99 T: StatusItemView,
100 {
101 for (index, item) in self.left_items.iter().enumerate() {
102 if item.item_type() == TypeId::of::<T>() {
103 return Some(index);
104 }
105 }
106 for (index, item) in self.right_items.iter().enumerate() {
107 if item.item_type() == TypeId::of::<T>() {
108 return Some(index + self.left_items.len());
109 }
110 }
111 return None;
112 }
113
114 pub fn insert_item_after<T>(
115 &mut self,
116 position: usize,
117 item: View<T>,
118 cx: &mut ViewContext<Self>,
119 ) where
120 T: 'static + StatusItemView,
121 {
122 if position < self.left_items.len() {
123 self.left_items.insert(position + 1, Box::new(item))
124 } else {
125 self.right_items
126 .insert(position + 1 - self.left_items.len(), Box::new(item))
127 }
128 cx.notify()
129 }
130
131 pub fn remove_item_at(&mut self, position: usize, cx: &mut ViewContext<Self>) {
132 if position < self.left_items.len() {
133 self.left_items.remove(position);
134 } else {
135 self.right_items.remove(position - self.left_items.len());
136 }
137 cx.notify();
138 }
139
140 pub fn add_right_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
141 where
142 T: 'static + StatusItemView,
143 {
144 self.right_items.push(Box::new(item));
145 cx.notify();
146 }
147
148 pub fn set_active_pane(&mut self, active_pane: &View<Pane>, cx: &mut ViewContext<Self>) {
149 self.active_pane = active_pane.clone();
150 self._observe_active_pane =
151 cx.observe(active_pane, |this, _, cx| this.update_active_pane_item(cx));
152 self.update_active_pane_item(cx);
153 }
154
155 fn update_active_pane_item(&mut self, cx: &mut ViewContext<Self>) {
156 let active_pane_item = self.active_pane.read(cx).active_item();
157 for item in self.left_items.iter().chain(&self.right_items) {
158 item.set_active_pane_item(active_pane_item.as_deref(), cx);
159 }
160 }
161}
162
163impl<T: StatusItemView> StatusItemViewHandle for View<T> {
164 fn to_any(&self) -> AnyView {
165 self.clone().into()
166 }
167
168 fn set_active_pane_item(
169 &self,
170 active_pane_item: Option<&dyn ItemHandle>,
171 cx: &mut WindowContext,
172 ) {
173 self.update(cx, |this, cx| {
174 this.set_active_pane_item(active_pane_item, cx)
175 });
176 }
177
178 fn item_type(&self) -> TypeId {
179 TypeId::of::<T>()
180 }
181}
182
183impl From<&dyn StatusItemViewHandle> for AnyView {
184 fn from(val: &dyn StatusItemViewHandle) -> Self {
185 val.to_any().clone()
186 }
187}