element.rs

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