workspace.rs

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