element.rs

  1use smallvec::SmallVec;
  2
  3use super::{Handle, Layout, LayoutId, Pixels, Point, Result, ViewContext, WindowContext};
  4use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
  5
  6pub trait Element: 'static {
  7    type State;
  8    type FrameState;
  9
 10    fn layout(
 11        &mut self,
 12        state: &mut Self::State,
 13        cx: &mut ViewContext<Self::State>,
 14    ) -> Result<(LayoutId, Self::FrameState)>;
 15
 16    fn paint(
 17        &mut self,
 18        layout: Layout,
 19        state: &mut Self::State,
 20        frame_state: &mut Self::FrameState,
 21        cx: &mut ViewContext<Self::State>,
 22    ) -> Result<()>;
 23}
 24
 25pub trait ParentElement<S> {
 26    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<S>; 2]>;
 27
 28    fn child(mut self, child: impl IntoAnyElement<S>) -> Self
 29    where
 30        Self: Sized,
 31    {
 32        self.children_mut().push(child.into_any());
 33        self
 34    }
 35}
 36
 37trait ElementObject<S> {
 38    fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId>;
 39    fn paint(
 40        &mut self,
 41        parent_origin: super::Point<Pixels>,
 42        state: &mut S,
 43        cx: &mut ViewContext<S>,
 44    ) -> Result<()>;
 45}
 46
 47struct RenderedElement<E: Element> {
 48    element: E,
 49    phase: ElementRenderPhase<E::FrameState>,
 50}
 51
 52#[derive(Default)]
 53enum ElementRenderPhase<S> {
 54    #[default]
 55    Rendered,
 56    LayoutRequested {
 57        layout_id: LayoutId,
 58        frame_state: S,
 59    },
 60    Painted {
 61        layout: Layout,
 62        frame_state: S,
 63    },
 64}
 65
 66/// Internal struct that wraps an element to store Layout and FrameState after the element is rendered.
 67/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
 68/// improved usability.
 69impl<E: Element> RenderedElement<E> {
 70    fn new(element: E) -> Self {
 71        RenderedElement {
 72            element,
 73            phase: ElementRenderPhase::Rendered,
 74        }
 75    }
 76}
 77
 78impl<E: Element> ElementObject<E::State> for RenderedElement<E> {
 79    fn layout(&mut self, state: &mut E::State, cx: &mut ViewContext<E::State>) -> Result<LayoutId> {
 80        let (layout_id, frame_state) = self.element.layout(state, cx)?;
 81        self.phase = ElementRenderPhase::LayoutRequested {
 82            layout_id,
 83            frame_state,
 84        };
 85        Ok(layout_id)
 86    }
 87
 88    fn paint(
 89        &mut self,
 90        parent_origin: Point<Pixels>,
 91        state: &mut E::State,
 92        cx: &mut ViewContext<E::State>,
 93    ) -> Result<()> {
 94        self.phase = match std::mem::take(&mut self.phase) {
 95            ElementRenderPhase::Rendered => panic!("must call layout before paint"),
 96
 97            ElementRenderPhase::LayoutRequested {
 98                layout_id,
 99                mut frame_state,
100            } => {
101                let mut layout = cx.layout(layout_id)?;
102                layout.bounds.origin += parent_origin;
103                self.element
104                    .paint(layout.clone(), state, &mut frame_state, cx)?;
105                ElementRenderPhase::Painted {
106                    layout,
107                    frame_state,
108                }
109            }
110
111            ElementRenderPhase::Painted {
112                layout,
113                mut frame_state,
114            } => {
115                self.element
116                    .paint(layout.clone(), state, &mut frame_state, cx)?;
117                ElementRenderPhase::Painted {
118                    layout,
119                    frame_state,
120                }
121            }
122        };
123
124        Ok(())
125    }
126}
127
128pub struct AnyElement<S>(Box<dyn ElementObject<S>>);
129
130impl<S> AnyElement<S> {
131    pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId> {
132        self.0.layout(state, cx)
133    }
134
135    pub fn paint(
136        &mut self,
137        parent_origin: Point<Pixels>,
138        state: &mut S,
139        cx: &mut ViewContext<S>,
140    ) -> Result<()> {
141        self.0.paint(parent_origin, state, cx)
142    }
143}
144
145pub trait IntoAnyElement<S> {
146    fn into_any(self) -> AnyElement<S>;
147}
148
149impl<E: Element> IntoAnyElement<E::State> for E {
150    fn into_any(self) -> AnyElement<E::State> {
151        AnyElement(Box::new(RenderedElement::new(self)))
152    }
153}
154
155impl<S> IntoAnyElement<S> for AnyElement<S> {
156    fn into_any(self) -> AnyElement<S> {
157        self
158    }
159}
160
161#[derive(Clone)]
162pub struct View<S> {
163    state: Handle<S>,
164    render: Rc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S>>,
165}
166
167pub fn view<S: 'static, E: Element<State = S>>(
168    state: Handle<S>,
169    render: impl 'static + Fn(&mut S, &mut ViewContext<S>) -> E,
170) -> View<S> {
171    View {
172        state,
173        render: Rc::new(move |state, cx| render(state, cx).into_any()),
174    }
175}
176
177impl<S: 'static> View<S> {
178    pub fn into_any<ParentState>(self) -> AnyView<ParentState> {
179        AnyView {
180            view: Rc::new(RefCell::new(self)),
181            parent_state_type: PhantomData,
182        }
183    }
184}
185
186impl<S: 'static> Element for View<S> {
187    type State = ();
188    type FrameState = AnyElement<S>;
189
190    fn layout(
191        &mut self,
192        _: &mut Self::State,
193        cx: &mut ViewContext<Self::State>,
194    ) -> Result<(LayoutId, Self::FrameState)> {
195        self.state.update(cx, |state, cx| {
196            let mut element = (self.render)(state, cx);
197            let layout_id = element.layout(state, cx)?;
198            Ok((layout_id, element))
199        })
200    }
201
202    fn paint(
203        &mut self,
204        layout: Layout,
205        _: &mut Self::State,
206        element: &mut Self::FrameState,
207        cx: &mut ViewContext<Self::State>,
208    ) -> Result<()> {
209        self.state.update(cx, |state, cx| {
210            element.paint(layout.bounds.origin, state, cx)
211        })
212    }
213}
214
215trait ViewObject {
216    fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)>;
217    fn paint(
218        &mut self,
219        layout: Layout,
220        element: &mut dyn Any,
221        cx: &mut WindowContext,
222    ) -> Result<()>;
223}
224
225impl<S: 'static> ViewObject for View<S> {
226    fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)> {
227        self.state.update(cx, |state, cx| {
228            let mut element = (self.render)(state, cx);
229            let layout_id = element.layout(state, cx)?;
230            let element = Box::new(element) as Box<dyn Any>;
231            Ok((layout_id, element))
232        })
233    }
234
235    fn paint(
236        &mut self,
237        layout: Layout,
238        element: &mut dyn Any,
239        cx: &mut WindowContext,
240    ) -> Result<()> {
241        self.state.update(cx, |state, cx| {
242            element
243                .downcast_mut::<AnyElement<S>>()
244                .unwrap()
245                .paint(layout.bounds.origin, state, cx)
246        })
247    }
248}
249
250pub struct AnyView<S> {
251    view: Rc<RefCell<dyn ViewObject>>,
252    parent_state_type: PhantomData<S>,
253}
254
255impl<S: 'static> Element for AnyView<S> {
256    type State = S;
257    type FrameState = Box<dyn Any>;
258
259    fn layout(
260        &mut self,
261        _: &mut Self::State,
262        cx: &mut ViewContext<Self::State>,
263    ) -> Result<(LayoutId, Self::FrameState)> {
264        self.view.borrow_mut().layout(cx)
265    }
266
267    fn paint(
268        &mut self,
269        layout: Layout,
270        _: &mut Self::State,
271        element: &mut Self::FrameState,
272        cx: &mut ViewContext<Self::State>,
273    ) -> Result<()> {
274        self.view.borrow_mut().paint(layout, element, cx)
275    }
276}
277
278impl<S> Clone for AnyView<S> {
279    fn clone(&self) -> Self {
280        Self {
281            view: self.view.clone(),
282            parent_state_type: PhantomData,
283        }
284    }
285}