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}