workspace.rs

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