1use std::sync::Arc;
2
3use chrono::DateTime;
4use gpui3::{px, relative, rems, view, Context, Size, View};
5
6use crate::prelude::*;
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(
253 v_stack()
254 .flex_1()
255 .h_full()
256 .child(div().flex().flex_1().child(root_group))
257 .children(
258 Some(
259 Panel::new(cx)
260 .child(Terminal::new())
261 .allowed_sides(PanelAllowedSides::BottomOnly)
262 .side(PanelSide::Bottom),
263 )
264 .filter(|_| self.is_terminal_open()),
265 ),
266 )
267 .children(
268 Some(Panel::new(cx).side(PanelSide::Right).child(
269 ChatPanel::new(ScrollState::default()).messages(vec![
270 ChatMessage::new(
271 "osiewicz".to_string(),
272 "is this thing on?".to_string(),
273 DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
274 .unwrap()
275 .naive_local(),
276 ),
277 ChatMessage::new(
278 "maxdeviant".to_string(),
279 "Reading you loud and clear!".to_string(),
280 DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
281 .unwrap()
282 .naive_local(),
283 ),
284 ]),
285 ))
286 .filter(|_| self.is_chat_panel_open()),
287 )
288 .children(
289 Some(
290 Panel::new(cx)
291 .side(PanelSide::Right)
292 .child(div().w_96().h_full().child("Notifications")),
293 )
294 .filter(|_| self.is_notifications_panel_open()),
295 )
296 .children(
297 Some(Panel::new(cx).child(AssistantPanel::new()))
298 .filter(|_| self.is_assistant_panel_open()),
299 ),
300 )
301 .child(StatusBar::new())
302 .when(self.debug.show_toast, |this| {
303 this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast")))
304 })
305 .children(
306 Some(
307 div()
308 .absolute()
309 .top(px(50.))
310 .left(px(640.))
311 .z_index(8)
312 .child(LanguageSelector::new()),
313 )
314 .filter(|_| self.is_language_selector_open()),
315 )
316 .z_index(8)
317 // Debug
318 .child(
319 v_stack()
320 .z_index(9)
321 .absolute()
322 .bottom_10()
323 .left_1_4()
324 .w_40()
325 .gap_2()
326 .when(self.show_debug, |this| {
327 this.child(Button::<Workspace>::new("Toggle User Settings").on_click(
328 Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)),
329 ))
330 .child(
331 Button::<Workspace>::new("Toggle Toasts").on_click(Arc::new(
332 |workspace, cx| workspace.debug_toggle_toast(cx),
333 )),
334 )
335 .child(
336 Button::<Workspace>::new("Toggle Livestream").on_click(Arc::new(
337 |workspace, cx| workspace.debug_toggle_livestream(cx),
338 )),
339 )
340 })
341 .child(
342 Button::<Workspace>::new("Toggle Debug")
343 .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))),
344 ),
345 )
346 }
347}
348
349#[cfg(feature = "stories")]
350pub use stories::*;
351
352#[cfg(feature = "stories")]
353mod stories {
354 use super::*;
355
356 pub struct WorkspaceStory {
357 workspace: View<Workspace>,
358 }
359
360 impl WorkspaceStory {
361 pub fn view(cx: &mut WindowContext) -> View<Self> {
362 view(
363 cx.entity(|cx| Self {
364 workspace: Workspace::view(cx),
365 }),
366 |view, cx| view.workspace.clone(),
367 )
368 }
369 }
370}