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