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