workspace.rs

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