workspace.rs

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