element.rs

  1use crate::{
  2    AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size, WindowContext,
  3};
  4use derive_more::{Deref, DerefMut};
  5pub(crate) use smallvec::SmallVec;
  6use std::{any::Any, fmt::Debug, mem};
  7
  8pub trait Element {
  9    type ElementState: 'static;
 10
 11    fn element_id(&self) -> Option<ElementId>;
 12
 13    fn layout(
 14        &mut self,
 15        element_state: Option<Self::ElementState>,
 16        cx: &mut WindowContext,
 17    ) -> (LayoutId, Self::ElementState);
 18
 19    fn paint(
 20        &mut self,
 21        bounds: Bounds<Pixels>,
 22        element_state: &mut Self::ElementState,
 23        cx: &mut WindowContext,
 24    );
 25
 26    fn draw<T, R>(
 27        self,
 28        origin: Point<Pixels>,
 29        available_space: Size<T>,
 30        cx: &mut WindowContext,
 31        f: impl FnOnce(&Self::ElementState, &mut WindowContext) -> R,
 32    ) -> R
 33    where
 34        Self: Sized,
 35        T: Clone + Default + Debug + Into<AvailableSpace>,
 36    {
 37        let mut element = RenderedElement {
 38            element: self,
 39            phase: ElementRenderPhase::Start,
 40        };
 41        element.draw(origin, available_space.map(Into::into), cx);
 42        if let ElementRenderPhase::Painted { frame_state } = &element.phase {
 43            if let Some(frame_state) = frame_state.as_ref() {
 44                f(&frame_state, cx)
 45            } else {
 46                let element_id = element
 47                    .element
 48                    .element_id()
 49                    .expect("we either have some frame_state or some element_id");
 50                cx.with_element_state(element_id, |element_state, cx| {
 51                    let element_state = element_state.unwrap();
 52                    let result = f(&element_state, cx);
 53                    (result, element_state)
 54                })
 55            }
 56        } else {
 57            unreachable!()
 58        }
 59    }
 60}
 61
 62#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
 63pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
 64
 65pub trait ParentComponent {
 66    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
 67
 68    fn child(mut self, child: impl Component) -> Self
 69    where
 70        Self: Sized,
 71    {
 72        self.children_mut().push(child.render());
 73        self
 74    }
 75
 76    fn children(mut self, iter: impl IntoIterator<Item = impl Component>) -> Self
 77    where
 78        Self: Sized,
 79    {
 80        self.children_mut()
 81            .extend(iter.into_iter().map(|item| item.render()));
 82        self
 83    }
 84}
 85
 86trait ElementObject {
 87    fn element_id(&self) -> Option<ElementId>;
 88    fn layout(&mut self, cx: &mut WindowContext) -> LayoutId;
 89    fn paint(&mut self, cx: &mut WindowContext);
 90    fn measure(
 91        &mut self,
 92        available_space: Size<AvailableSpace>,
 93        cx: &mut WindowContext,
 94    ) -> Size<Pixels>;
 95    fn draw(
 96        &mut self,
 97        origin: Point<Pixels>,
 98        available_space: Size<AvailableSpace>,
 99        cx: &mut WindowContext,
100    );
101}
102
103struct RenderedElement<E: Element> {
104    element: E,
105    phase: ElementRenderPhase<E::ElementState>,
106}
107
108#[derive(Default)]
109enum ElementRenderPhase<V> {
110    #[default]
111    Start,
112    LayoutRequested {
113        layout_id: LayoutId,
114        frame_state: Option<V>,
115    },
116    LayoutComputed {
117        layout_id: LayoutId,
118        available_space: Size<AvailableSpace>,
119        frame_state: Option<V>,
120    },
121    Painted {
122        frame_state: Option<V>,
123    },
124}
125
126/// Internal struct that wraps an element to store Layout and ElementState after the element is rendered.
127/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
128/// improved usability.
129impl<E: Element> RenderedElement<E> {
130    fn new(element: E) -> Self {
131        RenderedElement {
132            element,
133            phase: ElementRenderPhase::Start,
134        }
135    }
136}
137
138impl<E> ElementObject for RenderedElement<E>
139where
140    E: Element,
141    E::ElementState: 'static,
142{
143    fn element_id(&self) -> Option<ElementId> {
144        self.element.element_id()
145    }
146
147    fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
148        let (layout_id, frame_state) = match mem::take(&mut self.phase) {
149            ElementRenderPhase::Start => {
150                if let Some(id) = self.element.element_id() {
151                    let layout_id = cx.with_element_state(id, |element_state, cx| {
152                        self.element.layout(element_state, cx)
153                    });
154                    (layout_id, None)
155                } else {
156                    let (layout_id, frame_state) = self.element.layout(None, cx);
157                    (layout_id, Some(frame_state))
158                }
159            }
160            ElementRenderPhase::LayoutRequested { .. }
161            | ElementRenderPhase::LayoutComputed { .. }
162            | ElementRenderPhase::Painted { .. } => {
163                panic!("element rendered twice")
164            }
165        };
166
167        self.phase = ElementRenderPhase::LayoutRequested {
168            layout_id,
169            frame_state,
170        };
171        layout_id
172    }
173
174    fn paint(&mut self, cx: &mut WindowContext) {
175        self.phase = match mem::take(&mut self.phase) {
176            ElementRenderPhase::LayoutRequested {
177                layout_id,
178                mut frame_state,
179            }
180            | ElementRenderPhase::LayoutComputed {
181                layout_id,
182                mut frame_state,
183                ..
184            } => {
185                let bounds = cx.layout_bounds(layout_id);
186                if let Some(id) = self.element.element_id() {
187                    cx.with_element_state(id, |element_state, cx| {
188                        let mut element_state = element_state.unwrap();
189                        self.element.paint(bounds, &mut element_state, cx);
190                        ((), element_state)
191                    });
192                } else {
193                    self.element
194                        .paint(bounds, frame_state.as_mut().unwrap(), cx);
195                }
196                ElementRenderPhase::Painted { frame_state }
197            }
198
199            _ => panic!("must call layout before paint"),
200        };
201    }
202
203    fn measure(
204        &mut self,
205        available_space: Size<AvailableSpace>,
206        cx: &mut WindowContext,
207    ) -> Size<Pixels> {
208        if matches!(&self.phase, ElementRenderPhase::Start) {
209            self.layout(cx);
210        }
211
212        let layout_id = match &mut self.phase {
213            ElementRenderPhase::LayoutRequested {
214                layout_id,
215                frame_state,
216            } => {
217                cx.compute_layout(*layout_id, available_space);
218                let layout_id = *layout_id;
219                self.phase = ElementRenderPhase::LayoutComputed {
220                    layout_id,
221                    available_space,
222                    frame_state: frame_state.take(),
223                };
224                layout_id
225            }
226            ElementRenderPhase::LayoutComputed {
227                layout_id,
228                available_space: prev_available_space,
229                ..
230            } => {
231                if available_space != *prev_available_space {
232                    cx.compute_layout(*layout_id, available_space);
233                    *prev_available_space = available_space;
234                }
235                *layout_id
236            }
237            _ => panic!("cannot measure after painting"),
238        };
239
240        cx.layout_bounds(layout_id).size
241    }
242
243    fn draw(
244        &mut self,
245        origin: Point<Pixels>,
246        available_space: Size<AvailableSpace>,
247        cx: &mut WindowContext,
248    ) {
249        self.measure(available_space, cx);
250        cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
251    }
252}
253
254pub struct AnyElement(Box<dyn ElementObject>);
255
256impl AnyElement {
257    pub fn new<E>(element: E) -> Self
258    where
259        E: 'static + Element,
260        E::ElementState: Any,
261    {
262        AnyElement(Box::new(RenderedElement::new(element)))
263    }
264
265    pub fn element_id(&self) -> Option<ElementId> {
266        self.0.element_id()
267    }
268
269    pub fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
270        self.0.layout(cx)
271    }
272
273    pub fn paint(&mut self, cx: &mut WindowContext) {
274        self.0.paint(cx)
275    }
276
277    /// Initializes this element and performs layout within the given available space to determine its size.
278    pub fn measure(
279        &mut self,
280        available_space: Size<AvailableSpace>,
281        cx: &mut WindowContext,
282    ) -> Size<Pixels> {
283        self.0.measure(available_space, cx)
284    }
285
286    /// Initializes this element and performs layout in the available space, then paints it at the given origin.
287    pub fn draw(
288        &mut self,
289        origin: Point<Pixels>,
290        available_space: Size<AvailableSpace>,
291        cx: &mut WindowContext,
292    ) {
293        self.0.draw(origin, available_space, cx)
294    }
295}
296
297pub trait Component {
298    fn render(self) -> AnyElement;
299
300    fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
301    where
302        Self: Sized,
303        U: Component,
304    {
305        f(self)
306    }
307
308    fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
309    where
310        Self: Sized,
311    {
312        self.map(|this| if condition { then(this) } else { this })
313    }
314
315    fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
316    where
317        Self: Sized,
318    {
319        self.map(|this| {
320            if let Some(value) = option {
321                then(this, value)
322            } else {
323                this
324            }
325        })
326    }
327}
328
329impl Component for AnyElement {
330    fn render(self) -> AnyElement {
331        self
332    }
333}
334
335impl<E, F> Element for Option<F>
336where
337    E: 'static + Component,
338    F: FnOnce(&mut WindowContext) -> E + 'static,
339{
340    type ElementState = AnyElement;
341
342    fn element_id(&self) -> Option<ElementId> {
343        None
344    }
345
346    fn layout(
347        &mut self,
348        _: Option<Self::ElementState>,
349        cx: &mut WindowContext,
350    ) -> (LayoutId, Self::ElementState) {
351        let render = self.take().unwrap();
352        let mut rendered_element = (render)(cx).render();
353        let layout_id = rendered_element.layout(cx);
354        (layout_id, rendered_element)
355    }
356
357    fn paint(
358        &mut self,
359        _bounds: Bounds<Pixels>,
360        rendered_element: &mut Self::ElementState,
361        cx: &mut WindowContext,
362    ) {
363        rendered_element.paint(cx)
364    }
365}
366
367impl<E, F> Component for Option<F>
368where
369    E: 'static + Component,
370    F: FnOnce(&mut WindowContext) -> E + 'static,
371{
372    fn render(self) -> AnyElement {
373        AnyElement::new(self)
374    }
375}
376
377impl<E, F> Component for F
378where
379    E: 'static + Component,
380    F: FnOnce(&mut WindowContext) -> E + 'static,
381{
382    fn render(self) -> AnyElement {
383        AnyElement::new(Some(self))
384    }
385}