element.rs

  1//! Elements are the workhorses of GPUI. They are responsible for laying out and painting all of
  2//! the contents of a window. Elements form a tree and are laid out according to the web layout
  3//! standards as implemented by [taffy](https://github.com/DioxusLabs/taffy). Most of the time,
  4//! you won't need to interact with this module or these APIs directly. Elements provide their
  5//! own APIs and GPUI, or other element implementation, uses the APIs in this module to convert
  6//! that element tree into the pixels you see on the screen.
  7//!
  8//! # Element Basics
  9//!
 10//! Elements are constructed by calling [`Render::render()`] on the root view of the window, which
 11//! which recursively constructs the element tree from the current state of the application,.
 12//! These elements are then laid out by Taffy, and painted to the screen according to their own
 13//! implementation of [`Element::paint()`]. Before the start of the next frame, the entire element
 14//! tree and any callbacks they have registered with GPUI are dropped and the process repeats.
 15//!
 16//! But some state is too simple and voluminous to store in every view that needs it, e.g.
 17//! whether a hover has been started or not. For this, GPUI provides the [`Element::State`], associated type.
 18//! If an element returns an [`ElementId`] from [`IntoElement::element_id()`], and that element id
 19//! appears in the same place relative to other views and ElementIds in the frame, then the previous
 20//! frame's state will be passed to the element's layout and paint methods.
 21//!
 22//! # Implementing your own elements
 23//!
 24//! Elements are intended to be the low level, imperative API to GPUI. They are responsible for upholding,
 25//! or breaking, GPUI's features as they deem necessary. As an example, most GPUI elements are expected
 26//! to stay in the bounds that their parent element gives them. But with [`WindowContext::break_content_mask`],
 27//! you can ignore this restriction and paint anywhere inside of the window's bounds. This is useful for overlays
 28//! and popups and anything else that shows up 'on top' of other elements.
 29//! With great power, comes great responsibility.
 30//!
 31//! However, most of the time, you won't need to implement your own elements. GPUI provides a number of
 32//! elements that should cover most common use cases out of the box and it's recommended that you use those
 33//! to construct `components`, using the [`RenderOnce`] trait and the `#[derive(IntoElement)]` macro. Only implement
 34//! elements when you need to take manual control of the layout and painting process, such as when using
 35//! your own custom layout algorithm or rendering a code editor.
 36
 37use crate::{
 38    util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, ElementContext, ElementId, LayoutId,
 39    Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
 40};
 41use derive_more::{Deref, DerefMut};
 42pub(crate) use smallvec::SmallVec;
 43use std::{any::Any, fmt::Debug, ops::DerefMut};
 44
 45/// Implemented by types that participate in laying out and painting the contents of a window.
 46/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
 47/// You can create custom elements by implementing this trait, see the module-level documentation
 48/// for more details.
 49pub trait Element: 'static + IntoElement {
 50    /// The type of state to store for this element between frames. See the module-level documentation
 51    /// for details.
 52    type State: 'static;
 53
 54    /// Before an element can be painted, we need to know where it's going to be and how big it is.
 55    /// Use this method to request a layout from Taffy and initialize the element's state.
 56    fn request_layout(
 57        &mut self,
 58        state: Option<Self::State>,
 59        cx: &mut ElementContext,
 60    ) -> (LayoutId, Self::State);
 61
 62    /// Once layout has been completed, this method will be called to paint the element to the screen.
 63    /// The state argument is the same state that was returned from [`Element::request_layout()`].
 64    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext);
 65
 66    /// Convert this element into a dynamically-typed [`AnyElement`].
 67    fn into_any(self) -> AnyElement {
 68        AnyElement::new(self)
 69    }
 70}
 71
 72/// Implemented by any type that can be converted into an element.
 73pub trait IntoElement: Sized {
 74    /// The specific type of element into which the implementing type is converted.
 75    /// Useful for converting other types into elements automatically, like Strings
 76    type Element: Element;
 77
 78    /// The [`ElementId`] of self once converted into an [`Element`].
 79    /// If present, the resulting element's state will be carried across frames.
 80    fn element_id(&self) -> Option<ElementId>;
 81
 82    /// Convert self into a type that implements [`Element`].
 83    fn into_element(self) -> Self::Element;
 84
 85    /// Convert self into a dynamically-typed [`AnyElement`].
 86    fn into_any_element(self) -> AnyElement {
 87        self.into_element().into_any()
 88    }
 89
 90    /// Convert into an element, then draw in the current window at the given origin.
 91    /// The available space argument is provided to the layout engine to determine the size of the
 92    // root element.  Once the element is drawn, its associated element state is yielded to the
 93    // given callback.
 94    fn draw_and_update_state<T, R>(
 95        self,
 96        origin: Point<Pixels>,
 97        available_space: Size<T>,
 98        cx: &mut ElementContext,
 99        f: impl FnOnce(&mut <Self::Element as Element>::State, &mut ElementContext) -> R,
100    ) -> R
101    where
102        T: Clone + Default + Debug + Into<AvailableSpace>,
103    {
104        let element = self.into_element();
105        let element_id = element.element_id();
106        let element = DrawableElement {
107            element: Some(element),
108            phase: ElementDrawPhase::Start,
109        };
110
111        let frame_state =
112            DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
113
114        if let Some(mut frame_state) = frame_state {
115            f(&mut frame_state, cx)
116        } else {
117            cx.with_element_state(element_id.unwrap(), |element_state, cx| {
118                let mut element_state = element_state.unwrap();
119                let result = f(&mut element_state, cx);
120                (result, element_state)
121            })
122        }
123    }
124}
125
126impl<T: IntoElement> FluentBuilder for T {}
127
128/// An object that can be drawn to the screen. This is the trait that distinguishes `Views` from
129/// models. Views are drawn to the screen and care about the current window's state, models are not and do not.
130pub trait Render: 'static + Sized {
131    /// Render this view into an element tree.
132    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
133}
134
135impl Render for () {
136    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {}
137}
138
139/// You can derive [`IntoElement`] on any type that implements this trait.
140/// It is used to construct reusable `components` out of plain data. Think of
141/// components as a recipe for a certain pattern of elements. RenderOnce allows
142/// you to invoke this pattern, without breaking the fluent builder pattern of
143/// the element APIs.
144pub trait RenderOnce: 'static {
145    /// Render this component into an element tree. Note that this method
146    /// takes ownership of self, as compared to [`Render::render()`] method
147    /// which takes a mutable reference.
148    fn render(self, cx: &mut WindowContext) -> impl IntoElement;
149}
150
151/// This is a helper trait to provide a uniform interface for constructing elements that
152/// can accept any number of any kind of child elements
153pub trait ParentElement {
154    /// Extend this element's children with the given child elements.
155    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>);
156
157    /// Add a single child element to this element.
158    fn child(mut self, child: impl IntoElement) -> Self
159    where
160        Self: Sized,
161    {
162        self.extend(std::iter::once(child.into_element().into_any()));
163        self
164    }
165
166    /// Add multiple child elements to this element.
167    fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
168    where
169        Self: Sized,
170    {
171        self.extend(children.into_iter().map(|child| child.into_any_element()));
172        self
173    }
174}
175
176/// An element for rendering components. An implementation detail of the [`IntoElement`] derive macro
177/// for [`RenderOnce`]
178#[doc(hidden)]
179pub struct Component<C: RenderOnce>(Option<C>);
180
181impl<C: RenderOnce> Component<C> {
182    /// Create a new component from the given RenderOnce type.
183    pub fn new(component: C) -> Self {
184        Component(Some(component))
185    }
186}
187
188impl<C: RenderOnce> Element for Component<C> {
189    type State = AnyElement;
190
191    fn request_layout(
192        &mut self,
193        _: Option<Self::State>,
194        cx: &mut ElementContext,
195    ) -> (LayoutId, Self::State) {
196        let mut element = self
197            .0
198            .take()
199            .unwrap()
200            .render(cx.deref_mut())
201            .into_any_element();
202        let layout_id = element.request_layout(cx);
203        (layout_id, element)
204    }
205
206    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
207        element.paint(cx)
208    }
209}
210
211impl<C: RenderOnce> IntoElement for Component<C> {
212    type Element = Self;
213
214    fn element_id(&self) -> Option<ElementId> {
215        None
216    }
217
218    fn into_element(self) -> Self::Element {
219        self
220    }
221}
222
223/// A globally unique identifier for an element, used to track state across frames.
224#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
225pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
226
227trait ElementObject {
228    fn element_id(&self) -> Option<ElementId>;
229
230    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
231
232    fn paint(&mut self, cx: &mut ElementContext);
233
234    fn measure(
235        &mut self,
236        available_space: Size<AvailableSpace>,
237        cx: &mut ElementContext,
238    ) -> Size<Pixels>;
239
240    fn draw(
241        &mut self,
242        origin: Point<Pixels>,
243        available_space: Size<AvailableSpace>,
244        cx: &mut ElementContext,
245    );
246}
247
248/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
249pub(crate) struct DrawableElement<E: Element> {
250    element: Option<E>,
251    phase: ElementDrawPhase<E::State>,
252}
253
254#[derive(Default)]
255enum ElementDrawPhase<S> {
256    #[default]
257    Start,
258    LayoutRequested {
259        layout_id: LayoutId,
260        frame_state: Option<S>,
261    },
262    LayoutComputed {
263        layout_id: LayoutId,
264        available_space: Size<AvailableSpace>,
265        frame_state: Option<S>,
266    },
267}
268
269/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
270impl<E: Element> DrawableElement<E> {
271    fn new(element: E) -> Self {
272        DrawableElement {
273            element: Some(element),
274            phase: ElementDrawPhase::Start,
275        }
276    }
277
278    fn element_id(&self) -> Option<ElementId> {
279        self.element.as_ref()?.element_id()
280    }
281
282    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
283        let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
284        {
285            let layout_id = cx.with_element_state(id, |element_state, cx| {
286                self.element
287                    .as_mut()
288                    .unwrap()
289                    .request_layout(element_state, cx)
290            });
291            (layout_id, None)
292        } else {
293            let (layout_id, frame_state) = self.element.as_mut().unwrap().request_layout(None, cx);
294            (layout_id, Some(frame_state))
295        };
296
297        self.phase = ElementDrawPhase::LayoutRequested {
298            layout_id,
299            frame_state,
300        };
301        layout_id
302    }
303
304    fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
305        match self.phase {
306            ElementDrawPhase::LayoutRequested {
307                layout_id,
308                frame_state,
309            }
310            | ElementDrawPhase::LayoutComputed {
311                layout_id,
312                frame_state,
313                ..
314            } => {
315                let bounds = cx.layout_bounds(layout_id);
316
317                if let Some(mut frame_state) = frame_state {
318                    self.element
319                        .take()
320                        .unwrap()
321                        .paint(bounds, &mut frame_state, cx);
322                    Some(frame_state)
323                } else {
324                    let element_id = self
325                        .element
326                        .as_ref()
327                        .unwrap()
328                        .element_id()
329                        .expect("if we don't have frame state, we should have element state");
330                    cx.with_element_state(element_id, |element_state, cx| {
331                        let mut element_state = element_state.unwrap();
332                        self.element
333                            .take()
334                            .unwrap()
335                            .paint(bounds, &mut element_state, cx);
336                        ((), element_state)
337                    });
338                    None
339                }
340            }
341
342            _ => panic!("must call layout before paint"),
343        }
344    }
345
346    fn measure(
347        &mut self,
348        available_space: Size<AvailableSpace>,
349        cx: &mut ElementContext,
350    ) -> Size<Pixels> {
351        if matches!(&self.phase, ElementDrawPhase::Start) {
352            self.request_layout(cx);
353        }
354
355        let layout_id = match &mut self.phase {
356            ElementDrawPhase::LayoutRequested {
357                layout_id,
358                frame_state,
359            } => {
360                cx.compute_layout(*layout_id, available_space);
361                let layout_id = *layout_id;
362                self.phase = ElementDrawPhase::LayoutComputed {
363                    layout_id,
364                    available_space,
365                    frame_state: frame_state.take(),
366                };
367                layout_id
368            }
369            ElementDrawPhase::LayoutComputed {
370                layout_id,
371                available_space: prev_available_space,
372                ..
373            } => {
374                if available_space != *prev_available_space {
375                    cx.compute_layout(*layout_id, available_space);
376                    *prev_available_space = available_space;
377                }
378                *layout_id
379            }
380            _ => panic!("cannot measure after painting"),
381        };
382
383        cx.layout_bounds(layout_id).size
384    }
385
386    fn draw(
387        mut self,
388        origin: Point<Pixels>,
389        available_space: Size<AvailableSpace>,
390        cx: &mut ElementContext,
391    ) -> Option<E::State> {
392        self.measure(available_space, cx);
393        cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
394    }
395}
396
397impl<E> ElementObject for Option<DrawableElement<E>>
398where
399    E: Element,
400    E::State: 'static,
401{
402    fn element_id(&self) -> Option<ElementId> {
403        self.as_ref().unwrap().element_id()
404    }
405
406    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
407        DrawableElement::request_layout(self.as_mut().unwrap(), cx)
408    }
409
410    fn paint(&mut self, cx: &mut ElementContext) {
411        DrawableElement::paint(self.take().unwrap(), cx);
412    }
413
414    fn measure(
415        &mut self,
416        available_space: Size<AvailableSpace>,
417        cx: &mut ElementContext,
418    ) -> Size<Pixels> {
419        DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
420    }
421
422    fn draw(
423        &mut self,
424        origin: Point<Pixels>,
425        available_space: Size<AvailableSpace>,
426        cx: &mut ElementContext,
427    ) {
428        DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
429    }
430}
431
432/// A dynamically typed element that can be used to store any element type.
433pub struct AnyElement(ArenaBox<dyn ElementObject>);
434
435impl AnyElement {
436    pub(crate) fn new<E>(element: E) -> Self
437    where
438        E: 'static + Element,
439        E::State: Any,
440    {
441        let element = ELEMENT_ARENA
442            .with_borrow_mut(|arena| arena.alloc(|| Some(DrawableElement::new(element))))
443            .map(|element| element as &mut dyn ElementObject);
444        AnyElement(element)
445    }
446
447    /// Request the layout ID of the element stored in this `AnyElement`.
448    /// Used for laying out child elements in a parent element.
449    pub fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
450        self.0.request_layout(cx)
451    }
452
453    /// Paints the element stored in this `AnyElement`.
454    pub fn paint(&mut self, cx: &mut ElementContext) {
455        self.0.paint(cx)
456    }
457
458    /// Initializes this element and performs layout within the given available space to determine its size.
459    pub fn measure(
460        &mut self,
461        available_space: Size<AvailableSpace>,
462        cx: &mut ElementContext,
463    ) -> Size<Pixels> {
464        self.0.measure(available_space, cx)
465    }
466
467    /// Initializes this element and performs layout in the available space, then paints it at the given origin.
468    pub fn draw(
469        &mut self,
470        origin: Point<Pixels>,
471        available_space: Size<AvailableSpace>,
472        cx: &mut ElementContext,
473    ) {
474        self.0.draw(origin, available_space, cx)
475    }
476
477    /// Returns the element ID of the element stored in this `AnyElement`, if any.
478    pub fn inner_id(&self) -> Option<ElementId> {
479        self.0.element_id()
480    }
481}
482
483impl Element for AnyElement {
484    type State = ();
485
486    fn request_layout(
487        &mut self,
488        _: Option<Self::State>,
489        cx: &mut ElementContext,
490    ) -> (LayoutId, Self::State) {
491        let layout_id = self.request_layout(cx);
492        (layout_id, ())
493    }
494
495    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut ElementContext) {
496        self.paint(cx)
497    }
498}
499
500impl IntoElement for AnyElement {
501    type Element = Self;
502
503    fn element_id(&self) -> Option<ElementId> {
504        None
505    }
506
507    fn into_element(self) -> Self::Element {
508        self
509    }
510
511    fn into_any_element(self) -> AnyElement {
512        self
513    }
514}
515
516/// The empty element, which renders nothing.
517pub type Empty = ();
518
519impl IntoElement for () {
520    type Element = Self;
521
522    fn element_id(&self) -> Option<ElementId> {
523        None
524    }
525
526    fn into_element(self) -> Self::Element {
527        self
528    }
529}
530
531impl Element for () {
532    type State = ();
533
534    fn request_layout(
535        &mut self,
536        _state: Option<Self::State>,
537        cx: &mut ElementContext,
538    ) -> (LayoutId, Self::State) {
539        (cx.request_layout(&crate::Style::default(), None), ())
540    }
541
542    fn paint(
543        &mut self,
544        _bounds: Bounds<Pixels>,
545        _state: &mut Self::State,
546        _cx: &mut ElementContext,
547    ) {
548    }
549}