1use std::marker::PhantomData;
2use std::sync::atomic::Ordering;
3use std::sync::Arc;
4
5use crate::prelude::*;
6use crate::{get_workspace_state, Button, Icon, IconButton, IconColor, ToolDivider};
7
8#[derive(Default, PartialEq)]
9pub enum Tool {
10 #[default]
11 ProjectPanel,
12 CollaborationPanel,
13 Terminal,
14 Assistant,
15 Feedback,
16 Diagnostics,
17}
18
19struct ToolGroup {
20 active_index: Option<usize>,
21 tools: Vec<Tool>,
22}
23
24impl Default for ToolGroup {
25 fn default() -> Self {
26 ToolGroup {
27 active_index: None,
28 tools: vec![],
29 }
30 }
31}
32
33#[derive(Element)]
34pub struct StatusBar<S: 'static + Send + Sync + Clone> {
35 state_type: PhantomData<S>,
36 left_tools: Option<ToolGroup>,
37 right_tools: Option<ToolGroup>,
38 bottom_tools: Option<ToolGroup>,
39}
40
41impl<S: 'static + Send + Sync + Clone> StatusBar<S> {
42 pub fn new() -> Self {
43 Self {
44 state_type: PhantomData,
45 left_tools: None,
46 right_tools: None,
47 bottom_tools: None,
48 }
49 }
50
51 pub fn left_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
52 self.left_tools = {
53 let mut tools = vec![tool];
54 tools.extend(self.left_tools.take().unwrap_or_default().tools);
55 Some(ToolGroup {
56 active_index,
57 tools,
58 })
59 };
60 self
61 }
62
63 pub fn right_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
64 self.right_tools = {
65 let mut tools = vec![tool];
66 tools.extend(self.left_tools.take().unwrap_or_default().tools);
67 Some(ToolGroup {
68 active_index,
69 tools,
70 })
71 };
72 self
73 }
74
75 pub fn bottom_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
76 self.bottom_tools = {
77 let mut tools = vec![tool];
78 tools.extend(self.left_tools.take().unwrap_or_default().tools);
79 Some(ToolGroup {
80 active_index,
81 tools,
82 })
83 };
84 self
85 }
86
87 fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
88 let theme = theme(cx);
89
90 div()
91 .py_0p5()
92 .px_1()
93 .flex()
94 .items_center()
95 .justify_between()
96 .w_full()
97 .fill(theme.lowest.base.default.background)
98 .child(self.left_tools(&theme))
99 .child(self.right_tools(&theme))
100 }
101
102 fn left_tools(&self, theme: &Theme) -> impl Element<State = S> {
103 let workspace_state = get_workspace_state();
104
105 div()
106 .flex()
107 .items_center()
108 .gap_1()
109 .child(
110 IconButton::new(Icon::FileTree)
111 .when(
112 workspace_state.show_project_panel.load(Ordering::SeqCst),
113 |this| this.color(IconColor::Accent),
114 )
115 .on_click(|_, cx| {
116 let is_showing_project_panel =
117 workspace_state.show_project_panel.load(Ordering::SeqCst);
118
119 workspace_state
120 .show_project_panel
121 .compare_exchange(
122 is_showing_project_panel,
123 !is_showing_project_panel,
124 Ordering::SeqCst,
125 Ordering::SeqCst,
126 )
127 .unwrap();
128
129 cx.notify();
130 }),
131 )
132 .child(IconButton::new(Icon::Hash))
133 .child(ToolDivider::new())
134 .child(IconButton::new(Icon::XCircle))
135 }
136
137 fn right_tools(&self, theme: &Theme) -> impl Element<State = S> {
138 let workspace_state = get_workspace_state();
139
140 div()
141 .flex()
142 .items_center()
143 .gap_2()
144 .child(
145 div()
146 .flex()
147 .items_center()
148 .gap_1()
149 .child(Button::new("116:25"))
150 .child(Button::new("Rust").on_click(Arc::new(|_, cx| {
151 let is_showing_language_selector = workspace_state
152 .show_language_selector
153 .load(Ordering::SeqCst);
154
155 workspace_state
156 .show_language_selector
157 .compare_exchange(
158 is_showing_language_selector,
159 !is_showing_language_selector,
160 Ordering::SeqCst,
161 Ordering::SeqCst,
162 )
163 .unwrap();
164
165 cx.notify();
166 }))),
167 )
168 .child(ToolDivider::new())
169 .child(
170 div()
171 .flex()
172 .items_center()
173 .gap_1()
174 .child(
175 IconButton::new(Icon::Copilot)
176 .on_click(|_, _| println!("Copilot clicked.")),
177 )
178 .child(
179 IconButton::new(Icon::Envelope)
180 .on_click(|_, _| println!("Send Feedback clicked.")),
181 ),
182 )
183 .child(ToolDivider::new())
184 .child(
185 div()
186 .flex()
187 .items_center()
188 .gap_1()
189 .child(
190 IconButton::new(Icon::Terminal)
191 .when(
192 workspace_state.show_terminal.load(Ordering::SeqCst),
193 |this| this.color(IconColor::Accent),
194 )
195 .on_click(|_, cx| {
196 let is_showing_terminal =
197 workspace_state.show_terminal.load(Ordering::SeqCst);
198
199 workspace_state
200 .show_terminal
201 .compare_exchange(
202 is_showing_terminal,
203 !is_showing_terminal,
204 Ordering::SeqCst,
205 Ordering::SeqCst,
206 )
207 .unwrap();
208
209 cx.notify();
210 }),
211 )
212 .child(
213 IconButton::new(Icon::MessageBubbles)
214 .when(
215 workspace_state.show_chat_panel.load(Ordering::SeqCst),
216 |this| this.color(IconColor::Accent),
217 )
218 .on_click(|_, cx| {
219 let is_showing_chat_panel =
220 workspace_state.show_chat_panel.load(Ordering::SeqCst);
221
222 workspace_state
223 .show_chat_panel
224 .compare_exchange(
225 is_showing_chat_panel,
226 !is_showing_chat_panel,
227 Ordering::SeqCst,
228 Ordering::SeqCst,
229 )
230 .unwrap();
231
232 cx.notify();
233 }),
234 )
235 .child(IconButton::new(Icon::Ai)),
236 )
237 }
238}