elements.rs

  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}