1use std::sync::Arc;
2
3use chrono::DateTime;
4use gpui2::{px, relative, rems, Div, Render, Size, View, VisualContext};
5
6use crate::{prelude::*, NotificationsPanel};
7use crate::{
8 static_livestream, user_settings_mut, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel,
9 CollabPanel, EditorPane, FakeSettings, Label, LanguageSelector, Pane, PaneGroup, Panel,
10 PanelAllowedSides, PanelSide, ProjectPanel, SettingValue, SplitDirection, StatusBar, Terminal,
11 TitleBar, Toast, ToastOrigin,
12};
13
14#[derive(Clone)]
15pub struct Gpui2UiDebug {
16 pub in_livestream: bool,
17 pub enable_user_settings: bool,
18 pub show_toast: bool,
19}
20
21impl Default for Gpui2UiDebug {
22 fn default() -> Self {
23 Self {
24 in_livestream: false,
25 enable_user_settings: false,
26 show_toast: false,
27 }
28 }
29}
30
31#[derive(Clone)]
32pub struct Workspace {
33 title_bar: View<TitleBar>,
34 editor_1: View<EditorPane>,
35 show_project_panel: bool,
36 show_collab_panel: bool,
37 show_chat_panel: bool,
38 show_assistant_panel: bool,
39 show_notifications_panel: bool,
40 show_terminal: bool,
41 show_debug: bool,
42 show_language_selector: bool,
43 debug: Gpui2UiDebug,
44}
45
46impl Workspace {
47 pub fn new(cx: &mut ViewContext<Self>) -> Self {
48 Self {
49 title_bar: TitleBar::view(cx, None),
50 editor_1: EditorPane::view(cx),
51 show_project_panel: true,
52 show_collab_panel: false,
53 show_chat_panel: false,
54 show_assistant_panel: false,
55 show_terminal: true,
56 show_language_selector: false,
57 show_debug: false,
58 show_notifications_panel: true,
59 debug: Gpui2UiDebug::default(),
60 }
61 }
62
63 pub fn is_project_panel_open(&self) -> bool {
64 self.show_project_panel
65 }
66
67 pub fn toggle_project_panel(&mut self, cx: &mut ViewContext<Self>) {
68 self.show_project_panel = !self.show_project_panel;
69
70 self.show_collab_panel = false;
71
72 cx.notify();
73 }
74
75 pub fn is_collab_panel_open(&self) -> bool {
76 self.show_collab_panel
77 }
78
79 pub fn toggle_collab_panel(&mut self) {
80 self.show_collab_panel = !self.show_collab_panel;
81
82 self.show_project_panel = false;
83 }
84
85 pub fn is_terminal_open(&self) -> bool {
86 self.show_terminal
87 }
88
89 pub fn toggle_terminal(&mut self, cx: &mut ViewContext<Self>) {
90 self.show_terminal = !self.show_terminal;
91
92 cx.notify();
93 }
94
95 pub fn is_chat_panel_open(&self) -> bool {
96 self.show_chat_panel
97 }
98
99 pub fn toggle_chat_panel(&mut self, cx: &mut ViewContext<Self>) {
100 self.show_chat_panel = !self.show_chat_panel;
101
102 self.show_assistant_panel = false;
103 self.show_notifications_panel = false;
104
105 cx.notify();
106 }
107
108 pub fn is_notifications_panel_open(&self) -> bool {
109 self.show_notifications_panel
110 }
111
112 pub fn toggle_notifications_panel(&mut self, cx: &mut ViewContext<Self>) {
113 self.show_notifications_panel = !self.show_notifications_panel;
114
115 self.show_chat_panel = false;
116 self.show_assistant_panel = false;
117
118 cx.notify();
119 }
120
121 pub fn is_assistant_panel_open(&self) -> bool {
122 self.show_assistant_panel
123 }
124
125 pub fn toggle_assistant_panel(&mut self, cx: &mut ViewContext<Self>) {
126 self.show_assistant_panel = !self.show_assistant_panel;
127
128 self.show_chat_panel = false;
129 self.show_notifications_panel = false;
130
131 cx.notify();
132 }
133
134 pub fn is_language_selector_open(&self) -> bool {
135 self.show_language_selector
136 }
137
138 pub fn toggle_language_selector(&mut self, cx: &mut ViewContext<Self>) {
139 self.show_language_selector = !self.show_language_selector;
140
141 cx.notify();
142 }
143
144 pub fn toggle_debug(&mut self, cx: &mut ViewContext<Self>) {
145 self.show_debug = !self.show_debug;
146
147 cx.notify();
148 }
149
150 pub fn debug_toggle_user_settings(&mut self, cx: &mut ViewContext<Self>) {
151 self.debug.enable_user_settings = !self.debug.enable_user_settings;
152
153 cx.notify();
154 }
155
156 pub fn debug_toggle_livestream(&mut self, cx: &mut ViewContext<Self>) {
157 self.debug.in_livestream = !self.debug.in_livestream;
158
159 self.title_bar = TitleBar::view(
160 cx,
161 Some(static_livestream()).filter(|_| self.debug.in_livestream),
162 );
163
164 cx.notify();
165 }
166
167 pub fn debug_toggle_toast(&mut self, cx: &mut ViewContext<Self>) {
168 self.debug.show_toast = !self.debug.show_toast;
169
170 cx.notify();
171 }
172
173 pub fn view(cx: &mut WindowContext) -> View<Self> {
174 cx.build_view(|cx| Self::new(cx))
175 }
176}
177
178impl Render for Workspace {
179 type Element = Div<Self>;
180
181 fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
182 // HACK: This should happen inside of `debug_toggle_user_settings`, but
183 // we don't have `cx.global::<FakeSettings>()` in event handlers at the moment.
184 // Need to talk with Nathan/Antonio about this.
185 {
186 let settings = user_settings_mut(cx);
187
188 if self.debug.enable_user_settings {
189 settings.list_indent_depth = SettingValue::UserDefined(rems(0.5).into());
190 settings.ui_scale = SettingValue::UserDefined(1.25);
191 } else {
192 *settings = FakeSettings::default();
193 }
194 }
195
196 let root_group = PaneGroup::new_panes(
197 vec![Pane::new(
198 "pane-0",
199 Size {
200 width: relative(1.).into(),
201 height: relative(1.).into(),
202 },
203 )
204 .child(self.editor_1.clone())],
205 SplitDirection::Horizontal,
206 );
207
208 div()
209 .relative()
210 .size_full()
211 .flex()
212 .flex_col()
213 .font("Zed Sans")
214 .gap_0()
215 .justify_start()
216 .items_start()
217 .text_color(cx.theme().colors().text)
218 .bg(cx.theme().colors().background)
219 .child(self.title_bar.clone())
220 .child(
221 div()
222 .flex_1()
223 .w_full()
224 .flex()
225 .flex_row()
226 .overflow_hidden()
227 .border_t()
228 .border_b()
229 .border_color(cx.theme().colors().border)
230 .children(
231 Some(
232 Panel::new("project-panel-outer", cx)
233 .side(PanelSide::Left)
234 .child(ProjectPanel::new("project-panel-inner")),
235 )
236 .filter(|_| self.is_project_panel_open()),
237 )
238 .children(
239 Some(
240 Panel::new("collab-panel-outer", cx)
241 .child(CollabPanel::new("collab-panel-inner"))
242 .side(PanelSide::Left),
243 )
244 .filter(|_| self.is_collab_panel_open()),
245 )
246 // .child(NotificationToast::new(
247 // "maxbrunsfeld has requested to add you as a contact.".into(),
248 // ))
249 .child(
250 v_stack()
251 .flex_1()
252 .h_full()
253 .child(div().flex().flex_1().child(root_group))
254 .children(
255 Some(
256 Panel::new("terminal-panel", cx)
257 .child(Terminal::new())
258 .allowed_sides(PanelAllowedSides::BottomOnly)
259 .side(PanelSide::Bottom),
260 )
261 .filter(|_| self.is_terminal_open()),
262 ),
263 )
264 .children(
265 Some(
266 Panel::new("chat-panel-outer", cx)
267 .side(PanelSide::Right)
268 .child(ChatPanel::new("chat-panel-inner").messages(vec![
269 ChatMessage::new(
270 "osiewicz".to_string(),
271 "is this thing on?".to_string(),
272 DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
273 .unwrap()
274 .naive_local(),
275 ),
276 ChatMessage::new(
277 "maxdeviant".to_string(),
278 "Reading you loud and clear!".to_string(),
279 DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
280 .unwrap()
281 .naive_local(),
282 ),
283 ])),
284 )
285 .filter(|_| self.is_chat_panel_open()),
286 )
287 .children(
288 Some(
289 Panel::new("notifications-panel-outer", cx)
290 .side(PanelSide::Right)
291 .child(NotificationsPanel::new("notifications-panel-inner")),
292 )
293 .filter(|_| self.is_notifications_panel_open()),
294 )
295 .children(
296 Some(
297 Panel::new("assistant-panel-outer", cx)
298 .child(AssistantPanel::new("assistant-panel-inner")),
299 )
300 .filter(|_| self.is_assistant_panel_open()),
301 ),
302 )
303 .child(StatusBar::new())
304 .when(self.debug.show_toast, |this| {
305 this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast")))
306 })
307 .children(
308 Some(
309 div()
310 .absolute()
311 .top(px(50.))
312 .left(px(640.))
313 .z_index(8)
314 .child(LanguageSelector::new("language-selector")),
315 )
316 .filter(|_| self.is_language_selector_open()),
317 )
318 .z_index(8)
319 // Debug
320 .child(
321 v_stack()
322 .z_index(9)
323 .absolute()
324 .bottom_10()
325 .left_1_4()
326 .w_40()
327 .gap_2()
328 .when(self.show_debug, |this| {
329 this.child(Button::<Workspace>::new("Toggle User Settings").on_click(
330 Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)),
331 ))
332 .child(
333 Button::<Workspace>::new("Toggle Toasts").on_click(Arc::new(
334 |workspace, cx| workspace.debug_toggle_toast(cx),
335 )),
336 )
337 .child(
338 Button::<Workspace>::new("Toggle Livestream").on_click(Arc::new(
339 |workspace, cx| workspace.debug_toggle_livestream(cx),
340 )),
341 )
342 })
343 .child(
344 Button::<Workspace>::new("Toggle Debug")
345 .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))),
346 ),
347 )
348 }
349}
350
351#[cfg(feature = "stories")]
352pub use stories::*;
353
354#[cfg(feature = "stories")]
355mod stories {
356 use super::*;
357 use gpui2::VisualContext;
358
359 pub struct WorkspaceStory {
360 workspace: View<Workspace>,
361 }
362
363 impl WorkspaceStory {
364 pub fn view(cx: &mut WindowContext) -> View<Self> {
365 cx.build_view(|cx| Self {
366 workspace: Workspace::view(cx),
367 })
368 }
369 }
370
371 impl Render for WorkspaceStory {
372 type Element = Div<Self>;
373
374 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
375 div().child(self.workspace.clone())
376 }
377 }
378}