1use gpui::prelude::*;
6use gpui::*;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum StackSpacing {
11 None,
13 Xs,
15 Sm,
17 #[default]
19 Md,
20 Lg,
22 Xl,
24 Xxl,
26}
27
28impl StackSpacing {
29 fn to_pixels(&self) -> Pixels {
30 match self {
31 StackSpacing::None => px(0.0),
32 StackSpacing::Xs => px(2.0),
33 StackSpacing::Sm => px(4.0),
34 StackSpacing::Md => px(8.0),
35 StackSpacing::Lg => px(16.0),
36 StackSpacing::Xl => px(24.0),
37 StackSpacing::Xxl => px(32.0),
38 }
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
44pub enum StackAlign {
45 Start,
47 #[default]
49 Center,
50 End,
52 Stretch,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
58pub enum StackJustify {
59 #[default]
61 Start,
62 Center,
64 End,
66 SpaceBetween,
68 SpaceAround,
70 SpaceEvenly,
72}
73
74pub struct VStack {
76 children: Vec<AnyElement>,
77 spacing: StackSpacing,
78 align: StackAlign,
79 justify: StackJustify,
80}
81
82impl VStack {
83 pub fn new() -> Self {
85 Self {
86 children: Vec::new(),
87 spacing: StackSpacing::default(),
88 align: StackAlign::Stretch,
89 justify: StackJustify::default(),
90 }
91 }
92
93 pub fn child(mut self, child: impl IntoElement) -> Self {
95 self.children.push(child.into_any_element());
96 self
97 }
98
99 pub fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self {
101 self.children
102 .extend(children.into_iter().map(|c| c.into_any_element()));
103 self
104 }
105
106 pub fn spacing(mut self, spacing: StackSpacing) -> Self {
108 self.spacing = spacing;
109 self
110 }
111
112 pub fn align(mut self, align: StackAlign) -> Self {
114 self.align = align;
115 self
116 }
117
118 pub fn justify(mut self, justify: StackJustify) -> Self {
120 self.justify = justify;
121 self
122 }
123
124 pub fn build(self) -> Div {
126 let mut stack = div().flex().flex_col().gap(self.spacing.to_pixels());
127
128 stack = match self.align {
130 StackAlign::Start => stack.items_start(),
131 StackAlign::Center => stack.items_center(),
132 StackAlign::End => stack.items_end(),
133 StackAlign::Stretch => stack,
134 };
135
136 stack = match self.justify {
138 StackJustify::Start => stack.justify_start(),
139 StackJustify::Center => stack.justify_center(),
140 StackJustify::End => stack.justify_end(),
141 StackJustify::SpaceBetween => stack.justify_between(),
142 StackJustify::SpaceAround => stack.justify_around(),
143 StackJustify::SpaceEvenly => stack,
144 };
145
146 for child in self.children {
147 stack = stack.child(child);
148 }
149
150 stack
151 }
152}
153
154impl Default for VStack {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160impl IntoElement for VStack {
161 type Element = Div;
162
163 fn into_element(self) -> Self::Element {
164 self.build()
165 }
166}
167
168pub struct HStack {
170 children: Vec<AnyElement>,
171 spacing: StackSpacing,
172 align: StackAlign,
173 justify: StackJustify,
174 wrap: bool,
175}
176
177impl HStack {
178 pub fn new() -> Self {
180 Self {
181 children: Vec::new(),
182 spacing: StackSpacing::default(),
183 align: StackAlign::Center,
184 justify: StackJustify::default(),
185 wrap: false,
186 }
187 }
188
189 pub fn child(mut self, child: impl IntoElement) -> Self {
191 self.children.push(child.into_any_element());
192 self
193 }
194
195 pub fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self {
197 self.children
198 .extend(children.into_iter().map(|c| c.into_any_element()));
199 self
200 }
201
202 pub fn spacing(mut self, spacing: StackSpacing) -> Self {
204 self.spacing = spacing;
205 self
206 }
207
208 pub fn align(mut self, align: StackAlign) -> Self {
210 self.align = align;
211 self
212 }
213
214 pub fn justify(mut self, justify: StackJustify) -> Self {
216 self.justify = justify;
217 self
218 }
219
220 pub fn wrap(mut self, wrap: bool) -> Self {
222 self.wrap = wrap;
223 self
224 }
225
226 pub fn build(self) -> Div {
228 let mut stack = div().flex().gap(self.spacing.to_pixels());
229
230 if self.wrap {
231 stack = stack.flex_wrap();
232 }
233
234 stack = match self.align {
236 StackAlign::Start => stack.items_start(),
237 StackAlign::Center => stack.items_center(),
238 StackAlign::End => stack.items_end(),
239 StackAlign::Stretch => stack,
240 };
241
242 stack = match self.justify {
244 StackJustify::Start => stack.justify_start(),
245 StackJustify::Center => stack.justify_center(),
246 StackJustify::End => stack.justify_end(),
247 StackJustify::SpaceBetween => stack.justify_between(),
248 StackJustify::SpaceAround => stack.justify_around(),
249 StackJustify::SpaceEvenly => stack,
250 };
251
252 for child in self.children {
253 stack = stack.child(child);
254 }
255
256 stack
257 }
258}
259
260impl Default for HStack {
261 fn default() -> Self {
262 Self::new()
263 }
264}
265
266impl IntoElement for HStack {
267 type Element = Div;
268
269 fn into_element(self) -> Self::Element {
270 self.build()
271 }
272}
273
274pub struct Spacer;
276
277impl Spacer {
278 pub fn new() -> Self {
280 Self
281 }
282
283 pub fn build(self) -> Div {
285 div().flex_1()
286 }
287}
288
289impl Default for Spacer {
290 fn default() -> Self {
291 Self::new()
292 }
293}
294
295impl IntoElement for Spacer {
296 type Element = Div;
297
298 fn into_element(self) -> Self::Element {
299 self.build()
300 }
301}
302
303pub struct Divider {
305 id: Option<SharedString>,
306 vertical: bool,
307 color: Option<Rgba>,
308 hover_color: Option<Rgba>,
309 thickness: Option<Pixels>,
310 interactive: bool,
311}
312
313impl Divider {
314 pub fn new() -> Self {
316 Self {
317 id: None,
318 vertical: false,
319 color: None,
320 hover_color: None,
321 thickness: None,
322 interactive: false,
323 }
324 }
325
326 pub fn vertical() -> Self {
328 Self {
329 id: None,
330 vertical: true,
331 color: None,
332 hover_color: None,
333 thickness: None,
334 interactive: false,
335 }
336 }
337
338 pub fn id(mut self, id: impl Into<SharedString>) -> Self {
340 self.id = Some(id.into());
341 self
342 }
343
344 pub fn color(mut self, color: Rgba) -> Self {
346 self.color = Some(color);
347 self
348 }
349
350 pub fn hover_color(mut self, color: Rgba) -> Self {
352 self.hover_color = Some(color);
353 self
354 }
355
356 pub fn thickness(mut self, thickness: Pixels) -> Self {
358 self.thickness = Some(thickness);
359 self
360 }
361
362 pub fn interactive(mut self) -> Self {
364 self.interactive = true;
365 self
366 }
367
368 pub fn build(self) -> Stateful<Div> {
371 let color = self.color.unwrap_or(rgb(0x3a3a3a));
372 let id = self.id.unwrap_or_else(|| SharedString::from("divider"));
373
374 let base = if self.vertical {
375 let thickness = self.thickness.unwrap_or(px(1.0));
376 div().id(id).w(thickness).h_full().bg(color)
377 } else {
378 let thickness = self.thickness.unwrap_or(px(1.0));
379 div().id(id).h(thickness).w_full().bg(color)
380 };
381
382 if self.interactive {
383 let hover_color = self.hover_color.unwrap_or(rgb(0x007acc));
384 let cursor = if self.vertical {
385 gpui::CursorStyle::ResizeLeftRight
386 } else {
387 gpui::CursorStyle::ResizeUpDown
388 };
389 base.cursor(cursor)
390 .hover(move |style| style.bg(hover_color))
391 } else {
392 base
393 }
394 }
395
396 pub fn build_simple(self) -> Div {
398 let color = self.color.unwrap_or(rgb(0x3a3a3a));
399
400 if self.vertical {
401 let thickness = self.thickness.unwrap_or(px(1.0));
402 div().w(thickness).h_full().bg(color)
403 } else {
404 let thickness = self.thickness.unwrap_or(px(1.0));
405 div().h(thickness).w_full().bg(color)
406 }
407 }
408}
409
410impl Default for Divider {
411 fn default() -> Self {
412 Self::new()
413 }
414}
415
416impl IntoElement for Divider {
417 type Element = Stateful<Div>;
418
419 fn into_element(self) -> Self::Element {
420 self.build()
421 }
422}