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