1use gpui::{
2 color::Color,
3 geometry::{
4 rect::RectF,
5 vector::{vec2f, Vector2F},
6 },
7 json::{json, ToJson},
8 scene,
9 serde_json::Value,
10 AnyElement, Element, LayoutContext, Quad, SceneBuilder, SizeConstraint, View, ViewContext,
11};
12use std::{any::Any, ops::Range};
13
14// Core idea is that everything is a channel, and channels are heirarchical.
15//
16// Tree 🌲 of channels
17// - (Potentially v0.2) All channels associated with a conversation (Slack model)
18// - Audio
19// - You can share projects into the channel
20// - 1.
21//
22//
23// - 2 thoughts:
24// - Difference from where we are to the above:
25// - Channels = rooms + chat + persistence
26// - Chat = multiplayer assistant panel + server integrated persistence
27// - The tree structure, is good for navigating chats, AND it's good for distributing permissions.
28// #zed-public// /zed- <- Share a pointer (URL) for this
29//
30//
31
32pub struct Node<V: View> {
33 style: NodeStyle,
34 children: Vec<AnyElement<V>>,
35}
36
37impl<V: View> Default for Node<V> {
38 fn default() -> Self {
39 Self {
40 style: Default::default(),
41 children: Default::default(),
42 }
43 }
44}
45
46impl<V: View> Node<V> {
47 pub fn new() -> Self {
48 Self::default()
49 }
50
51 pub fn child(mut self, child: impl Element<V>) -> Self {
52 self.children.push(child.into_any());
53 self
54 }
55
56 pub fn children<I, E>(mut self, children: I) -> Self
57 where
58 I: IntoIterator<Item = E>,
59 E: Element<V>,
60 {
61 self.children
62 .extend(children.into_iter().map(|child| child.into_any()));
63 self
64 }
65
66 pub fn width(mut self, width: impl Into<Length>) -> Self {
67 self.style.width = width.into();
68 self
69 }
70
71 pub fn height(mut self, height: impl Into<Length>) -> Self {
72 self.style.height = height.into();
73 self
74 }
75
76 pub fn fill(mut self, fill: impl Into<Fill>) -> Self {
77 self.style.fill = fill.into();
78 self
79 }
80
81 fn layout_2d_children(
82 &mut self,
83 axis: Axis2d,
84 size: Vector2F,
85 view: &mut V,
86 cx: &mut LayoutContext<V>,
87 ) -> Vector2F {
88 let mut total_flex: Option<f32> = None;
89 let mut total_size = 0.0;
90 let mut cross_axis_max: f32 = 0.0;
91
92 // First pass: Layout non-flex children only
93 for child in &mut self.children {
94 let child_flex = child.metadata::<NodeStyle>().and_then(|style| match axis {
95 Axis2d::X => style.width.flex(),
96 Axis2d::Y => style.height.flex(),
97 });
98
99 if let Some(child_flex) = child_flex {
100 *total_flex.get_or_insert(0.) += child_flex;
101 } else {
102 match axis {
103 Axis2d::X => {
104 let child_constraint =
105 SizeConstraint::new(Vector2F::zero(), vec2f(f32::INFINITY, size.y()));
106 let child_size = child.layout(child_constraint, view, cx);
107 cross_axis_max = cross_axis_max.max(child_size.y());
108 total_size += child_size.x();
109 }
110 Axis2d::Y => {
111 let child_constraint =
112 SizeConstraint::new(Vector2F::zero(), vec2f(size.x(), f32::INFINITY));
113 let child_size = child.layout(child_constraint, view, cx);
114 cross_axis_max = cross_axis_max.max(child_size.x());
115 total_size += child_size.y();
116 }
117 }
118 }
119 }
120
121 // let remaining_space = match axis {
122 // Axis2d::X => constraint.max.x() - total_size,
123 // Axis2d::Y => constraint.max.y() - total_size,
124 // };
125
126 // // Second pass: Layout flexible children
127 // if let Some(total_flex) = total_flex {
128 // if total_flex > 0. {
129 // let space_per_flex = remaining_space.max(0.) / total_flex;
130
131 // for child in &mut self.children {
132 // if let Some(child_flex) =
133 // child.metadata::<AtomStyle>().and_then(|style| style.flex)
134 // {
135 // let child_max = space_per_flex * child_flex;
136 // let mut child_constraint = constraint;
137 // match axis {
138 // Axis3d::Vertical => {
139 // child_constraint.min.set_y(0.0);
140 // child_constraint.max.set_y(child_max);
141 // }
142 // Axis3d::Horizontal => {
143 // child_constraint.min.set_x(0.0);
144 // child_constraint.max.set_x(child_max);
145 // }
146 // }
147
148 // let child_size = child.layout(child_constraint, view, cx);
149
150 // cross_axis_max = match axis {
151 // Axis3d::Vertical => {
152 // total_size += child_size.y();
153 // cross_axis_max.max(child_size.x())
154 // }
155 // Axis3d::Horizontal => {
156 // total_size += child_size.x();
157 // cross_axis_max.max(child_size.y())
158 // }
159 // };
160 // }
161 // }
162 // }
163 // }
164
165 let size = match axis {
166 Axis2d::X => vec2f(total_size, cross_axis_max),
167 Axis2d::Y => vec2f(cross_axis_max, total_size),
168 };
169 size
170 }
171
172 fn paint_2d_children(
173 &mut self,
174 scene: &mut SceneBuilder,
175 axis: Axis2d,
176 bounds: RectF,
177 visible_bounds: RectF,
178 size_of_children: &mut Vector2F,
179 view: &mut V,
180 cx: &mut ViewContext<V>,
181 ) {
182 let parent_size = bounds.size();
183 let mut child_origin = bounds.origin();
184
185 // Align all children together along the primary axis
186 let mut align_horizontally = false;
187 let mut align_vertically = false;
188 match axis {
189 Axis2d::X => align_horizontally = true,
190 Axis2d::Y => align_vertically = true,
191 }
192 align_child(
193 &mut child_origin,
194 parent_size,
195 *size_of_children,
196 self.style.align.0,
197 align_horizontally,
198 align_vertically,
199 );
200
201 for child in &mut self.children {
202 // Align each child along the cross axis
203 align_horizontally = !align_horizontally;
204 align_vertically = !align_vertically;
205 align_child(
206 &mut child_origin,
207 parent_size,
208 child.size(),
209 self.style.align.0,
210 align_horizontally,
211 align_vertically,
212 );
213
214 child.paint(scene, child_origin, visible_bounds, view, cx);
215
216 // Advance along the primary axis by the size of this child
217 match axis {
218 Axis2d::X => child_origin.set_x(child_origin.x() + child.size().x()),
219 Axis2d::Y => child_origin.set_y(child_origin.x() + child.size().y()),
220 }
221 }
222 }
223
224 // fn layout_stacked_children(
225 // &mut self,
226 // constraint: SizeConstraint,
227 // view: &mut V,
228 // cx: &mut LayoutContext<V>,
229 // ) -> Vector2F {
230 // let mut size = Vector2F::zero();
231
232 // for child in &mut self.children {
233 // let child_size = child.layout(constraint, view, cx);
234 // size.set_x(size.x().max(child_size.x()));
235 // size.set_y(size.y().max(child_size.y()));
236 // }
237
238 // size
239 // }
240
241 fn inset_size(&self) -> Vector2F {
242 self.padding_size() + self.border_size() + self.margin_size()
243 }
244
245 fn margin_size(&self) -> Vector2F {
246 vec2f(
247 self.style.margin.left + self.style.margin.right,
248 self.style.margin.top + self.style.margin.bottom,
249 )
250 }
251
252 fn padding_size(&self) -> Vector2F {
253 vec2f(
254 self.style.padding.left + self.style.padding.right,
255 self.style.padding.top + self.style.padding.bottom,
256 )
257 }
258
259 fn border_size(&self) -> Vector2F {
260 let mut x = 0.0;
261 if self.style.border.left {
262 x += self.style.border.width;
263 }
264 if self.style.border.right {
265 x += self.style.border.width;
266 }
267
268 let mut y = 0.0;
269 if self.style.border.top {
270 y += self.style.border.width;
271 }
272 if self.style.border.bottom {
273 y += self.style.border.width;
274 }
275
276 vec2f(x, y)
277 }
278}
279
280impl<V: View> Element<V> for Node<V> {
281 type LayoutState = Vector2F; // Content size
282 type PaintState = ();
283
284 fn layout(
285 &mut self,
286 constraint: SizeConstraint,
287 view: &mut V,
288 cx: &mut LayoutContext<V>,
289 ) -> (Vector2F, Self::LayoutState) {
290 let mut size = Vector2F::zero();
291 let margin_size = self.margin_size();
292 match self.style.width {
293 Length::Fixed(width) => size.set_x(width + margin_size.x()),
294 Length::Auto { flex, min, max } => {
295 todo!()
296 }
297 }
298 match self.style.height {
299 Length::Fixed(height) => size.set_y(height + margin_size.y()),
300 Length::Auto { flex, min, max } => todo!(),
301 }
302
303 // Impose horizontal constraints
304 if constraint.min.x().is_finite() {
305 size.set_x(size.x().max(constraint.min.x()));
306 }
307 size.set_x(size.x().min(constraint.max.x()));
308
309 // Impose vertical constraints
310 if constraint.min.y().is_finite() {
311 size.set_y(size.y().max(constraint.min.y()));
312 }
313 size.set_x(size.y().min(constraint.max.y()));
314
315 let inner_size = size - margin_size - self.border_size() - self.padding_size();
316 let size_of_children = match self.style.axis {
317 Axis3d::X => self.layout_2d_children(Axis2d::X, inner_size, view, cx),
318 Axis3d::Y => self.layout_2d_children(Axis2d::Y, inner_size, view, cx),
319 Axis3d::Z => todo!(), // self.layout_stacked_children(inner_constraint, view, cx),
320 };
321
322 (dbg!(size), dbg!(size_of_children))
323 }
324
325 fn paint(
326 &mut self,
327 scene: &mut SceneBuilder,
328 bounds: RectF,
329 visible_bounds: RectF,
330 size_of_children: &mut Vector2F,
331 view: &mut V,
332 cx: &mut ViewContext<V>,
333 ) -> Self::PaintState {
334 let margin = &self.style.margin;
335
336 // Account for margins
337 let content_bounds = RectF::from_points(
338 bounds.origin() + vec2f(margin.left, margin.top),
339 bounds.lower_right() - vec2f(margin.right, margin.bottom),
340 );
341
342 // Paint drop shadow
343 for shadow in &self.style.shadows {
344 scene.push_shadow(scene::Shadow {
345 bounds: content_bounds + shadow.offset,
346 corner_radius: self.style.corner_radius,
347 sigma: shadow.blur,
348 color: shadow.color,
349 });
350 }
351
352 // // Paint cursor style
353 // if let Some(hit_bounds) = content_bounds.intersection(visible_bounds) {
354 // if let Some(style) = self.style.cursor {
355 // scene.push_cursor_region(CursorRegion {
356 // bounds: hit_bounds,
357 // style,
358 // });
359 // }
360 // }
361
362 // Render the background and/or the border (if it not an overlay border).
363 let Fill::Color(fill_color) = self.style.fill;
364 let is_fill_visible = !fill_color.is_fully_transparent();
365 if is_fill_visible || self.style.border.is_visible() {
366 scene.push_quad(Quad {
367 bounds: content_bounds,
368 background: is_fill_visible.then_some(fill_color),
369 border: scene::Border {
370 width: self.style.border.width,
371 color: self.style.border.color,
372 overlay: false,
373 top: self.style.border.top,
374 right: self.style.border.right,
375 bottom: self.style.border.bottom,
376 left: self.style.border.left,
377 },
378 corner_radius: self.style.corner_radius,
379 });
380 }
381
382 if !self.children.is_empty() {
383 // Account for padding first.
384 let padding = &self.style.padding;
385 let padded_bounds = RectF::from_points(
386 content_bounds.origin() + vec2f(padding.left, padding.top),
387 content_bounds.lower_right() - vec2f(padding.right, padding.top),
388 );
389
390 match self.style.axis {
391 Axis3d::X => self.paint_2d_children(
392 scene,
393 Axis2d::X,
394 padded_bounds,
395 visible_bounds,
396 size_of_children,
397 view,
398 cx,
399 ),
400 Axis3d::Y => self.paint_2d_children(
401 scene,
402 Axis2d::Y,
403 padded_bounds,
404 visible_bounds,
405 size_of_children,
406 view,
407 cx,
408 ),
409 Axis3d::Z => todo!(),
410 }
411
412 // match self.style.orientation {
413 // Orientation::Axial(axis) => {
414 // let mut child_origin = padded_bounds.origin();
415 // // Align all children together along the primary axis
416 // match axis {
417 // Axis3d::Horizontal => align_child(
418 // &mut child_origin,
419 // parent_size,
420 // *size_of_children,
421 // child_aligment,
422 // true,
423 // false,
424 // ),
425 // Axis3d::Vertical => align_child(
426 // &mut child_origin,
427 // parent_size,
428 // *size_of_children,
429 // child_aligment,
430 // false,
431 // true,
432 // ),
433 // };
434
435 // for child in &mut self.children {
436 // // Align each child along the cross axis
437 // match axis {
438 // Axis3d::Horizontal => {
439 // child_origin.set_y(padded_bounds.origin_y());
440 // align_child(
441 // &mut child_origin,
442 // parent_size,
443 // child.size(),
444 // child_aligment,
445 // false,
446 // true,
447 // );
448 // }
449 // Axis3d::Vertical => {
450 // child_origin.set_x(padded_bounds.origin_x());
451 // align_child(
452 // &mut child_origin,
453 // parent_size,
454 // child.size(),
455 // child_aligment,
456 // true,
457 // false,
458 // );
459 // }
460 // }
461
462 // child.paint(scene, child_origin, visible_bounds, view, cx);
463
464 // // Advance along the cross axis by the size of this child
465 // match axis {
466 // Axis3d::Horizontal => {
467 // child_origin.set_x(child_origin.x() + child.size().x())
468 // }
469 // Axis3d::Vertical => {
470 // child_origin.set_y(child_origin.x() + child.size().y())
471 // }
472 // }
473 // }
474 // }
475 // }
476 }
477 }
478
479 fn rect_for_text_range(
480 &self,
481 range_utf16: Range<usize>,
482 _: RectF,
483 _: RectF,
484 _: &Self::LayoutState,
485 _: &Self::PaintState,
486 view: &V,
487 cx: &ViewContext<V>,
488 ) -> Option<RectF> {
489 self.children
490 .iter()
491 .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
492 }
493
494 fn debug(
495 &self,
496 bounds: RectF,
497 _: &Self::LayoutState,
498 _: &Self::PaintState,
499 view: &V,
500 cx: &ViewContext<V>,
501 ) -> Value {
502 json!({
503 "type": "Cell",
504 "bounds": bounds.to_json(),
505 "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<Value>>()
506 })
507 }
508
509 fn metadata(&self) -> Option<&dyn Any> {
510 Some(&self.style)
511 }
512}
513
514fn align_child(
515 child_origin: &mut Vector2F,
516 parent_size: Vector2F,
517 child_size: Vector2F,
518 alignment: Vector2F,
519 horizontal: bool,
520 vertical: bool,
521) {
522 let parent_center = parent_size / 2.;
523 let parent_target = parent_center + parent_center * alignment;
524 let child_center = child_size / 2.;
525 let child_target = child_center + child_center * alignment;
526
527 if horizontal {
528 child_origin.set_x(child_origin.x() + parent_target.x() - child_target.x())
529 }
530 if vertical {
531 child_origin.set_y(child_origin.y() + parent_target.y() - child_target.y());
532 }
533}
534
535struct Interactive<Style> {
536 default: Style,
537 hovered: Style,
538 active: Style,
539 disabled: Style,
540}
541
542#[derive(Clone, Default)]
543pub struct NodeStyle {
544 axis: Axis3d,
545 wrap: bool,
546 align: Align,
547 overflow_x: Overflow,
548 overflow_y: Overflow,
549 gap_x: Gap,
550 gap_y: Gap,
551
552 width: Length,
553 height: Length,
554 margin: Edges<f32>,
555 padding: Edges<f32>,
556
557 text_color: Option<Color>,
558 font_size: Option<f32>,
559 font_style: Option<FontStyle>,
560 font_weight: Option<FontWeight>,
561
562 opacity: f32,
563 fill: Fill,
564 border: Border,
565 corner_radius: f32, // corner radius matches swift!
566 shadows: Vec<Shadow>,
567}
568
569// Sides?
570#[derive(Clone, Default)]
571struct Edges<T> {
572 top: T,
573 bottom: T,
574 left: T,
575 right: T,
576}
577
578#[derive(Clone, Default)]
579struct CornerRadii {
580 top_left: f32,
581 top_right: f32,
582 bottom_right: f32,
583 bottom_left: f32,
584}
585
586#[derive(Clone)]
587pub enum Fill {
588 Color(Color),
589}
590
591impl From<Color> for Fill {
592 fn from(value: Color) -> Self {
593 Fill::Color(value)
594 }
595}
596
597impl Default for Fill {
598 fn default() -> Self {
599 Fill::Color(Color::default())
600 }
601}
602
603#[derive(Clone, Default)]
604struct Border {
605 color: Color,
606 width: f32,
607 top: bool,
608 bottom: bool,
609 left: bool,
610 right: bool,
611}
612
613impl Border {
614 fn is_visible(&self) -> bool {
615 self.width > 0.
616 && !self.color.is_fully_transparent()
617 && (self.top || self.bottom || self.left || self.right)
618 }
619}
620
621#[derive(Clone, Copy)]
622pub enum Length {
623 Fixed(f32),
624 Auto { flex: f32, min: f32, max: f32 },
625}
626
627impl Default for Length {
628 fn default() -> Self {
629 Length::Auto {
630 flex: 1.,
631 min: 0.,
632 max: f32::INFINITY,
633 }
634 }
635}
636
637impl From<f32> for Length {
638 fn from(value: f32) -> Self {
639 Length::Fixed(value)
640 }
641}
642
643impl Length {
644 pub fn max(&self) -> f32 {
645 match self {
646 Length::Fixed(value) => *value,
647 Length::Auto { max, .. } => *max,
648 }
649 }
650
651 pub fn flex(&self) -> Option<f32> {
652 match self {
653 Length::Fixed(_) => None,
654 Length::Auto { flex, .. } => Some(*flex),
655 }
656 }
657}
658
659#[derive(Clone)]
660struct Align(Vector2F);
661
662impl Default for Align {
663 fn default() -> Self {
664 Self(vec2f(-1., -1.))
665 }
666}
667
668#[derive(Clone, Copy, Default)]
669enum Axis3d {
670 X,
671 #[default]
672 Y,
673 Z,
674}
675
676impl Axis3d {
677 fn to_2d(self) -> Option<Axis2d> {
678 match self {
679 Axis3d::X => Some(Axis2d::X),
680 Axis3d::Y => Some(Axis2d::Y),
681 Axis3d::Z => None,
682 }
683 }
684}
685
686#[derive(Clone, Copy, Default)]
687enum Axis2d {
688 X,
689 #[default]
690 Y,
691}
692
693#[derive(Clone, Copy, Default)]
694enum Overflow {
695 #[default]
696 Visible,
697 Hidden,
698 Auto,
699}
700
701#[derive(Clone, Copy)]
702enum Gap {
703 Fixed(f32),
704 Around,
705 Between,
706 Even,
707}
708
709impl Default for Gap {
710 fn default() -> Self {
711 Gap::Fixed(0.)
712 }
713}
714
715#[derive(Clone, Copy, Default)]
716struct Shadow {
717 offset: Vector2F,
718 blur: f32,
719 color: Color,
720}
721
722#[derive(Clone, Copy, Default)]
723enum FontStyle {
724 #[default]
725 Normal,
726 Italic,
727 Oblique,
728}
729
730#[derive(Clone, Copy, Default)]
731enum FontWeight {
732 Thin,
733 ExtraLight,
734 Light,
735 #[default]
736 Normal,
737 Medium,
738 Semibold,
739 Bold,
740 ExtraBold,
741 Black,
742}