workspace.rs

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