element.rs

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