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}