workspace.rs

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