element.rs

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