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
118/// You can derive [`IntoElement`] on any type that implements this trait.
119/// It is used to allow views to be expressed in terms of abstract data.
120pub trait RenderOnce: 'static {
121    fn render(self, cx: &mut WindowContext) -> impl IntoElement;
122}
123
124pub trait ParentElement {
125    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
126
127    fn child(mut self, child: impl IntoElement) -> Self
128    where
129        Self: Sized,
130    {
131        self.children_mut().push(child.into_element().into_any());
132        self
133    }
134
135    fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
136    where
137        Self: Sized,
138    {
139        self.children_mut()
140            .extend(children.into_iter().map(|child| child.into_any_element()));
141        self
142    }
143}
144
145pub struct Component<C: RenderOnce>(Option<C>);
146
147impl<C: RenderOnce> Component<C> {
148    pub fn new(component: C) -> Self {
149        Component(Some(component))
150    }
151}
152
153impl<C: RenderOnce> Element for Component<C> {
154    type State = AnyElement;
155
156    fn request_layout(
157        &mut self,
158        _: Option<Self::State>,
159        cx: &mut WindowContext,
160    ) -> (LayoutId, Self::State) {
161        let mut element = self.0.take().unwrap().render(cx).into_any_element();
162        let layout_id = element.request_layout(cx);
163        (layout_id, element)
164    }
165
166    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
167        element.paint(cx)
168    }
169}
170
171impl<C: RenderOnce> IntoElement for Component<C> {
172    type Element = Self;
173
174    fn element_id(&self) -> Option<ElementId> {
175        None
176    }
177
178    fn into_element(self) -> Self::Element {
179        self
180    }
181}
182
183#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
184pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
185
186trait ElementObject {
187    fn element_id(&self) -> Option<ElementId>;
188
189    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
190
191    fn paint(&mut self, cx: &mut WindowContext);
192
193    fn measure(
194        &mut self,
195        available_space: Size<AvailableSpace>,
196        cx: &mut WindowContext,
197    ) -> Size<Pixels>;
198
199    fn draw(
200        &mut self,
201        origin: Point<Pixels>,
202        available_space: Size<AvailableSpace>,
203        cx: &mut WindowContext,
204    );
205}
206
207pub struct DrawableElement<E: Element> {
208    element: Option<E>,
209    phase: ElementDrawPhase<E::State>,
210}
211
212#[derive(Default)]
213enum ElementDrawPhase<S> {
214    #[default]
215    Start,
216    LayoutRequested {
217        layout_id: LayoutId,
218        frame_state: Option<S>,
219    },
220    LayoutComputed {
221        layout_id: LayoutId,
222        available_space: Size<AvailableSpace>,
223        frame_state: Option<S>,
224    },
225}
226
227/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
228impl<E: Element> DrawableElement<E> {
229    fn new(element: E) -> Self {
230        DrawableElement {
231            element: Some(element),
232            phase: ElementDrawPhase::Start,
233        }
234    }
235
236    fn element_id(&self) -> Option<ElementId> {
237        self.element.as_ref()?.element_id()
238    }
239
240    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
241        let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
242        {
243            let layout_id = cx.with_element_state(id, |element_state, cx| {
244                self.element
245                    .as_mut()
246                    .unwrap()
247                    .request_layout(element_state, cx)
248            });
249            (layout_id, None)
250        } else {
251            let (layout_id, frame_state) = self.element.as_mut().unwrap().request_layout(None, cx);
252            (layout_id, Some(frame_state))
253        };
254
255        self.phase = ElementDrawPhase::LayoutRequested {
256            layout_id,
257            frame_state,
258        };
259        layout_id
260    }
261
262    fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> {
263        match self.phase {
264            ElementDrawPhase::LayoutRequested {
265                layout_id,
266                frame_state,
267            }
268            | ElementDrawPhase::LayoutComputed {
269                layout_id,
270                frame_state,
271                ..
272            } => {
273                let bounds = cx.layout_bounds(layout_id);
274
275                if let Some(mut frame_state) = frame_state {
276                    self.element
277                        .take()
278                        .unwrap()
279                        .paint(bounds, &mut frame_state, cx);
280                    Some(frame_state)
281                } else {
282                    let element_id = self
283                        .element
284                        .as_ref()
285                        .unwrap()
286                        .element_id()
287                        .expect("if we don't have frame state, we should have element state");
288                    cx.with_element_state(element_id, |element_state, cx| {
289                        let mut element_state = element_state.unwrap();
290                        self.element
291                            .take()
292                            .unwrap()
293                            .paint(bounds, &mut element_state, cx);
294                        ((), element_state)
295                    });
296                    None
297                }
298            }
299
300            _ => panic!("must call layout before paint"),
301        }
302    }
303
304    fn measure(
305        &mut self,
306        available_space: Size<AvailableSpace>,
307        cx: &mut WindowContext,
308    ) -> Size<Pixels> {
309        if matches!(&self.phase, ElementDrawPhase::Start) {
310            self.request_layout(cx);
311        }
312
313        let layout_id = match &mut self.phase {
314            ElementDrawPhase::LayoutRequested {
315                layout_id,
316                frame_state,
317            } => {
318                cx.compute_layout(*layout_id, available_space);
319                let layout_id = *layout_id;
320                self.phase = ElementDrawPhase::LayoutComputed {
321                    layout_id,
322                    available_space,
323                    frame_state: frame_state.take(),
324                };
325                layout_id
326            }
327            ElementDrawPhase::LayoutComputed {
328                layout_id,
329                available_space: prev_available_space,
330                ..
331            } => {
332                if available_space != *prev_available_space {
333                    cx.compute_layout(*layout_id, available_space);
334                    *prev_available_space = available_space;
335                }
336                *layout_id
337            }
338            _ => panic!("cannot measure after painting"),
339        };
340
341        cx.layout_bounds(layout_id).size
342    }
343
344    fn draw(
345        mut self,
346        origin: Point<Pixels>,
347        available_space: Size<AvailableSpace>,
348        cx: &mut WindowContext,
349    ) -> Option<E::State> {
350        self.measure(available_space, cx);
351        cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
352    }
353}
354
355impl<E> ElementObject for Option<DrawableElement<E>>
356where
357    E: Element,
358    E::State: 'static,
359{
360    fn element_id(&self) -> Option<ElementId> {
361        self.as_ref().unwrap().element_id()
362    }
363
364    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
365        DrawableElement::request_layout(self.as_mut().unwrap(), cx)
366    }
367
368    fn paint(&mut self, cx: &mut WindowContext) {
369        DrawableElement::paint(self.take().unwrap(), cx);
370    }
371
372    fn measure(
373        &mut self,
374        available_space: Size<AvailableSpace>,
375        cx: &mut WindowContext,
376    ) -> Size<Pixels> {
377        DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
378    }
379
380    fn draw(
381        &mut self,
382        origin: Point<Pixels>,
383        available_space: Size<AvailableSpace>,
384        cx: &mut WindowContext,
385    ) {
386        DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
387    }
388}
389
390pub struct AnyElement(ArenaBox<dyn ElementObject>);
391
392impl AnyElement {
393    pub fn new<E>(element: E) -> Self
394    where
395        E: 'static + Element,
396        E::State: Any,
397    {
398        let element = ELEMENT_ARENA
399            .with_borrow_mut(|arena| arena.alloc(|| Some(DrawableElement::new(element))))
400            .map(|element| element as &mut dyn ElementObject);
401        AnyElement(element)
402    }
403
404    pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
405        self.0.request_layout(cx)
406    }
407
408    pub fn paint(&mut self, cx: &mut WindowContext) {
409        self.0.paint(cx)
410    }
411
412    /// Initializes this element and performs layout within the given available space to determine its size.
413    pub fn measure(
414        &mut self,
415        available_space: Size<AvailableSpace>,
416        cx: &mut WindowContext,
417    ) -> Size<Pixels> {
418        self.0.measure(available_space, cx)
419    }
420
421    /// Initializes this element and performs layout in the available space, then paints it at the given origin.
422    pub fn draw(
423        &mut self,
424        origin: Point<Pixels>,
425        available_space: Size<AvailableSpace>,
426        cx: &mut WindowContext,
427    ) {
428        self.0.draw(origin, available_space, cx)
429    }
430
431    pub fn inner_id(&self) -> Option<ElementId> {
432        self.0.element_id()
433    }
434}
435
436impl Element for AnyElement {
437    type State = ();
438
439    fn request_layout(
440        &mut self,
441        _: Option<Self::State>,
442        cx: &mut WindowContext,
443    ) -> (LayoutId, Self::State) {
444        let layout_id = self.request_layout(cx);
445        (layout_id, ())
446    }
447
448    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
449        self.paint(cx)
450    }
451}
452
453impl IntoElement for AnyElement {
454    type Element = Self;
455
456    fn element_id(&self) -> Option<ElementId> {
457        None
458    }
459
460    fn into_element(self) -> Self::Element {
461        self
462    }
463
464    fn into_any_element(self) -> AnyElement {
465        self
466    }
467}
468
469/// The empty element, which renders nothing.
470pub type Empty = ();
471
472impl IntoElement for () {
473    type Element = Self;
474
475    fn element_id(&self) -> Option<ElementId> {
476        None
477    }
478
479    fn into_element(self) -> Self::Element {
480        self
481    }
482}
483
484impl Element for () {
485    type State = ();
486
487    fn request_layout(
488        &mut self,
489        _state: Option<Self::State>,
490        cx: &mut WindowContext,
491    ) -> (LayoutId, Self::State) {
492        (cx.request_layout(&crate::Style::default(), None), ())
493    }
494
495    fn paint(
496        &mut self,
497        _bounds: Bounds<Pixels>,
498        _state: &mut Self::State,
499        _cx: &mut WindowContext,
500    ) {
501    }
502}