workspace.rs
1use crate::{collab_panel::collab_panel, modules::tab_bar, theme::theme};
2use gpui2::{
3 elements::{div, div::ScrollState, img, svg},
4 style::{StyleHelpers, Styleable},
5 Element, IntoElement, ParentElement, ViewContext,
6};
7
8#[derive(Element, Default)]
9struct WorkspaceElement {
10 left_scroll_state: ScrollState,
11 right_scroll_state: ScrollState,
12 tab_bar_scroll_state: ScrollState,
13}
14
15pub fn workspace<V: 'static>() -> impl Element<V> {
16 WorkspaceElement::default()
17}
18
19impl WorkspaceElement {
20 fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
21 let theme = theme(cx);
22
23 div()
24 .size_full()
25 .flex()
26 .flex_col()
27 .font("Zed Sans Extended")
28 .gap_0()
29 .justify_start()
30 .items_start()
31 .text_color(theme.lowest.base.default.foreground)
32 .fill(theme.middle.base.default.background)
33 .child(titlebar())
34 .child(
35 div()
36 .flex_1()
37 .w_full()
38 .flex()
39 .flex_row()
40 .overflow_hidden()
41 .child(collab_panel(self.left_scroll_state.clone()))
42 .child(
43 div()
44 .h_full()
45 .flex_1()
46 .fill(theme.highest.base.default.background)
47 .child(
48 div()
49 .flex()
50 .flex_col()
51 .flex_1()
52 .child(tab_bar(self.tab_bar_scroll_state.clone())),
53 ),
54 )
55 .child(collab_panel(self.right_scroll_state.clone())),
56 )
57 .child(statusbar())
58 }
59}
60
61#[derive(Element)]
62struct TitleBar;
63
64pub fn titlebar<V: 'static>() -> impl Element<V> {
65 TitleBar
66}
67
68impl TitleBar {
69 fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
70 let theme = theme(cx);
71 div()
72 .flex()
73 .items_center()
74 .justify_between()
75 .w_full()
76 .h_8()
77 .fill(theme.lowest.base.default.background)
78 .child(self.left_group(cx))
79 .child(self.right_group(cx))
80 }
81
82 fn left_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
83 let theme = theme(cx);
84 div()
85 .flex()
86 .items_center()
87 .h_full()
88 .gap_4()
89 .px_2()
90 // === Traffic Lights === //
91 .child(
92 div()
93 .flex()
94 .items_center()
95 .gap_2()
96 .child(
97 div()
98 .w_3()
99 .h_3()
100 .rounded_full()
101 .fill(theme.lowest.positive.default.foreground),
102 )
103 .child(
104 div()
105 .w_3()
106 .h_3()
107 .rounded_full()
108 .fill(theme.lowest.warning.default.foreground),
109 )
110 .child(
111 div()
112 .w_3()
113 .h_3()
114 .rounded_full()
115 .fill(theme.lowest.negative.default.foreground),
116 ),
117 )
118 // === Project Info === //
119 .child(
120 div()
121 .flex()
122 .items_center()
123 .gap_1()
124 .child(
125 div()
126 .h_full()
127 .flex()
128 .items_center()
129 .justify_center()
130 .px_2()
131 .rounded_md()
132 .hover()
133 .fill(theme.lowest.base.hovered.background)
134 .active()
135 .fill(theme.lowest.base.pressed.background)
136 .child(div().text_sm().child("project")),
137 )
138 .child(
139 div()
140 .h_full()
141 .flex()
142 .items_center()
143 .justify_center()
144 .px_2()
145 .rounded_md()
146 .text_color(theme.lowest.variant.default.foreground)
147 .hover()
148 .fill(theme.lowest.base.hovered.background)
149 .active()
150 .fill(theme.lowest.base.pressed.background)
151 .child(div().text_sm().child("branch")),
152 ),
153 )
154 }
155
156 fn right_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
157 let theme = theme(cx);
158 div()
159 .flex()
160 .items_center()
161 .h_full()
162 .gap_3()
163 .px_2()
164 // === Actions === //
165 .child(
166 div().child(
167 div().flex().items_center().gap_1().child(
168 div().size_4().flex().items_center().justify_center().child(
169 svg()
170 .path("icons/exit.svg")
171 .size_4()
172 .fill(theme.lowest.base.default.foreground),
173 ),
174 ),
175 ),
176 )
177 .child(div().w_px().h_3().fill(theme.lowest.base.default.border))
178 // === Comms === //
179 .child(
180 div().child(
181 div()
182 .flex()
183 .items_center()
184 .gap_px()
185 .child(
186 div()
187 .px_2()
188 .py_1()
189 .rounded_md()
190 .h_full()
191 .flex()
192 .items_center()
193 .justify_center()
194 .hover()
195 .fill(theme.lowest.base.hovered.background)
196 .active()
197 .fill(theme.lowest.base.pressed.background)
198 .child(
199 svg()
200 .path("icons/microphone.svg")
201 .size_3p5()
202 .fill(theme.lowest.base.default.foreground),
203 ),
204 )
205 .child(
206 div()
207 .px_2()
208 .py_1()
209 .rounded_md()
210 .h_full()
211 .flex()
212 .items_center()
213 .justify_center()
214 .hover()
215 .fill(theme.lowest.base.hovered.background)
216 .active()
217 .fill(theme.lowest.base.pressed.background)
218 .child(
219 svg()
220 .path("icons/radix/speaker-loud.svg")
221 .size_3p5()
222 .fill(theme.lowest.base.default.foreground),
223 ),
224 )
225 .child(
226 div()
227 .px_2()
228 .py_1()
229 .rounded_md()
230 .h_full()
231 .flex()
232 .items_center()
233 .justify_center()
234 .hover()
235 .fill(theme.lowest.base.hovered.background)
236 .active()
237 .fill(theme.lowest.base.pressed.background)
238 .child(
239 svg()
240 .path("icons/radix/desktop.svg")
241 .size_3p5()
242 .fill(theme.lowest.base.default.foreground),
243 ),
244 ),
245 ),
246 )
247 .child(div().w_px().h_3().fill(theme.lowest.base.default.border))
248 // User Group
249 .child(
250 div().child(
251 div()
252 .px_1()
253 .py_1()
254 .flex()
255 .items_center()
256 .justify_center()
257 .rounded_md()
258 .gap_0p5()
259 .hover()
260 .fill(theme.lowest.base.hovered.background)
261 .active()
262 .fill(theme.lowest.base.pressed.background)
263 .child(
264 img()
265 .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
266 .size_4()
267 .rounded_md()
268 .fill(theme.middle.on.default.foreground),
269 )
270 .child(
271 svg()
272 .path("icons/caret_down_8.svg")
273 .w_2()
274 .h_2()
275 .fill(theme.lowest.variant.default.foreground),
276 ),
277 ),
278 )
279 }
280}
281
282// ================================================================================ //
283
284#[derive(Element)]
285struct StatusBar;
286
287pub fn statusbar<V: 'static>() -> impl Element<V> {
288 StatusBar
289}
290
291impl StatusBar {
292 fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
293 let theme = theme(cx);
294 div()
295 .flex()
296 .items_center()
297 .justify_between()
298 .w_full()
299 .h_8()
300 .fill(theme.lowest.base.default.background)
301 .child(self.left_group(cx))
302 .child(self.right_group(cx))
303 }
304
305 fn left_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
306 let theme = theme(cx);
307 div()
308 .flex()
309 .items_center()
310 .h_full()
311 .gap_4()
312 .px_2()
313 // === Tools === //
314 .child(
315 div()
316 .flex()
317 .items_center()
318 .gap_1()
319 .child(
320 div()
321 .w_6()
322 .h_full()
323 .flex()
324 .items_center()
325 .justify_center()
326 .child(
327 svg()
328 .path("icons/project.svg")
329 .w_4()
330 .h_4()
331 .fill(theme.lowest.base.default.foreground),
332 ),
333 )
334 .child(
335 div()
336 .w_6()
337 .h_full()
338 .flex()
339 .items_center()
340 .justify_center()
341 .child(
342 svg()
343 .path("icons/conversations.svg")
344 .w_4()
345 .h_4()
346 .fill(theme.lowest.base.default.foreground),
347 ),
348 )
349 .child(
350 div()
351 .w_6()
352 .h_full()
353 .flex()
354 .items_center()
355 .justify_center()
356 .child(
357 svg()
358 .path("icons/file_icons/notebook.svg")
359 .w_4()
360 .h_4()
361 .fill(theme.lowest.accent.default.foreground),
362 ),
363 ),
364 )
365 // === Diagnostics === //
366 .child(
367 div()
368 .flex()
369 .items_center()
370 .gap_2()
371 .child(
372 div()
373 .h_full()
374 .flex()
375 .items_center()
376 .justify_center()
377 .gap_0p5()
378 .px_1()
379 .text_color(theme.lowest.variant.default.foreground)
380 .hover()
381 .fill(theme.lowest.base.hovered.background)
382 .active()
383 .fill(theme.lowest.base.pressed.background)
384 .child(
385 svg()
386 .path("icons/error.svg")
387 .w_4()
388 .h_4()
389 .fill(theme.lowest.negative.default.foreground),
390 )
391 .child(div().text_sm().child("2")),
392 )
393 .child(
394 div()
395 .text_sm()
396 .text_color(theme.lowest.variant.default.foreground)
397 .child("Something is wrong"),
398 ),
399 )
400 }
401
402 fn right_group<V: 'static>(&mut self, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
403 let theme = theme(cx);
404 div()
405 .flex()
406 .items_center()
407 .h_full()
408 .gap_4()
409 .px_2()
410 // === Tools === //
411 .child(
412 div()
413 .flex()
414 .items_center()
415 .gap_1()
416 .child(
417 div()
418 .w_6()
419 .h_full()
420 .flex()
421 .items_center()
422 .justify_center()
423 .child(
424 svg()
425 .path("icons/check_circle.svg")
426 .w_4()
427 .h_4()
428 .fill(theme.lowest.base.default.foreground),
429 ),
430 )
431 .child(
432 div()
433 .w_6()
434 .h_full()
435 .flex()
436 .items_center()
437 .justify_center()
438 .child(
439 svg()
440 .path("icons/copilot.svg")
441 .w_4()
442 .h_4()
443 .fill(theme.lowest.accent.default.foreground),
444 ),
445 ),
446 )
447 }
448}