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