1use chrono::DateTime;
2use gpui3::{px, relative, view, Context, Size, View};
3
4use crate::prelude::*;
5use crate::{
6 theme, v_stack, AssistantPanel, ChatMessage, ChatPanel, CollabPanel, EditorPane, Label,
7 LanguageSelector, Pane, PaneGroup, Panel, PanelAllowedSides, PanelSide, ProjectPanel,
8 SplitDirection, StatusBar, Terminal, TitleBar, Toast, ToastOrigin,
9};
10
11#[derive(Clone)]
12pub struct Workspace {
13 title_bar: View<TitleBar>,
14 editor_1: View<EditorPane>,
15 show_project_panel: bool,
16 show_collab_panel: bool,
17 show_chat_panel: bool,
18 show_assistant_panel: bool,
19 show_notifications_panel: bool,
20 show_terminal: bool,
21 show_language_selector: bool,
22 left_panel_scroll_state: ScrollState,
23 right_panel_scroll_state: ScrollState,
24 tab_bar_scroll_state: ScrollState,
25 bottom_panel_scroll_state: ScrollState,
26}
27
28impl Workspace {
29 pub fn new(cx: &mut ViewContext<Self>) -> Self {
30 Self {
31 title_bar: TitleBar::view(cx),
32 editor_1: EditorPane::view(cx),
33 show_project_panel: true,
34 show_collab_panel: false,
35 show_chat_panel: false,
36 show_assistant_panel: false,
37 show_terminal: true,
38 show_language_selector: false,
39 show_notifications_panel: true,
40 left_panel_scroll_state: ScrollState::default(),
41 right_panel_scroll_state: ScrollState::default(),
42 tab_bar_scroll_state: ScrollState::default(),
43 bottom_panel_scroll_state: ScrollState::default(),
44 }
45 }
46
47 pub fn is_project_panel_open(&self) -> bool {
48 self.show_project_panel
49 }
50
51 pub fn toggle_project_panel(&mut self, cx: &mut ViewContext<Self>) {
52 self.show_project_panel = !self.show_project_panel;
53
54 self.show_collab_panel = false;
55
56 cx.notify();
57 }
58
59 pub fn is_collab_panel_open(&self) -> bool {
60 self.show_collab_panel
61 }
62
63 pub fn toggle_collab_panel(&mut self) {
64 self.show_collab_panel = !self.show_collab_panel;
65
66 self.show_project_panel = false;
67 }
68
69 pub fn is_terminal_open(&self) -> bool {
70 self.show_terminal
71 }
72
73 pub fn toggle_terminal(&mut self, cx: &mut ViewContext<Self>) {
74 self.show_terminal = !self.show_terminal;
75
76 cx.notify();
77 }
78
79 pub fn is_chat_panel_open(&self) -> bool {
80 self.show_chat_panel
81 }
82
83 pub fn toggle_chat_panel(&mut self, cx: &mut ViewContext<Self>) {
84 self.show_chat_panel = !self.show_chat_panel;
85
86 self.show_assistant_panel = false;
87
88 cx.notify();
89 }
90
91 pub fn is_notifications_panel_open(&self) -> bool {
92 self.show_notifications_panel
93 }
94
95 pub fn toggle_notifications_panel(&mut self, cx: &mut ViewContext<Self>) {
96 self.show_notifications_panel = !self.show_notifications_panel;
97
98 self.show_notifications_panel = false;
99
100 cx.notify();
101 }
102
103 pub fn is_assistant_panel_open(&self) -> bool {
104 self.show_assistant_panel
105 }
106
107 pub fn toggle_assistant_panel(&mut self, cx: &mut ViewContext<Self>) {
108 self.show_assistant_panel = !self.show_assistant_panel;
109
110 self.show_chat_panel = false;
111
112 cx.notify();
113 }
114
115 pub fn is_language_selector_open(&self) -> bool {
116 self.show_language_selector
117 }
118
119 pub fn toggle_language_selector(&mut self, cx: &mut ViewContext<Self>) {
120 self.show_language_selector = !self.show_language_selector;
121
122 cx.notify();
123 }
124
125 pub fn view(cx: &mut WindowContext) -> View<Self> {
126 view(cx.entity(|cx| Self::new(cx)), Self::render)
127 }
128
129 pub fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<ViewState = Self> {
130 let theme = theme(cx).clone();
131
132 let root_group = PaneGroup::new_panes(
133 vec![Pane::new(
134 ScrollState::default(),
135 Size {
136 width: relative(1.).into(),
137 height: relative(1.).into(),
138 },
139 )
140 .child(self.editor_1.clone())],
141 SplitDirection::Horizontal,
142 );
143
144 div()
145 .relative()
146 .size_full()
147 .flex()
148 .flex_col()
149 .font("Zed Sans Extended")
150 .gap_0()
151 .justify_start()
152 .items_start()
153 .text_color(theme.lowest.base.default.foreground)
154 .bg(theme.lowest.base.default.background)
155 .child(self.title_bar.clone())
156 .child(
157 div()
158 .flex_1()
159 .w_full()
160 .flex()
161 .flex_row()
162 .overflow_hidden()
163 .border_t()
164 .border_b()
165 .border_color(theme.lowest.base.default.border)
166 .children(
167 Some(
168 Panel::new(self.left_panel_scroll_state.clone())
169 .side(PanelSide::Left)
170 .child(ProjectPanel::new(ScrollState::default())),
171 )
172 .filter(|_| self.is_project_panel_open()),
173 )
174 .children(
175 Some(
176 Panel::new(self.left_panel_scroll_state.clone())
177 .child(CollabPanel::new(ScrollState::default()))
178 .side(PanelSide::Left),
179 )
180 .filter(|_| self.is_collab_panel_open()),
181 )
182 .child(
183 v_stack()
184 .flex_1()
185 .h_full()
186 .child(
187 div()
188 .flex()
189 .flex_1()
190 // CSS Hack: Flex 1 has to have a set height to properly fill the space
191 // Or it will give you a height of 0
192 // Marshall: We may not need this anymore with `gpui3`. It seems to render
193 // fine without it.
194 .h_px()
195 .child(root_group),
196 )
197 .children(
198 Some(
199 Panel::new(self.bottom_panel_scroll_state.clone())
200 .child(Terminal::new())
201 .allowed_sides(PanelAllowedSides::BottomOnly)
202 .side(PanelSide::Bottom),
203 )
204 .filter(|_| self.is_terminal_open()),
205 ),
206 )
207 .children(
208 Some(
209 Panel::new(self.right_panel_scroll_state.clone())
210 .side(PanelSide::Right)
211 .child(ChatPanel::new(ScrollState::default()).messages(vec![
212 ChatMessage::new(
213 "osiewicz".to_string(),
214 "is this thing on?".to_string(),
215 DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
216 .unwrap()
217 .naive_local(),
218 ),
219 ChatMessage::new(
220 "maxdeviant".to_string(),
221 "Reading you loud and clear!".to_string(),
222 DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
223 .unwrap()
224 .naive_local(),
225 ),
226 ])),
227 )
228 .filter(|_| self.is_chat_panel_open()),
229 )
230 .children(
231 Some(
232 Panel::new(self.right_panel_scroll_state.clone())
233 .side(PanelSide::Right)
234 .child(div().w_96().h_full().child("Notifications")),
235 )
236 .filter(|_| self.is_notifications_panel_open()),
237 )
238 .children(
239 Some(
240 Panel::new(self.right_panel_scroll_state.clone())
241 .child(AssistantPanel::new()),
242 )
243 .filter(|_| self.is_assistant_panel_open()),
244 ),
245 )
246 .child(StatusBar::new())
247 .children(
248 Some(
249 div()
250 .absolute()
251 .top(px(50.))
252 .left(px(640.))
253 .z_index(999)
254 .child(LanguageSelector::new()),
255 )
256 .filter(|_| self.is_language_selector_open()),
257 )
258 .child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast")))
259 // .child(Toast::new(ToastOrigin::BottomRight).child(Label::new("Another toast")))
260 // .child(NotificationToast::new(
261 // "Can't pull changes from origin",
262 // "Your local branch is behind the remote branch. Please pull the latest changes before pushing.",
263 // Button::new("Stash & Switch").variant(ButtonVariant::Filled),
264 // ).secondary_action(Button::new("Cancel")))
265 }
266}
267
268#[cfg(feature = "stories")]
269pub use stories::*;
270
271#[cfg(feature = "stories")]
272mod stories {
273 use super::*;
274
275 pub struct WorkspaceStory {
276 workspace: View<Workspace>,
277 }
278
279 impl WorkspaceStory {
280 pub fn view(cx: &mut WindowContext) -> View<Self> {
281 view(
282 cx.entity(|cx| Self {
283 workspace: Workspace::view(cx),
284 }),
285 |view, cx| view.workspace.clone(),
286 )
287 }
288 }
289}