element.rs

  1pub use crate::ViewContext;
  2use anyhow::Result;
  3use gpui::geometry::vector::Vector2F;
  4pub use gpui::{Layout, LayoutId};
  5use smallvec::SmallVec;
  6
  7pub trait Element<V: 'static>: 'static + IntoElement<V> {
  8    type PaintState;
  9
 10    fn layout(
 11        &mut self,
 12        view: &mut V,
 13        cx: &mut ViewContext<V>,
 14    ) -> Result<(LayoutId, Self::PaintState)>
 15    where
 16        Self: Sized;
 17
 18    fn paint(
 19        &mut self,
 20        view: &mut V,
 21        parent_origin: Vector2F,
 22        layout: &Layout,
 23        state: &mut Self::PaintState,
 24        cx: &mut ViewContext<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 ViewContext<V>) -> Result<LayoutId>;
 42    fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<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 ViewContext<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 ViewContext<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(layout) => {
114                    self.element
115                        .paint(view, parent_origin, &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
128                    .paint(view, parent_origin, &layout, &mut paint_state, cx);
129                ElementPhase::PostPaint {
130                    layout,
131                    paint_state,
132                }
133            }
134            phase @ ElementPhase::Error(_) => phase,
135
136            phase @ _ => {
137                panic!("invalid element phase to call paint: {:?}", phase);
138            }
139        };
140    }
141}
142
143/// A dynamic element.
144pub struct AnyElement<V>(Box<dyn AnyStatefulElement<V>>);
145
146impl<V> AnyElement<V> {
147    pub fn layout(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> Result<LayoutId> {
148        self.0.layout(view, cx)
149    }
150
151    pub fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>) {
152        self.0.paint(view, parent_origin, cx)
153    }
154}
155
156pub trait ParentElement<V: 'static> {
157    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
158
159    fn child(mut self, child: impl IntoElement<V>) -> Self
160    where
161        Self: Sized,
162    {
163        self.children_mut().push(child.into_element().into_any());
164        self
165    }
166
167    fn children<I, E>(mut self, children: I) -> Self
168    where
169        I: IntoIterator<Item = E>,
170        E: IntoElement<V>,
171        Self: Sized,
172    {
173        self.children_mut().extend(
174            children
175                .into_iter()
176                .map(|child| child.into_element().into_any()),
177        );
178        self
179    }
180}
181
182pub trait IntoElement<V: 'static> {
183    type Element: Element<V>;
184
185    fn into_element(self) -> Self::Element;
186}