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