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 let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
210
211 div()
212 .relative()
213 .size_full()
214 .flex()
215 .flex_col()
216 .font(ui_font)
217 .gap_0()
218 .justify_start()
219 .items_start()
220 .text_color(cx.theme().colors().text)
221 .bg(cx.theme().colors().background)
222 .child(self.title_bar.clone())
223 .child(
224 div()
225 .absolute()
226 .top_12()
227 .left_12()
228 .z_index(99)
229 .bg(cx.theme().colors().background)
230 .child(
231 Checkbox::new("test_checkbox", self.test_checkbox_selection).on_click(
232 |selection, workspace: &mut Workspace, cx| {
233 workspace.test_checkbox_selection = selection;
234
235 cx.notify();
236 },
237 ),
238 ),
239 )
240 .child(
241 div()
242 .flex_1()
243 .w_full()
244 .flex()
245 .flex_row()
246 .overflow_hidden()
247 .border_t()
248 .border_b()
249 .border_color(cx.theme().colors().border)
250 .children(
251 Some(
252 Panel::new("project-panel-outer", cx)
253 .side(PanelSide::Left)
254 .child(ProjectPanel::new("project-panel-inner")),
255 )
256 .filter(|_| self.is_project_panel_open()),
257 )
258 .children(
259 Some(
260 Panel::new("collab-panel-outer", cx)
261 .child(CollabPanel::new("collab-panel-inner"))
262 .side(PanelSide::Left),
263 )
264 .filter(|_| self.is_collab_panel_open()),
265 )
266 // .child(NotificationToast::new(
267 // "maxbrunsfeld has requested to add you as a contact.".into(),
268 // ))
269 .child(
270 v_stack()
271 .flex_1()
272 .h_full()
273 .child(div().flex().flex_1().child(root_group))
274 .children(
275 Some(
276 Panel::new("terminal-panel", cx)
277 .child(Terminal::new())
278 .allowed_sides(PanelAllowedSides::BottomOnly)
279 .side(PanelSide::Bottom),
280 )
281 .filter(|_| self.is_terminal_open()),
282 ),
283 )
284 .children(
285 Some(
286 Panel::new("chat-panel-outer", cx)
287 .side(PanelSide::Right)
288 .child(ChatPanel::new("chat-panel-inner").messages(vec![
289 ChatMessage::new(
290 "osiewicz".to_string(),
291 "is this thing on?".to_string(),
292 DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
293 .unwrap()
294 .naive_local(),
295 ),
296 ChatMessage::new(
297 "maxdeviant".to_string(),
298 "Reading you loud and clear!".to_string(),
299 DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
300 .unwrap()
301 .naive_local(),
302 ),
303 ])),
304 )
305 .filter(|_| self.is_chat_panel_open()),
306 )
307 .children(
308 Some(
309 Panel::new("notifications-panel-outer", cx)
310 .side(PanelSide::Right)
311 .child(NotificationsPanel::new("notifications-panel-inner")),
312 )
313 .filter(|_| self.is_notifications_panel_open()),
314 )
315 .children(
316 Some(
317 Panel::new("assistant-panel-outer", cx)
318 .child(AssistantPanel::new("assistant-panel-inner")),
319 )
320 .filter(|_| self.is_assistant_panel_open()),
321 ),
322 )
323 .child(StatusBar::new())
324 .when(self.debug.show_toast, |this| {
325 this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast")))
326 })
327 .children(
328 Some(
329 div()
330 .absolute()
331 .top(px(50.))
332 .left(px(640.))
333 .z_index(8)
334 .child(LanguageSelector::new("language-selector")),
335 )
336 .filter(|_| self.is_language_selector_open()),
337 )
338 .z_index(8)
339 // Debug
340 .child(
341 v_stack()
342 .z_index(9)
343 .absolute()
344 .top_20()
345 .left_1_4()
346 .w_40()
347 .gap_2()
348 .when(self.show_debug, |this| {
349 this.child(Button::<Workspace>::new("Toggle User Settings").on_click(
350 Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)),
351 ))
352 .child(
353 Button::<Workspace>::new("Toggle Toasts").on_click(Arc::new(
354 |workspace, cx| workspace.debug_toggle_toast(cx),
355 )),
356 )
357 .child(
358 Button::<Workspace>::new("Toggle Livestream").on_click(Arc::new(
359 |workspace, cx| workspace.debug_toggle_livestream(cx),
360 )),
361 )
362 })
363 .child(
364 Button::<Workspace>::new("Toggle Debug")
365 .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))),
366 ),
367 )
368 }
369}
370
371#[cfg(feature = "stories")]
372pub use stories::*;
373
374#[cfg(feature = "stories")]
375mod stories {
376 use super::*;
377 use gpui::VisualContext;
378
379 pub struct WorkspaceStory {
380 workspace: View<Workspace>,
381 }
382
383 impl WorkspaceStory {
384 pub fn view(cx: &mut WindowContext) -> View<Self> {
385 cx.build_view(|cx| Self {
386 workspace: Workspace::view(cx),
387 })
388 }
389 }
390
391 impl Render for WorkspaceStory {
392 type Element = Div<Self>;
393
394 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
395 div().child(self.workspace.clone())
396 }
397 }
398}