workspace.rs

  1use std::sync::Arc;
  2
  3use chrono::DateTime;
  4use gpui::{px, relative, Div, Render, Size, View, VisualContext};
  5use settings2::Settings;
  6use theme2::ThemeSettings;
  7
  8use crate::prelude::*;
  9use crate::{
 10    static_livestream, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel, Checkbox,
 11    CollabPanel, EditorPane, Label, LanguageSelector, NotificationsPanel, Pane, PaneGroup, Panel,
 12    PanelAllowedSides, PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar,
 13    Toast, ToastOrigin,
 14};
 15
 16#[derive(Clone)]
 17pub struct Gpui2UiDebug {
 18    pub in_livestream: bool,
 19    pub enable_user_settings: bool,
 20    pub show_toast: bool,
 21}
 22
 23impl Default for Gpui2UiDebug {
 24    fn default() -> Self {
 25        Self {
 26            in_livestream: false,
 27            enable_user_settings: false,
 28            show_toast: false,
 29        }
 30    }
 31}
 32
 33#[derive(Clone)]
 34pub struct Workspace {
 35    title_bar: View<TitleBar>,
 36    editor_1: View<EditorPane>,
 37    show_project_panel: bool,
 38    show_collab_panel: bool,
 39    show_chat_panel: bool,
 40    show_assistant_panel: bool,
 41    show_notifications_panel: bool,
 42    show_terminal: bool,
 43    show_debug: bool,
 44    show_language_selector: bool,
 45    test_checkbox_selection: Selection,
 46    debug: Gpui2UiDebug,
 47}
 48
 49impl Workspace {
 50    pub fn new(cx: &mut ViewContext<Self>) -> Self {
 51        Self {
 52            title_bar: TitleBar::view(cx, None),
 53            editor_1: EditorPane::view(cx),
 54            show_project_panel: true,
 55            show_collab_panel: false,
 56            show_chat_panel: false,
 57            show_assistant_panel: false,
 58            show_terminal: true,
 59            show_language_selector: false,
 60            show_debug: false,
 61            show_notifications_panel: true,
 62            test_checkbox_selection: Selection::Unselected,
 63            debug: Gpui2UiDebug::default(),
 64        }
 65    }
 66
 67    pub fn is_project_panel_open(&self) -> bool {
 68        self.show_project_panel
 69    }
 70
 71    pub fn toggle_project_panel(&mut self, cx: &mut ViewContext<Self>) {
 72        self.show_project_panel = !self.show_project_panel;
 73
 74        self.show_collab_panel = false;
 75
 76        cx.notify();
 77    }
 78
 79    pub fn is_collab_panel_open(&self) -> bool {
 80        self.show_collab_panel
 81    }
 82
 83    pub fn toggle_collab_panel(&mut self) {
 84        self.show_collab_panel = !self.show_collab_panel;
 85
 86        self.show_project_panel = false;
 87    }
 88
 89    pub fn is_terminal_open(&self) -> bool {
 90        self.show_terminal
 91    }
 92
 93    pub fn toggle_terminal(&mut self, cx: &mut ViewContext<Self>) {
 94        self.show_terminal = !self.show_terminal;
 95
 96        cx.notify();
 97    }
 98
 99    pub fn is_chat_panel_open(&self) -> bool {
100        self.show_chat_panel
101    }
102
103    pub fn toggle_chat_panel(&mut self, cx: &mut ViewContext<Self>) {
104        self.show_chat_panel = !self.show_chat_panel;
105
106        self.show_assistant_panel = false;
107        self.show_notifications_panel = false;
108
109        cx.notify();
110    }
111
112    pub fn is_notifications_panel_open(&self) -> bool {
113        self.show_notifications_panel
114    }
115
116    pub fn toggle_notifications_panel(&mut self, cx: &mut ViewContext<Self>) {
117        self.show_notifications_panel = !self.show_notifications_panel;
118
119        self.show_chat_panel = false;
120        self.show_assistant_panel = false;
121
122        cx.notify();
123    }
124
125    pub fn is_assistant_panel_open(&self) -> bool {
126        self.show_assistant_panel
127    }
128
129    pub fn toggle_assistant_panel(&mut self, cx: &mut ViewContext<Self>) {
130        self.show_assistant_panel = !self.show_assistant_panel;
131
132        self.show_chat_panel = false;
133        self.show_notifications_panel = false;
134
135        cx.notify();
136    }
137
138    pub fn is_language_selector_open(&self) -> bool {
139        self.show_language_selector
140    }
141
142    pub fn toggle_language_selector(&mut self, cx: &mut ViewContext<Self>) {
143        self.show_language_selector = !self.show_language_selector;
144
145        cx.notify();
146    }
147
148    pub fn toggle_debug(&mut self, cx: &mut ViewContext<Self>) {
149        self.show_debug = !self.show_debug;
150
151        cx.notify();
152    }
153
154    pub fn debug_toggle_user_settings(&mut self, cx: &mut ViewContext<Self>) {
155        self.debug.enable_user_settings = !self.debug.enable_user_settings;
156
157        let mut theme_settings = ThemeSettings::get_global(cx).clone();
158
159        if self.debug.enable_user_settings {
160            theme_settings.ui_font_size = 18.0.into();
161        } else {
162            theme_settings.ui_font_size = 16.0.into();
163        }
164
165        ThemeSettings::override_global(theme_settings.clone(), cx);
166
167        cx.set_rem_size(theme_settings.ui_font_size);
168
169        cx.notify();
170    }
171
172    pub fn debug_toggle_livestream(&mut self, cx: &mut ViewContext<Self>) {
173        self.debug.in_livestream = !self.debug.in_livestream;
174
175        self.title_bar = TitleBar::view(
176            cx,
177            Some(static_livestream()).filter(|_| self.debug.in_livestream),
178        );
179
180        cx.notify();
181    }
182
183    pub fn debug_toggle_toast(&mut self, cx: &mut ViewContext<Self>) {
184        self.debug.show_toast = !self.debug.show_toast;
185
186        cx.notify();
187    }
188
189    pub fn view(cx: &mut WindowContext) -> View<Self> {
190        cx.build_view(|cx| Self::new(cx))
191    }
192}
193
194impl Render for Workspace {
195    type Element = Div<Self>;
196
197    fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
198        let root_group = PaneGroup::new_panes(
199            vec![Pane::new(
200                "pane-0",
201                Size {
202                    width: relative(1.).into(),
203                    height: relative(1.).into(),
204                },
205            )
206            .child(self.editor_1.clone())],
207            SplitDirection::Horizontal,
208        );
209        let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
210
211        div()
212            .relative()
213            .size_full()
214            .flex()
215            .flex_col()
216            .font(ui_font)
217            .gap_0()
218            .justify_start()
219            .items_start()
220            .text_color(cx.theme().colors().text)
221            .bg(cx.theme().colors().background)
222            .child(self.title_bar.clone())
223            .child(
224                div()
225                    .absolute()
226                    .top_12()
227                    .left_12()
228                    .z_index(99)
229                    .bg(cx.theme().colors().background)
230                    .child(
231                        Checkbox::new("test_checkbox", self.test_checkbox_selection).on_click(
232                            |selection, workspace: &mut Workspace, cx| {
233                                workspace.test_checkbox_selection = selection;
234
235                                cx.notify();
236                            },
237                        ),
238                    ),
239            )
240            .child(
241                div()
242                    .flex_1()
243                    .w_full()
244                    .flex()
245                    .flex_row()
246                    .overflow_hidden()
247                    .border_t()
248                    .border_b()
249                    .border_color(cx.theme().colors().border)
250                    .children(
251                        Some(
252                            Panel::new("project-panel-outer", cx)
253                                .side(PanelSide::Left)
254                                .child(ProjectPanel::new("project-panel-inner")),
255                        )
256                        .filter(|_| self.is_project_panel_open()),
257                    )
258                    .children(
259                        Some(
260                            Panel::new("collab-panel-outer", cx)
261                                .child(CollabPanel::new("collab-panel-inner"))
262                                .side(PanelSide::Left),
263                        )
264                        .filter(|_| self.is_collab_panel_open()),
265                    )
266                    // .child(NotificationToast::new(
267                    //     "maxbrunsfeld has requested to add you as a contact.".into(),
268                    // ))
269                    .child(
270                        v_stack()
271                            .flex_1()
272                            .h_full()
273                            .child(div().flex().flex_1().child(root_group))
274                            .children(
275                                Some(
276                                    Panel::new("terminal-panel", cx)
277                                        .child(Terminal::new())
278                                        .allowed_sides(PanelAllowedSides::BottomOnly)
279                                        .side(PanelSide::Bottom),
280                                )
281                                .filter(|_| self.is_terminal_open()),
282                            ),
283                    )
284                    .children(
285                        Some(
286                            Panel::new("chat-panel-outer", cx)
287                                .side(PanelSide::Right)
288                                .child(ChatPanel::new("chat-panel-inner").messages(vec![
289                                    ChatMessage::new(
290                                        "osiewicz".to_string(),
291                                        "is this thing on?".to_string(),
292                                        DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
293                                            .unwrap()
294                                            .naive_local(),
295                                    ),
296                                    ChatMessage::new(
297                                        "maxdeviant".to_string(),
298                                        "Reading you loud and clear!".to_string(),
299                                        DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
300                                            .unwrap()
301                                            .naive_local(),
302                                    ),
303                                ])),
304                        )
305                        .filter(|_| self.is_chat_panel_open()),
306                    )
307                    .children(
308                        Some(
309                            Panel::new("notifications-panel-outer", cx)
310                                .side(PanelSide::Right)
311                                .child(NotificationsPanel::new("notifications-panel-inner")),
312                        )
313                        .filter(|_| self.is_notifications_panel_open()),
314                    )
315                    .children(
316                        Some(
317                            Panel::new("assistant-panel-outer", cx)
318                                .child(AssistantPanel::new("assistant-panel-inner")),
319                        )
320                        .filter(|_| self.is_assistant_panel_open()),
321                    ),
322            )
323            .child(StatusBar::new())
324            .when(self.debug.show_toast, |this| {
325                this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast")))
326            })
327            .children(
328                Some(
329                    div()
330                        .absolute()
331                        .top(px(50.))
332                        .left(px(640.))
333                        .z_index(8)
334                        .child(LanguageSelector::new("language-selector")),
335                )
336                .filter(|_| self.is_language_selector_open()),
337            )
338            .z_index(8)
339            // Debug
340            .child(
341                v_stack()
342                    .z_index(9)
343                    .absolute()
344                    .top_20()
345                    .left_1_4()
346                    .w_40()
347                    .gap_2()
348                    .when(self.show_debug, |this| {
349                        this.child(Button::<Workspace>::new("Toggle User Settings").on_click(
350                            Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)),
351                        ))
352                        .child(
353                            Button::<Workspace>::new("Toggle Toasts").on_click(Arc::new(
354                                |workspace, cx| workspace.debug_toggle_toast(cx),
355                            )),
356                        )
357                        .child(
358                            Button::<Workspace>::new("Toggle Livestream").on_click(Arc::new(
359                                |workspace, cx| workspace.debug_toggle_livestream(cx),
360                            )),
361                        )
362                    })
363                    .child(
364                        Button::<Workspace>::new("Toggle Debug")
365                            .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))),
366                    ),
367            )
368    }
369}
370
371#[cfg(feature = "stories")]
372pub use stories::*;
373
374#[cfg(feature = "stories")]
375mod stories {
376    use super::*;
377    use gpui::VisualContext;
378
379    pub struct WorkspaceStory {
380        workspace: View<Workspace>,
381    }
382
383    impl WorkspaceStory {
384        pub fn view(cx: &mut WindowContext) -> View<Self> {
385            cx.build_view(|cx| Self {
386                workspace: Workspace::view(cx),
387            })
388        }
389    }
390
391    impl Render for WorkspaceStory {
392        type Element = Div<Self>;
393
394        fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
395            div().child(self.workspace.clone())
396        }
397    }
398}