element.rs

  1use super::{Layout, LayoutId, Pixels, Point, Result, ViewContext};
  2pub(crate) use smallvec::SmallVec;
  3
  4pub trait Element: 'static {
  5    type State;
  6    type FrameState;
  7
  8    fn layout(
  9        &mut self,
 10        state: &mut Self::State,
 11        cx: &mut ViewContext<Self::State>,
 12    ) -> Result<(LayoutId, Self::FrameState)>;
 13
 14    fn paint(
 15        &mut self,
 16        layout: Layout,
 17        state: &mut Self::State,
 18        frame_state: &mut Self::FrameState,
 19        cx: &mut ViewContext<Self::State>,
 20    ) -> Result<()>;
 21}
 22
 23pub trait ParentElement<S> {
 24    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<S>; 2]>;
 25
 26    fn child(mut self, child: impl IntoAnyElement<S>) -> Self
 27    where
 28        Self: Sized,
 29    {
 30        self.children_mut().push(child.into_any());
 31        self
 32    }
 33
 34    fn children(mut self, iter: impl IntoIterator<Item = impl IntoAnyElement<S>>) -> Self
 35    where
 36        Self: Sized,
 37    {
 38        self.children_mut()
 39            .extend(iter.into_iter().map(|item| item.into_any()));
 40        self
 41    }
 42
 43    // HACK: This is a temporary hack to get children working for the purposes
 44    // of building UI on top of the current version of gpui2.
 45    //
 46    // We'll (hopefully) be moving away from this in the future.
 47    fn children_any<I>(mut self, children: I) -> Self
 48    where
 49        I: IntoIterator<Item = AnyElement<S>>,
 50        Self: Sized,
 51    {
 52        self.children_mut().extend(children.into_iter());
 53        self
 54    }
 55
 56    // HACK: This is a temporary hack to get children working for the purposes
 57    // of building UI on top of the current version of gpui2.
 58    //
 59    // We'll (hopefully) be moving away from this in the future.
 60    fn child_any(mut self, children: AnyElement<S>) -> Self
 61    where
 62        Self: Sized,
 63    {
 64        self.children_mut().push(children);
 65        self
 66    }
 67}
 68
 69trait ElementObject<S> {
 70    fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId>;
 71    fn paint(
 72        &mut self,
 73        state: &mut S,
 74        offset: Option<Point<Pixels>>,
 75        cx: &mut ViewContext<S>,
 76    ) -> Result<()>;
 77}
 78
 79struct RenderedElement<E: Element> {
 80    element: E,
 81    phase: ElementRenderPhase<E::FrameState>,
 82}
 83
 84#[derive(Default)]
 85enum ElementRenderPhase<S> {
 86    #[default]
 87    Rendered,
 88    LayoutRequested {
 89        layout_id: LayoutId,
 90        frame_state: S,
 91    },
 92    Painted {
 93        layout: Layout,
 94        frame_state: S,
 95    },
 96}
 97
 98/// Internal struct that wraps an element to store Layout and FrameState after the element is rendered.
 99/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
100/// improved usability.
101impl<E: Element> RenderedElement<E> {
102    fn new(element: E) -> Self {
103        RenderedElement {
104            element,
105            phase: ElementRenderPhase::Rendered,
106        }
107    }
108}
109
110impl<E: Element> ElementObject<E::State> for RenderedElement<E> {
111    fn layout(&mut self, state: &mut E::State, cx: &mut ViewContext<E::State>) -> Result<LayoutId> {
112        let (layout_id, frame_state) = self.element.layout(state, cx)?;
113        self.phase = ElementRenderPhase::LayoutRequested {
114            layout_id,
115            frame_state,
116        };
117        Ok(layout_id)
118    }
119
120    fn paint(
121        &mut self,
122        state: &mut E::State,
123        offset: Option<Point<Pixels>>,
124        cx: &mut ViewContext<E::State>,
125    ) -> Result<()> {
126        self.phase = match std::mem::take(&mut self.phase) {
127            ElementRenderPhase::Rendered => panic!("must call layout before paint"),
128
129            ElementRenderPhase::LayoutRequested {
130                layout_id,
131                mut frame_state,
132            } => {
133                let mut layout = cx.layout(layout_id)?.clone();
134                offset.map(|offset| layout.bounds.origin += offset);
135                self.element
136                    .paint(layout.clone(), state, &mut frame_state, cx)?;
137                ElementRenderPhase::Painted {
138                    layout,
139                    frame_state,
140                }
141            }
142
143            ElementRenderPhase::Painted {
144                layout,
145                mut frame_state,
146            } => {
147                self.element
148                    .paint(layout.clone(), state, &mut frame_state, cx)?;
149                ElementRenderPhase::Painted {
150                    layout,
151                    frame_state,
152                }
153            }
154        };
155
156        Ok(())
157    }
158}
159
160pub struct AnyElement<S>(Box<dyn ElementObject<S>>);
161
162impl<S> AnyElement<S> {
163    pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId> {
164        self.0.layout(state, cx)
165    }
166
167    pub fn paint(
168        &mut self,
169        state: &mut S,
170        offset: Option<Point<Pixels>>,
171        cx: &mut ViewContext<S>,
172    ) -> Result<()> {
173        self.0.paint(state, offset, cx)
174    }
175}
176
177pub trait IntoAnyElement<S> {
178    fn into_any(self) -> AnyElement<S>;
179}
180
181impl<E: Element> IntoAnyElement<E::State> for E {
182    fn into_any(self) -> AnyElement<E::State> {
183        AnyElement(Box::new(RenderedElement::new(self)))
184    }
185}
186
187impl<S> IntoAnyElement<S> for AnyElement<S> {
188    fn into_any(self) -> AnyElement<S> {
189        self
190    }
191}