workspace.rs

  1use chrono::DateTime;
  2use gpui3::{px, relative, view, Context, Size, View};
  3
  4use crate::settings::Settings;
  5use crate::{h_stack, prelude::*, Button};
  6use crate::{
  7    theme, v_stack, AssistantPanel, ChatMessage, ChatPanel, CollabPanel, EditorPane, Label,
  8    LanguageSelector, Pane, PaneGroup, Panel, PanelAllowedSides, PanelSide, ProjectPanel,
  9    SplitDirection, StatusBar, Terminal, TitleBar, Toast, ToastOrigin,
 10};
 11
 12#[derive(Clone)]
 13pub struct GPUI2UIDebug {
 14    pub in_livestream: bool,
 15    pub enable_user_settings: bool,
 16    pub show_toast: bool,
 17}
 18
 19impl Default for GPUI2UIDebug {
 20    fn default() -> Self {
 21        Self {
 22            in_livestream: false,
 23            enable_user_settings: false,
 24            show_toast: false,
 25        }
 26    }
 27}
 28
 29#[derive(Clone)]
 30pub struct Workspace {
 31    title_bar: View<TitleBar>,
 32    editor_1: View<EditorPane>,
 33    show_project_panel: bool,
 34    show_collab_panel: bool,
 35    show_chat_panel: bool,
 36    show_assistant_panel: bool,
 37    show_notifications_panel: bool,
 38    show_terminal: bool,
 39    show_debug: bool,
 40    show_language_selector: bool,
 41    left_panel_scroll_state: ScrollState,
 42    right_panel_scroll_state: ScrollState,
 43    tab_bar_scroll_state: ScrollState,
 44    bottom_panel_scroll_state: ScrollState,
 45    debug: GPUI2UIDebug,
 46    settings: Settings,
 47}
 48
 49impl Workspace {
 50    pub fn new(cx: &mut ViewContext<Self>) -> Self {
 51        Self {
 52            title_bar: TitleBar::view(cx),
 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            left_panel_scroll_state: ScrollState::default(),
 63            right_panel_scroll_state: ScrollState::default(),
 64            tab_bar_scroll_state: ScrollState::default(),
 65            bottom_panel_scroll_state: ScrollState::default(),
 66            debug: GPUI2UIDebug::default(),
 67            settings: Settings::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        if self.debug.enable_user_settings {
157            self.debug.enable_user_settings = false;
158        } else {
159            self.debug.enable_user_settings = true;
160        }
161        cx.notify();
162    }
163
164    pub fn view(cx: &mut WindowContext) -> View<Self> {
165        view(cx.entity(|cx| Self::new(cx)), Self::render)
166    }
167
168    pub fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<ViewState = Self> {
169        let theme = theme(cx).clone();
170
171        let root_group = PaneGroup::new_panes(
172            vec![Pane::new(
173                ScrollState::default(),
174                Size {
175                    width: relative(1.).into(),
176                    height: relative(1.).into(),
177                },
178            )
179            .child(self.editor_1.clone())],
180            SplitDirection::Horizontal,
181        );
182
183        v_stack()
184            .child(
185                div()
186                    .relative()
187                    .size_full()
188                    .flex()
189                    .flex_col()
190                    .font("Zed Sans Extended")
191                    .gap_0()
192                    .justify_start()
193                    .items_start()
194                    .text_color(theme.lowest.base.default.foreground)
195                    .bg(theme.lowest.base.default.background)
196                    .child(self.title_bar.clone())
197                    .child(
198                        div()
199                            .flex_1()
200                            .w_full()
201                            .flex()
202                            .flex_row()
203                            .overflow_hidden()
204                            .border_t()
205                            .border_b()
206                            .border_color(theme.lowest.base.default.border)
207                            .children(
208                                Some(
209                                    Panel::new(self.left_panel_scroll_state.clone())
210                                        .side(PanelSide::Left)
211                                        .child(ProjectPanel::new(ScrollState::default())),
212                                )
213                                .filter(|_| self.is_project_panel_open()),
214                            )
215                            .children(
216                                Some(
217                                    Panel::new(self.left_panel_scroll_state.clone())
218                                        .child(CollabPanel::new(ScrollState::default()))
219                                        .side(PanelSide::Left),
220                                )
221                                .filter(|_| self.is_collab_panel_open()),
222                            )
223                            .child(
224                                v_stack()
225                                    .flex_1()
226                                    .h_full()
227                                    .child(
228                                        div()
229                                            .flex()
230                                            .flex_1()
231                                            // CSS Hack: Flex 1 has to have a set height to properly fill the space
232                                            // Or it will give you a height of 0
233                                            // Marshall: We may not need this anymore with `gpui3`. It seems to render
234                                            //           fine without it.
235                                            .h_px()
236                                            .child(root_group),
237                                    )
238                                    .children(
239                                        Some(
240                                            Panel::new(self.bottom_panel_scroll_state.clone())
241                                                .child(Terminal::new())
242                                                .allowed_sides(PanelAllowedSides::BottomOnly)
243                                                .side(PanelSide::Bottom),
244                                        )
245                                        .filter(|_| self.is_terminal_open()),
246                                    ),
247                            )
248                            .children(
249                                Some(
250                                    Panel::new(self.right_panel_scroll_state.clone())
251                                        .side(PanelSide::Right)
252                                        .child(ChatPanel::new(ScrollState::default()).messages(
253                                            vec![
254                                    ChatMessage::new(
255                                        "osiewicz".to_string(),
256                                        "is this thing on?".to_string(),
257                                        DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
258                                            .unwrap()
259                                            .naive_local(),
260                                    ),
261                                    ChatMessage::new(
262                                        "maxdeviant".to_string(),
263                                        "Reading you loud and clear!".to_string(),
264                                        DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
265                                            .unwrap()
266                                            .naive_local(),
267                                    ),
268                                ],
269                                        )),
270                                )
271                                .filter(|_| self.is_chat_panel_open()),
272                            )
273                            .children(
274                                Some(
275                                    Panel::new(self.right_panel_scroll_state.clone())
276                                        .side(PanelSide::Right)
277                                        .child(div().w_96().h_full().child("Notifications")),
278                                )
279                                .filter(|_| self.is_notifications_panel_open()),
280                            )
281                            .children(
282                                Some(
283                                    Panel::new(self.right_panel_scroll_state.clone())
284                                        .child(AssistantPanel::new()),
285                                )
286                                .filter(|_| self.is_assistant_panel_open()),
287                            ),
288                    )
289                    .child(StatusBar::new())
290                    .children(
291                        Some(
292                            div()
293                                .absolute()
294                                .top(px(50.))
295                                .left(px(640.))
296                                .z_index(999)
297                                .child(LanguageSelector::new()),
298                        )
299                        .filter(|_| self.is_language_selector_open()),
300                    )
301                    .child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast"))),
302            )
303            .child(
304                h_stack().gap_2().child(
305                    Button::<Workspace>::new("Toggle Debug")
306                        .on_click(|workspace, cx| workspace.toggle_debug(cx)),
307                ),
308            )
309    }
310}
311
312#[cfg(feature = "stories")]
313pub use stories::*;
314
315#[cfg(feature = "stories")]
316mod stories {
317    use super::*;
318
319    pub struct WorkspaceStory {
320        workspace: View<Workspace>,
321    }
322
323    impl WorkspaceStory {
324        pub fn view(cx: &mut WindowContext) -> View<Self> {
325            view(
326                cx.entity(|cx| Self {
327                    workspace: Workspace::view(cx),
328                }),
329                |view, cx| view.workspace.clone(),
330            )
331        }
332    }
333}