workspace.rs

  1use chrono::DateTime;
  2use gpui3::{px, relative, rems, view, Context, Size, View};
  3
  4use crate::prelude::*;
  5use crate::{
  6    theme, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel, CollabPanel, EditorPane, Label,
  7    LanguageSelector, NotificationToast, Pane, PaneGroup, Panel, PanelAllowedSides, PanelSide,
  8    ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar, Toast, ToastOrigin,
  9};
 10
 11#[derive(Clone)]
 12pub struct Workspace {
 13    title_bar: View<TitleBar>,
 14    editor_1: View<EditorPane>,
 15    editor_2: View<EditorPane>,
 16    show_project_panel: bool,
 17    show_collab_panel: bool,
 18    show_chat_panel: bool,
 19    show_assistant_panel: bool,
 20    show_terminal: bool,
 21    show_language_selector: bool,
 22    left_panel_scroll_state: ScrollState,
 23    right_panel_scroll_state: ScrollState,
 24    tab_bar_scroll_state: ScrollState,
 25    bottom_panel_scroll_state: ScrollState,
 26}
 27
 28impl Workspace {
 29    pub fn new(cx: &mut ViewContext<Self>) -> Self {
 30        Self {
 31            title_bar: TitleBar::view(cx),
 32            editor_1: EditorPane::view(cx),
 33            editor_2: EditorPane::view(cx),
 34            show_project_panel: true,
 35            show_collab_panel: false,
 36            show_chat_panel: true,
 37            show_assistant_panel: false,
 38            show_terminal: true,
 39            show_language_selector: false,
 40            left_panel_scroll_state: ScrollState::default(),
 41            right_panel_scroll_state: ScrollState::default(),
 42            tab_bar_scroll_state: ScrollState::default(),
 43            bottom_panel_scroll_state: ScrollState::default(),
 44        }
 45    }
 46
 47    pub fn is_project_panel_open(&self) -> bool {
 48        self.show_project_panel
 49    }
 50
 51    pub fn toggle_project_panel(&mut self, cx: &mut ViewContext<Self>) {
 52        self.show_project_panel = !self.show_project_panel;
 53
 54        self.show_collab_panel = false;
 55
 56        cx.notify();
 57    }
 58
 59    pub fn is_collab_panel_open(&self) -> bool {
 60        self.show_collab_panel
 61    }
 62
 63    pub fn toggle_collab_panel(&mut self) {
 64        self.show_collab_panel = !self.show_collab_panel;
 65
 66        self.show_project_panel = false;
 67    }
 68
 69    pub fn is_terminal_open(&self) -> bool {
 70        self.show_terminal
 71    }
 72
 73    pub fn toggle_terminal(&mut self, cx: &mut ViewContext<Self>) {
 74        self.show_terminal = !self.show_terminal;
 75
 76        cx.notify();
 77    }
 78
 79    pub fn is_chat_panel_open(&self) -> bool {
 80        self.show_chat_panel
 81    }
 82
 83    pub fn toggle_chat_panel(&mut self, cx: &mut ViewContext<Self>) {
 84        self.show_chat_panel = !self.show_chat_panel;
 85
 86        self.show_assistant_panel = false;
 87
 88        cx.notify();
 89    }
 90
 91    pub fn is_assistant_panel_open(&self) -> bool {
 92        self.show_assistant_panel
 93    }
 94
 95    pub fn toggle_assistant_panel(&mut self, cx: &mut ViewContext<Self>) {
 96        self.show_assistant_panel = !self.show_assistant_panel;
 97
 98        self.show_chat_panel = false;
 99
100        cx.notify();
101    }
102
103    pub fn is_language_selector_open(&self) -> bool {
104        self.show_language_selector
105    }
106
107    pub fn toggle_language_selector(&mut self, cx: &mut ViewContext<Self>) {
108        self.show_language_selector = !self.show_language_selector;
109
110        cx.notify();
111    }
112
113    pub fn view(cx: &mut WindowContext) -> View<Self> {
114        view(cx.entity(|cx| Self::new(cx)), Self::render)
115    }
116
117    pub fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<ViewState = Self> {
118        let theme = theme(cx).clone();
119
120        let temp_size = rems(36.).into();
121
122        let root_group = PaneGroup::new_groups(
123            vec![
124                PaneGroup::new_panes(
125                    vec![
126                        Pane::new(
127                            ScrollState::default(),
128                            Size {
129                                width: relative(1.).into(),
130                                height: temp_size,
131                            },
132                        )
133                        .child(self.editor_1.clone()),
134                        Pane::new(
135                            ScrollState::default(),
136                            Size {
137                                width: relative(1.).into(),
138                                height: temp_size,
139                            },
140                        )
141                        .child(Terminal::new()),
142                    ],
143                    SplitDirection::Vertical,
144                ),
145                PaneGroup::new_panes(
146                    vec![Pane::new(
147                        ScrollState::default(),
148                        Size {
149                            width: relative(1.).into(),
150                            height: relative(1.).into(),
151                        },
152                    )
153                    .child(self.editor_2.clone())],
154                    SplitDirection::Vertical,
155                ),
156            ],
157            SplitDirection::Horizontal,
158        );
159
160        div()
161            .relative()
162            .size_full()
163            .flex()
164            .flex_col()
165            .font("Zed Sans Extended")
166            .gap_0()
167            .justify_start()
168            .items_start()
169            .text_color(theme.lowest.base.default.foreground)
170            .fill(theme.lowest.base.default.background)
171            .child(self.title_bar.clone())
172            .child(
173                div()
174                    .flex_1()
175                    .w_full()
176                    .flex()
177                    .flex_row()
178                    .overflow_hidden()
179                    .border_t()
180                    .border_b()
181                    .border_color(theme.lowest.base.default.border)
182                    .children(
183                        Some(
184                            Panel::new(self.left_panel_scroll_state.clone())
185                                .side(PanelSide::Left)
186                                .child(ProjectPanel::new(ScrollState::default())),
187                        )
188                        .filter(|_| self.is_project_panel_open()),
189                    )
190                    .children(
191                        Some(
192                            Panel::new(self.left_panel_scroll_state.clone())
193                                .child(CollabPanel::new(ScrollState::default()))
194                                .side(PanelSide::Left),
195                        )
196                        .filter(|_| self.is_collab_panel_open()),
197                    )
198                    .child(
199                        v_stack()
200                            .flex_1()
201                            .h_full()
202                            .child(
203                                div()
204                                    .flex()
205                                    .flex_1()
206                                    // CSS Hack: Flex 1 has to have a set height to properly fill the space
207                                    // Or it will give you a height of 0
208                                    // Marshall: We may not need this anymore with `gpui3`. It seems to render
209                                    //           fine without it.
210                                    .h_px()
211                                    .child(root_group),
212                            )
213                            .children(
214                                Some(
215                                    Panel::new(self.bottom_panel_scroll_state.clone())
216                                        .child(Terminal::new())
217                                        .allowed_sides(PanelAllowedSides::BottomOnly)
218                                        .side(PanelSide::Bottom),
219                                )
220                                .filter(|_| self.is_terminal_open()),
221                            ),
222                    )
223                    .children(
224                        Some(
225                            Panel::new(self.right_panel_scroll_state.clone())
226                                .side(PanelSide::Right)
227                                .child(ChatPanel::new(ScrollState::default()).messages(vec![
228                                    ChatMessage::new(
229                                        "osiewicz".to_string(),
230                                        "is this thing on?".to_string(),
231                                        DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
232                                            .unwrap()
233                                            .naive_local(),
234                                    ),
235                                    ChatMessage::new(
236                                        "maxdeviant".to_string(),
237                                        "Reading you loud and clear!".to_string(),
238                                        DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
239                                            .unwrap()
240                                            .naive_local(),
241                                    ),
242                                ])),
243                        )
244                        .filter(|_| self.is_chat_panel_open()),
245                    )
246                    .children(
247                        Some(
248                            Panel::new(self.right_panel_scroll_state.clone())
249                                .child(AssistantPanel::new()),
250                        )
251                        .filter(|_| self.is_assistant_panel_open()),
252                    ),
253            )
254            .child(StatusBar::new())
255            .children(
256                Some(
257                    div()
258                        .absolute()
259                        .top(px(50.))
260                        .left(px(640.))
261                        .z_index(999)
262                        .child(LanguageSelector::new()),
263                )
264                .filter(|_| self.is_language_selector_open()),
265            )
266            .child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast")))
267            // .child(Toast::new(ToastOrigin::BottomRight).child(Label::new("Another toast")))
268            .child(NotificationToast::new(
269                "Can't pull changes from origin",
270                "Your local branch is behind the remote branch. Please pull the latest changes before pushing.",
271                Button::new("Stash & Switch").variant(ButtonVariant::Filled),
272            ).secondary_action(Button::new("Cancel")))
273    }
274}
275
276#[cfg(feature = "stories")]
277pub use stories::*;
278
279#[cfg(feature = "stories")]
280mod stories {
281    use super::*;
282
283    pub struct WorkspaceStory {
284        workspace: View<Workspace>,
285    }
286
287    impl WorkspaceStory {
288        pub fn view(cx: &mut WindowContext) -> View<Self> {
289            view(
290                cx.entity(|cx| Self {
291                    workspace: Workspace::view(cx),
292                }),
293                |view, cx| view.workspace.clone(),
294            )
295        }
296    }
297}