1use crate::{ItemHandle, Pane};
2use gpui::{
3 div, AnyView, 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 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
37 div()
38 .py_0p5()
39 .px_1()
40 .flex()
41 .items_center()
42 .justify_between()
43 .w_full()
44 .h_8()
45 .bg(cx.theme().colors().status_bar_background)
46 .child(self.render_left_tools(cx))
47 .child(self.render_right_tools(cx))
48 }
49}
50
51impl StatusBar {
52 fn render_left_tools(&self, _: &mut ViewContext<Self>) -> impl IntoElement {
53 h_stack()
54 .items_center()
55 .gap_2()
56 .children(self.left_items.iter().map(|item| item.to_any()))
57 }
58
59 fn render_right_tools(&self, _: &mut ViewContext<Self>) -> impl IntoElement {
60 h_stack()
61 .items_center()
62 .gap_2()
63 .children(self.right_items.iter().rev().map(|item| item.to_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 let active_pane_item = self.active_pane.read(cx).active_item();
85 item.set_active_pane_item(active_pane_item.as_deref(), cx);
86
87 self.left_items.push(Box::new(item));
88 cx.notify();
89 }
90
91 pub fn item_of_type<T: StatusItemView>(&self) -> Option<View<T>> {
92 self.left_items
93 .iter()
94 .chain(self.right_items.iter())
95 .find_map(|item| item.to_any().clone().downcast().log_err())
96 }
97
98 pub fn position_of_item<T>(&self) -> Option<usize>
99 where
100 T: StatusItemView,
101 {
102 for (index, item) in self.left_items.iter().enumerate() {
103 if item.item_type() == TypeId::of::<T>() {
104 return Some(index);
105 }
106 }
107 for (index, item) in self.right_items.iter().enumerate() {
108 if item.item_type() == TypeId::of::<T>() {
109 return Some(index + self.left_items.len());
110 }
111 }
112 return None;
113 }
114
115 pub fn insert_item_after<T>(
116 &mut self,
117 position: usize,
118 item: View<T>,
119 cx: &mut ViewContext<Self>,
120 ) where
121 T: 'static + StatusItemView,
122 {
123 let active_pane_item = self.active_pane.read(cx).active_item();
124 item.set_active_pane_item(active_pane_item.as_deref(), cx);
125
126 if position < self.left_items.len() {
127 self.left_items.insert(position + 1, Box::new(item))
128 } else {
129 self.right_items
130 .insert(position + 1 - self.left_items.len(), Box::new(item))
131 }
132 cx.notify()
133 }
134
135 pub fn remove_item_at(&mut self, position: usize, cx: &mut ViewContext<Self>) {
136 if position < self.left_items.len() {
137 self.left_items.remove(position);
138 } else {
139 self.right_items.remove(position - self.left_items.len());
140 }
141 cx.notify();
142 }
143
144 pub fn add_right_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
145 where
146 T: 'static + StatusItemView,
147 {
148 let active_pane_item = self.active_pane.read(cx).active_item();
149 item.set_active_pane_item(active_pane_item.as_deref(), cx);
150
151 self.right_items.push(Box::new(item));
152 cx.notify();
153 }
154
155 pub fn set_active_pane(&mut self, active_pane: &View<Pane>, cx: &mut ViewContext<Self>) {
156 self.active_pane = active_pane.clone();
157 self._observe_active_pane =
158 cx.observe(active_pane, |this, _, cx| this.update_active_pane_item(cx));
159 self.update_active_pane_item(cx);
160 }
161
162 fn update_active_pane_item(&mut self, cx: &mut ViewContext<Self>) {
163 let active_pane_item = self.active_pane.read(cx).active_item();
164 for item in self.left_items.iter().chain(&self.right_items) {
165 item.set_active_pane_item(active_pane_item.as_deref(), cx);
166 }
167 }
168}
169
170impl<T: StatusItemView> StatusItemViewHandle for View<T> {
171 fn to_any(&self) -> AnyView {
172 self.clone().into()
173 }
174
175 fn set_active_pane_item(
176 &self,
177 active_pane_item: Option<&dyn ItemHandle>,
178 cx: &mut WindowContext,
179 ) {
180 self.update(cx, |this, cx| {
181 this.set_active_pane_item(active_pane_item, cx)
182 });
183 }
184
185 fn item_type(&self) -> TypeId {
186 TypeId::of::<T>()
187 }
188}
189
190impl From<&dyn StatusItemViewHandle> for AnyView {
191 fn from(val: &dyn StatusItemViewHandle) -> Self {
192 val.to_any().clone()
193 }
194}