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(bounds, state.rendered_element_state.as_mut().unwrap(), cx);
197        }
198    }
199}
200
201impl<C: RenderOnce> IntoElement for Component<C> {
202    type Element = Self;
203
204    fn element_id(&self) -> Option<ElementId> {
205        None
206    }
207
208    fn into_element(self) -> Self::Element {
209        self
210    }
211}
212
213#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
214pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
215
216trait ElementObject {
217    fn element_id(&self) -> Option<ElementId>;
218
219    fn layout(&mut self, cx: &mut WindowContext) -> LayoutId;
220
221    fn paint(&mut self, cx: &mut WindowContext);
222
223    fn measure(
224        &mut self,
225        available_space: Size<AvailableSpace>,
226        cx: &mut WindowContext,
227    ) -> Size<Pixels>;
228
229    fn draw(
230        &mut self,
231        origin: Point<Pixels>,
232        available_space: Size<AvailableSpace>,
233        cx: &mut WindowContext,
234    );
235}
236
237pub struct DrawableElement<E: Element> {
238    element: Option<E>,
239    phase: ElementDrawPhase<E::State>,
240}
241
242#[derive(Default)]
243enum ElementDrawPhase<S> {
244    #[default]
245    Start,
246    LayoutRequested {
247        layout_id: LayoutId,
248        frame_state: Option<S>,
249    },
250    LayoutComputed {
251        layout_id: LayoutId,
252        available_space: Size<AvailableSpace>,
253        frame_state: Option<S>,
254    },
255}
256
257/// A wrapper around an implementer of [Element] that allows it to be drawn in a window.
258impl<E: Element> DrawableElement<E> {
259    fn new(element: E) -> Self {
260        DrawableElement {
261            element: Some(element),
262            phase: ElementDrawPhase::Start,
263        }
264    }
265
266    fn element_id(&self) -> Option<ElementId> {
267        self.element.as_ref()?.element_id()
268    }
269
270    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
271        let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
272        {
273            let layout_id = cx.with_element_state(id, |element_state, cx| {
274                self.element
275                    .as_mut()
276                    .unwrap()
277                    .request_layout(element_state, cx)
278            });
279            (layout_id, None)
280        } else {
281            let (layout_id, frame_state) = self.element.as_mut().unwrap().request_layout(None, cx);
282            (layout_id, Some(frame_state))
283        };
284
285        self.phase = ElementDrawPhase::LayoutRequested {
286            layout_id,
287            frame_state,
288        };
289        layout_id
290    }
291
292    fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> {
293        match self.phase {
294            ElementDrawPhase::LayoutRequested {
295                layout_id,
296                frame_state,
297            }
298            | ElementDrawPhase::LayoutComputed {
299                layout_id,
300                frame_state,
301                ..
302            } => {
303                let bounds = cx.layout_bounds(layout_id);
304
305                if let Some(mut frame_state) = frame_state {
306                    self.element
307                        .take()
308                        .unwrap()
309                        .paint(bounds, &mut frame_state, cx);
310                    Some(frame_state)
311                } else {
312                    let element_id = self
313                        .element
314                        .as_ref()
315                        .unwrap()
316                        .element_id()
317                        .expect("if we don't have frame state, we should have element state");
318                    cx.with_element_state(element_id, |element_state, cx| {
319                        let mut element_state = element_state.unwrap();
320                        self.element
321                            .take()
322                            .unwrap()
323                            .paint(bounds, &mut element_state, cx);
324                        ((), element_state)
325                    });
326                    None
327                }
328            }
329
330            _ => panic!("must call layout before paint"),
331        }
332    }
333
334    fn measure(
335        &mut self,
336        available_space: Size<AvailableSpace>,
337        cx: &mut WindowContext,
338    ) -> Size<Pixels> {
339        if matches!(&self.phase, ElementDrawPhase::Start) {
340            self.request_layout(cx);
341        }
342
343        let layout_id = match &mut self.phase {
344            ElementDrawPhase::LayoutRequested {
345                layout_id,
346                frame_state,
347            } => {
348                cx.compute_layout(*layout_id, available_space);
349                let layout_id = *layout_id;
350                self.phase = ElementDrawPhase::LayoutComputed {
351                    layout_id,
352                    available_space,
353                    frame_state: frame_state.take(),
354                };
355                layout_id
356            }
357            ElementDrawPhase::LayoutComputed {
358                layout_id,
359                available_space: prev_available_space,
360                ..
361            } => {
362                if available_space != *prev_available_space {
363                    cx.compute_layout(*layout_id, available_space);
364                    *prev_available_space = available_space;
365                }
366                *layout_id
367            }
368            _ => panic!("cannot measure after painting"),
369        };
370
371        cx.layout_bounds(layout_id).size
372    }
373
374    fn draw(
375        mut self,
376        origin: Point<Pixels>,
377        available_space: Size<AvailableSpace>,
378        cx: &mut WindowContext,
379    ) -> Option<E::State> {
380        self.measure(available_space, cx);
381        cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
382    }
383}
384
385impl<E> ElementObject for Option<DrawableElement<E>>
386where
387    E: Element,
388    E::State: 'static,
389{
390    fn element_id(&self) -> Option<ElementId> {
391        self.as_ref().unwrap().element_id()
392    }
393
394    fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
395        DrawableElement::request_layout(self.as_mut().unwrap(), cx)
396    }
397
398    fn paint(&mut self, cx: &mut WindowContext) {
399        DrawableElement::paint(self.take().unwrap(), cx);
400    }
401
402    fn measure(
403        &mut self,
404        available_space: Size<AvailableSpace>,
405        cx: &mut WindowContext,
406    ) -> Size<Pixels> {
407        DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
408    }
409
410    fn draw(
411        &mut self,
412        origin: Point<Pixels>,
413        available_space: Size<AvailableSpace>,
414        cx: &mut WindowContext,
415    ) {
416        DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
417    }
418}
419
420pub struct AnyElement(ArenaBox<dyn ElementObject>);
421
422impl AnyElement {
423    pub fn new<E>(element: E) -> Self
424    where
425        E: 'static + Element,
426        E::State: Any,
427    {
428        let element = ELEMENT_ARENA
429            .with_borrow_mut(|arena| arena.alloc(|| Some(DrawableElement::new(element))))
430            .map(|element| element as &mut dyn ElementObject);
431        AnyElement(element)
432    }
433
434    pub fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
435        self.0.layout(cx)
436    }
437
438    pub fn paint(&mut self, cx: &mut WindowContext) {
439        self.0.paint(cx)
440    }
441
442    /// Initializes this element and performs layout within the given available space to determine its size.
443    pub fn measure(
444        &mut self,
445        available_space: Size<AvailableSpace>,
446        cx: &mut WindowContext,
447    ) -> Size<Pixels> {
448        self.0.measure(available_space, cx)
449    }
450
451    /// Initializes this element and performs layout in the available space, then paints it at the given origin.
452    pub fn draw(
453        &mut self,
454        origin: Point<Pixels>,
455        available_space: Size<AvailableSpace>,
456        cx: &mut WindowContext,
457    ) {
458        self.0.draw(origin, available_space, cx)
459    }
460
461    pub fn inner_id(&self) -> Option<ElementId> {
462        self.0.element_id()
463    }
464}
465
466impl Element for AnyElement {
467    type State = ();
468
469    fn request_layout(
470        &mut self,
471        _: Option<Self::State>,
472        cx: &mut WindowContext,
473    ) -> (LayoutId, Self::State) {
474        let layout_id = self.layout(cx);
475        (layout_id, ())
476    }
477
478    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
479        self.paint(cx)
480    }
481}
482
483impl IntoElement for AnyElement {
484    type Element = Self;
485
486    fn element_id(&self) -> Option<ElementId> {
487        None
488    }
489
490    fn into_element(self) -> Self::Element {
491        self
492    }
493
494    fn into_any_element(self) -> AnyElement {
495        self
496    }
497}
498
499/// The empty element, which renders nothing.
500pub type Empty = ();
501
502impl IntoElement for () {
503    type Element = Self;
504
505    fn element_id(&self) -> Option<ElementId> {
506        None
507    }
508
509    fn into_element(self) -> Self::Element {
510        self
511    }
512}
513
514impl Element for () {
515    type State = ();
516
517    fn request_layout(
518        &mut self,
519        _state: Option<Self::State>,
520        cx: &mut WindowContext,
521    ) -> (LayoutId, Self::State) {
522        (cx.request_layout(&crate::Style::default(), None), ())
523    }
524
525    fn paint(
526        &mut self,
527        _bounds: Bounds<Pixels>,
528        _state: &mut Self::State,
529        _cx: &mut WindowContext,
530    ) {
531    }
532}