workspace.rs

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