element.rs

  1use crate::{
  2    ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size,
  3    ViewContext, WindowContext, ELEMENT_ARENA,
  4};
  5use derive_more::{Deref, DerefMut};
  6pub(crate) use smallvec::SmallVec;
  7use std::{any::Any, fmt::Debug};
  8
  9/// Implemented by types that participate in laying out and painting the contents of a window.
 10/// Elements form a tree and are laid out according to web-based layout rules.
 11/// Rather than calling methods on implementers of this trait directly, you'll usually call `into_any` to convert  them into an AnyElement, which manages state internally.
 12/// You can create custom elements by implementing this trait.
 13pub trait Element: 'static + IntoElement {
 14    type State: 'static;
 15
 16    fn request_layout(
 17        &mut self,
 18        state: Option<Self::State>,
 19        cx: &mut WindowContext,
 20    ) -> (LayoutId, Self::State);
 21
 22    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
 23
 24    fn into_any(self) -> AnyElement {
 25        AnyElement::new(self)
 26    }
 27}
 28
 29/// Implemented by any type that can be converted into an element.
 30pub trait IntoElement: Sized {
 31    /// The specific type of element into which the implementing type is converted.
 32    type Element: Element;
 33
 34    /// The [`ElementId`] of self once converted into an [`Element`].
 35    /// If present, the resulting element's state will be carried across frames.
 36    fn element_id(&self) -> Option<ElementId>;
 37
 38    /// Convert self into a type that implements [`Element`].
 39    fn into_element(self) -> Self::Element;
 40
 41    /// Convert self into a dynamically-typed [`AnyElement`].
 42    fn into_any_element(self) -> AnyElement {
 43        self.into_element().into_any()
 44    }
 45
 46    /// Convert into an element, then draw in the current window at the given origin.
 47    /// The available space argument is provided to the layout engine to determine the size of the
 48    // root element.  Once the element is drawn, its associated element state is yielded to the
 49    // given callback.
 50    fn draw_and_update_state<T, R>(
 51        self,
 52        origin: Point<Pixels>,
 53        available_space: Size<T>,
 54        cx: &mut WindowContext,
 55        f: impl FnOnce(&mut <Self::Element as Element>::State, &mut WindowContext) -> R,
 56    ) -> R
 57    where
 58        T: Clone + Default + Debug + Into<AvailableSpace>,
 59    {
 60        let element = self.into_element();
 61        let element_id = element.element_id();
 62        let element = DrawableElement {
 63            element: Some(element),
 64            phase: ElementDrawPhase::Start,
 65        };
 66
 67        let frame_state =
 68            DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
 69
 70        if let Some(mut frame_state) = frame_state {
 71            f(&mut frame_state, cx)
 72        } else {
 73            cx.with_element_state(element_id.unwrap(), |element_state, cx| {
 74                let mut element_state = element_state.unwrap();
 75                let result = f(&mut element_state, cx);
 76                (result, element_state)
 77            })
 78        }
 79    }
 80
 81    /// Convert self to another type by calling the given closure. Useful in rendering code.
 82    fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
 83    where
 84        Self: Sized,
 85        U: IntoElement,
 86    {
 87        f(self)
 88    }
 89
 90    /// Conditionally chain onto self with the given closure. Useful in rendering code.
 91    fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
 92    where
 93        Self: Sized,
 94    {
 95        self.map(|this| if condition { then(this) } else { this })
 96    }
 97
 98    /// Conditionally chain onto self with the given closure if the given option is Some.
 99    /// The contents of the option are provided to the closure.
100    fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
101    where
102        Self: Sized,
103    {
104        self.map(|this| {
105            if let Some(value) = option {
106                then(this, value)
107            } else {
108                this
109            }
110        })
111    }
112}
113
114pub trait Render: 'static + Sized {
115    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
116}
117
118impl Render for () {
119    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
120        ()
121    }
122}
123
124/// You can derive [`IntoElement`] on any type that implements this trait.
125/// It is used to allow views to be expressed in terms of abstract data.
126pub trait RenderOnce: 'static {
127    fn render(self, cx: &mut WindowContext) -> impl IntoElement;
128}
129
130pub trait ParentElement {
131    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
132
133    fn child(mut self, child: impl IntoElement) -> Self
134    where
135        Self: Sized,
136    {
137        self.children_mut().push(child.into_element().into_any());
138        self
139    }
140
141    fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
142    where
143        Self: Sized,
144    {
145        self.children_mut()
146            .extend(children.into_iter().map(|child| child.into_any_element()));
147        self
148    }
149}
150
151pub struct Component<C: RenderOnce>(Option<C>);
152
153impl<C: RenderOnce> Component<C> {
154    pub fn new(component: C) -> Self {
155        Component(Some(component))
156    }
157}
158
159impl<C: RenderOnce> Element for Component<C> {
160    type State = AnyElement;
161
162    fn request_layout(
163        &mut self,
164        _: Option<Self::State>,
165        cx: &mut WindowContext,
166    ) -> (LayoutId, Self::State) {
167        let mut element = self.0.take().unwrap().render(cx).into_any_element();
168        let layout_id = element.request_layout(cx);
169        (layout_id, element)
170    }
171
172    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
173        element.paint(cx)
174    }
175}
176
177impl<C: RenderOnce> IntoElement for Component<C> {
178    type Element = Self;
179
180    fn element_id(&self) -> Option<ElementId> {
181        None
182    }
183
184    fn into_element(self) -> Self::Element {
185        self
186    }
187}
188
189#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
190pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
191
192trait ElementObject {
193    fn element_id(&self) -> Option<ElementId>;
194
195    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
196
197    fn paint(&mut self, cx: &mut WindowContext);
198
199    fn measure(
200        &mut self,
201        available_space: Size<AvailableSpace>,
202        cx: &mut WindowContext,
203    ) -> Size<Pixels>;
204
205    fn draw(
206        &mut self,
207        origin: Point<Pixels>,
208        available_space: Size<AvailableSpace>,
209        cx: &mut WindowContext,
210    );
211}
212
213pub struct DrawableElement<E: Element> {
214    element: Option<E>,
215    phase: ElementDrawPhase<E::State>,
216}
217
218#[derive(Default)]
219enum ElementDrawPhase<S> {
220    #[default]
221    Start,
222    LayoutRequested {
223        layout_id: LayoutId,
224        frame_state: Option<S>,
225    },
226    LayoutComputed {
227        layout_id: LayoutId,
228        available_space: Size<AvailableSpace>,
229        frame_state: Option<S>,
230    },
231}
232
233/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
234impl<E: Element> DrawableElement<E> {
235    fn new(element: E) -> Self {
236        DrawableElement {
237            element: Some(element),
238            phase: ElementDrawPhase::Start,
239        }
240    }
241
242    fn element_id(&self) -> Option<ElementId> {
243        self.element.as_ref()?.element_id()
244    }
245
246    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
247        let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
248        {
249            let layout_id = cx.with_element_state(id, |element_state, cx| {
250                self.element
251                    .as_mut()
252                    .unwrap()
253                    .request_layout(element_state, cx)
254            });
255            (layout_id, None)
256        } else {
257            let (layout_id, frame_state) = self.element.as_mut().unwrap().request_layout(None, cx);
258            (layout_id, Some(frame_state))
259        };
260
261        self.phase = ElementDrawPhase::LayoutRequested {
262            layout_id,
263            frame_state,
264        };
265        layout_id
266    }
267
268    fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> {
269        match self.phase {
270            ElementDrawPhase::LayoutRequested {
271                layout_id,
272                frame_state,
273            }
274            | ElementDrawPhase::LayoutComputed {
275                layout_id,
276                frame_state,
277                ..
278            } => {
279                let bounds = cx.layout_bounds(layout_id);
280
281                if let Some(mut frame_state) = frame_state {
282                    self.element
283                        .take()
284                        .unwrap()
285                        .paint(bounds, &mut frame_state, cx);
286                    Some(frame_state)
287                } else {
288                    let element_id = self
289                        .element
290                        .as_ref()
291                        .unwrap()
292                        .element_id()
293                        .expect("if we don't have frame state, we should have element state");
294                    cx.with_element_state(element_id, |element_state, cx| {
295                        let mut element_state = element_state.unwrap();
296                        self.element
297                            .take()
298                            .unwrap()
299                            .paint(bounds, &mut element_state, cx);
300                        ((), element_state)
301                    });
302                    None
303                }
304            }
305
306            _ => panic!("must call layout before paint"),
307        }
308    }
309
310    fn measure(
311        &mut self,
312        available_space: Size<AvailableSpace>,
313        cx: &mut WindowContext,
314    ) -> Size<Pixels> {
315        if matches!(&self.phase, ElementDrawPhase::Start) {
316            self.request_layout(cx);
317        }
318
319        let layout_id = match &mut self.phase {
320            ElementDrawPhase::LayoutRequested {
321                layout_id,
322                frame_state,
323            } => {
324                cx.compute_layout(*layout_id, available_space);
325                let layout_id = *layout_id;
326                self.phase = ElementDrawPhase::LayoutComputed {
327                    layout_id,
328                    available_space,
329                    frame_state: frame_state.take(),
330                };
331                layout_id
332            }
333            ElementDrawPhase::LayoutComputed {
334                layout_id,
335                available_space: prev_available_space,
336                ..
337            } => {
338                if available_space != *prev_available_space {
339                    cx.compute_layout(*layout_id, available_space);
340                    *prev_available_space = available_space;
341                }
342                *layout_id
343            }
344            _ => panic!("cannot measure after painting"),
345        };
346
347        cx.layout_bounds(layout_id).size
348    }
349
350    fn draw(
351        mut self,
352        origin: Point<Pixels>,
353        available_space: Size<AvailableSpace>,
354        cx: &mut WindowContext,
355    ) -> Option<E::State> {
356        self.measure(available_space, cx);
357        cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
358    }
359}
360
361impl<E> ElementObject for Option<DrawableElement<E>>
362where
363    E: Element,
364    E::State: 'static,
365{
366    fn element_id(&self) -> Option<ElementId> {
367        self.as_ref().unwrap().element_id()
368    }
369
370    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
371        DrawableElement::request_layout(self.as_mut().unwrap(), cx)
372    }
373
374    fn paint(&mut self, cx: &mut WindowContext) {
375        DrawableElement::paint(self.take().unwrap(), cx);
376    }
377
378    fn measure(
379        &mut self,
380        available_space: Size<AvailableSpace>,
381        cx: &mut WindowContext,
382    ) -> Size<Pixels> {
383        DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
384    }
385
386    fn draw(
387        &mut self,
388        origin: Point<Pixels>,
389        available_space: Size<AvailableSpace>,
390        cx: &mut WindowContext,
391    ) {
392        DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
393    }
394}
395
396pub struct AnyElement(ArenaBox<dyn ElementObject>);
397
398impl AnyElement {
399    pub fn new<E>(element: E) -> Self
400    where
401        E: 'static + Element,
402        E::State: Any,
403    {
404        let element = ELEMENT_ARENA
405            .with_borrow_mut(|arena| arena.alloc(|| Some(DrawableElement::new(element))))
406            .map(|element| element as &mut dyn ElementObject);
407        AnyElement(element)
408    }
409
410    pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
411        self.0.request_layout(cx)
412    }
413
414    pub fn paint(&mut self, cx: &mut WindowContext) {
415        self.0.paint(cx)
416    }
417
418    /// Initializes this element and performs layout within the given available space to determine its size.
419    pub fn measure(
420        &mut self,
421        available_space: Size<AvailableSpace>,
422        cx: &mut WindowContext,
423    ) -> Size<Pixels> {
424        self.0.measure(available_space, cx)
425    }
426
427    /// Initializes this element and performs layout in the available space, then paints it at the given origin.
428    pub fn draw(
429        &mut self,
430        origin: Point<Pixels>,
431        available_space: Size<AvailableSpace>,
432        cx: &mut WindowContext,
433    ) {
434        self.0.draw(origin, available_space, cx)
435    }
436
437    pub fn inner_id(&self) -> Option<ElementId> {
438        self.0.element_id()
439    }
440}
441
442impl Element for AnyElement {
443    type State = ();
444
445    fn request_layout(
446        &mut self,
447        _: Option<Self::State>,
448        cx: &mut WindowContext,
449    ) -> (LayoutId, Self::State) {
450        let layout_id = self.request_layout(cx);
451        (layout_id, ())
452    }
453
454    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
455        self.paint(cx)
456    }
457}
458
459impl IntoElement for AnyElement {
460    type Element = Self;
461
462    fn element_id(&self) -> Option<ElementId> {
463        None
464    }
465
466    fn into_element(self) -> Self::Element {
467        self
468    }
469
470    fn into_any_element(self) -> AnyElement {
471        self
472    }
473}
474
475/// The empty element, which renders nothing.
476pub type Empty = ();
477
478impl IntoElement for () {
479    type Element = Self;
480
481    fn element_id(&self) -> Option<ElementId> {
482        None
483    }
484
485    fn into_element(self) -> Self::Element {
486        self
487    }
488}
489
490impl Element for () {
491    type State = ();
492
493    fn request_layout(
494        &mut self,
495        _state: Option<Self::State>,
496        cx: &mut WindowContext,
497    ) -> (LayoutId, Self::State) {
498        (cx.request_layout(&crate::Style::default(), None), ())
499    }
500
501    fn paint(
502        &mut self,
503        _bounds: Bounds<Pixels>,
504        _state: &mut Self::State,
505        _cx: &mut WindowContext,
506    ) {
507    }
508}