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