element.rs

  1pub use crate::layout_context::LayoutContext;
  2pub use crate::paint_context::PaintContext;
  3use anyhow::Result;
  4use gpui::geometry::vector::Vector2F;
  5pub use gpui::{Layout, LayoutId};
  6use smallvec::SmallVec;
  7
  8pub trait Element<V: 'static>: 'static {
  9    type PaintState;
 10
 11    fn layout(
 12        &mut self,
 13        view: &mut V,
 14        cx: &mut LayoutContext<V>,
 15    ) -> Result<(LayoutId, Self::PaintState)>
 16    where
 17        Self: Sized;
 18
 19    fn paint(
 20        &mut self,
 21        view: &mut V,
 22        layout: &Layout,
 23        state: &mut Self::PaintState,
 24        cx: &mut PaintContext<V>,
 25    ) where
 26        Self: Sized;
 27
 28    fn into_any(self) -> AnyElement<V>
 29    where
 30        Self: 'static + Sized,
 31    {
 32        AnyElement(Box::new(StatefulElement {
 33            element: self,
 34            phase: ElementPhase::Init,
 35        }))
 36    }
 37}
 38
 39/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
 40trait AnyStatefulElement<V> {
 41    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
 42    fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>);
 43}
 44
 45/// A wrapper around an element that stores its layout state.
 46struct StatefulElement<V: 'static, E: Element<V>> {
 47    element: E,
 48    phase: ElementPhase<V, E>,
 49}
 50
 51enum ElementPhase<V: 'static, E: Element<V>> {
 52    Init,
 53    PostLayout {
 54        layout_id: LayoutId,
 55        paint_state: E::PaintState,
 56    },
 57    #[allow(dead_code)]
 58    PostPaint {
 59        layout: Layout,
 60        paint_state: E::PaintState,
 61    },
 62    Error(String),
 63}
 64
 65impl<V: 'static, E: Element<V>> std::fmt::Debug for ElementPhase<V, E> {
 66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 67        match self {
 68            ElementPhase::Init => write!(f, "Init"),
 69            ElementPhase::PostLayout { layout_id, .. } => {
 70                write!(f, "PostLayout with layout id: {:?}", layout_id)
 71            }
 72            ElementPhase::PostPaint { layout, .. } => {
 73                write!(f, "PostPaint with layout: {:?}", layout)
 74            }
 75            ElementPhase::Error(err) => write!(f, "Error: {}", err),
 76        }
 77    }
 78}
 79
 80impl<V: 'static, E: Element<V>> Default for ElementPhase<V, E> {
 81    fn default() -> Self {
 82        Self::Init
 83    }
 84}
 85
 86/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
 87impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
 88    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
 89        let result;
 90        self.phase = match self.element.layout(view, cx) {
 91            Ok((layout_id, paint_state)) => {
 92                result = Ok(layout_id);
 93                ElementPhase::PostLayout {
 94                    layout_id,
 95                    paint_state,
 96                }
 97            }
 98            Err(error) => {
 99                let message = error.to_string();
100                result = Err(error);
101                ElementPhase::Error(message)
102            }
103        };
104        result
105    }
106
107    fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>) {
108        self.phase = match std::mem::take(&mut self.phase) {
109            ElementPhase::PostLayout {
110                layout_id,
111                mut paint_state,
112            } => match cx.computed_layout(layout_id) {
113                Ok(mut layout) => {
114                    layout.bounds = layout.bounds + parent_origin;
115                    self.element.paint(view, &layout, &mut paint_state, cx);
116                    ElementPhase::PostPaint {
117                        layout,
118                        paint_state,
119                    }
120                }
121                Err(error) => ElementPhase::Error(error.to_string()),
122            },
123            ElementPhase::PostPaint {
124                layout,
125                mut paint_state,
126            } => {
127                self.element.paint(view, &layout, &mut paint_state, cx);
128                ElementPhase::PostPaint {
129                    layout,
130                    paint_state,
131                }
132            }
133            phase @ ElementPhase::Error(_) => phase,
134
135            phase @ _ => {
136                panic!("invalid element phase to call paint: {:?}", phase);
137            }
138        };
139    }
140}
141
142/// A dynamic element.
143pub struct AnyElement<V>(Box<dyn AnyStatefulElement<V>>);
144
145impl<V> AnyElement<V> {
146    pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
147        self.0.layout(view, cx)
148    }
149
150    pub fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>) {
151        self.0.paint(view, parent_origin, cx)
152    }
153}
154
155pub trait ParentElement<V: 'static> {
156    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
157
158    fn child(mut self, child: impl IntoElement<V>) -> Self
159    where
160        Self: Sized,
161    {
162        self.children_mut().push(child.into_element().into_any());
163        self
164    }
165
166    fn children<I, E>(mut self, children: I) -> Self
167    where
168        I: IntoIterator<Item = E>,
169        E: IntoElement<V>,
170        Self: Sized,
171    {
172        self.children_mut().extend(
173            children
174                .into_iter()
175                .map(|child| child.into_element().into_any()),
176        );
177        self
178    }
179}
180
181pub trait IntoElement<V: 'static> {
182    type Element: Element<V>;
183
184    fn into_element(self) -> Self::Element;
185}