element.rs

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