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/// A quick way to create a [`Render`]able view without having to define a new type.
140#[cfg(any(test, feature = "test-support"))]
141pub struct TestView(Box<dyn FnMut(&mut ViewContext<TestView>) -> AnyElement>);
142
143#[cfg(any(test, feature = "test-support"))]
144impl TestView {
145    /// Construct a TestView from a render closure.
146    pub fn new<F: FnMut(&mut ViewContext<TestView>) -> AnyElement + 'static>(f: F) -> Self {
147        Self(Box::new(f))
148    }
149}
150
151#[cfg(any(test, feature = "test-support"))]
152impl Render for TestView {
153    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
154        (self.0)(cx)
155    }
156}
157
158/// You can derive [`IntoElement`] on any type that implements this trait.
159/// It is used to construct reusable `components` out of plain data. Think of
160/// components as a recipe for a certain pattern of elements. RenderOnce allows
161/// you to invoke this pattern, without breaking the fluent builder pattern of
162/// the element APIs.
163pub trait RenderOnce: 'static {
164    /// Render this component into an element tree. Note that this method
165    /// takes ownership of self, as compared to [`Render::render()`] method
166    /// which takes a mutable reference.
167    fn render(self, cx: &mut WindowContext) -> impl IntoElement;
168}
169
170/// This is a helper trait to provide a uniform interface for constructing elements that
171/// can accept any number of any kind of child elements
172pub trait ParentElement {
173    /// Extend this element's children with the given child elements.
174    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>);
175
176    /// Add a single child element to this element.
177    fn child(mut self, child: impl IntoElement) -> Self
178    where
179        Self: Sized,
180    {
181        self.extend(std::iter::once(child.into_element().into_any()));
182        self
183    }
184
185    /// Add multiple child elements to this element.
186    fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
187    where
188        Self: Sized,
189    {
190        self.extend(children.into_iter().map(|child| child.into_any_element()));
191        self
192    }
193}
194
195/// An element for rendering components. An implementation detail of the [`IntoElement`] derive macro
196/// for [`RenderOnce`]
197#[doc(hidden)]
198pub struct Component<C: RenderOnce>(Option<C>);
199
200impl<C: RenderOnce> Component<C> {
201    /// Create a new component from the given RenderOnce type.
202    pub fn new(component: C) -> Self {
203        Component(Some(component))
204    }
205}
206
207impl<C: RenderOnce> Element for Component<C> {
208    type State = AnyElement;
209
210    fn request_layout(
211        &mut self,
212        _: Option<Self::State>,
213        cx: &mut ElementContext,
214    ) -> (LayoutId, Self::State) {
215        let mut element = self
216            .0
217            .take()
218            .unwrap()
219            .render(cx.deref_mut())
220            .into_any_element();
221        let layout_id = element.request_layout(cx);
222        (layout_id, element)
223    }
224
225    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
226        element.paint(cx)
227    }
228}
229
230impl<C: RenderOnce> IntoElement for Component<C> {
231    type Element = Self;
232
233    fn element_id(&self) -> Option<ElementId> {
234        None
235    }
236
237    fn into_element(self) -> Self::Element {
238        self
239    }
240}
241
242/// A globally unique identifier for an element, used to track state across frames.
243#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
244pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
245
246trait ElementObject {
247    fn element_id(&self) -> Option<ElementId>;
248
249    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
250
251    fn paint(&mut self, cx: &mut ElementContext);
252
253    fn measure(
254        &mut self,
255        available_space: Size<AvailableSpace>,
256        cx: &mut ElementContext,
257    ) -> Size<Pixels>;
258
259    fn draw(
260        &mut self,
261        origin: Point<Pixels>,
262        available_space: Size<AvailableSpace>,
263        cx: &mut ElementContext,
264    );
265}
266
267/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
268pub(crate) struct DrawableElement<E: Element> {
269    element: Option<E>,
270    phase: ElementDrawPhase<E::State>,
271}
272
273#[derive(Default)]
274enum ElementDrawPhase<S> {
275    #[default]
276    Start,
277    LayoutRequested {
278        layout_id: LayoutId,
279        frame_state: Option<S>,
280    },
281    LayoutComputed {
282        layout_id: LayoutId,
283        available_space: Size<AvailableSpace>,
284        frame_state: Option<S>,
285    },
286}
287
288/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
289impl<E: Element> DrawableElement<E> {
290    fn new(element: E) -> Self {
291        DrawableElement {
292            element: Some(element),
293            phase: ElementDrawPhase::Start,
294        }
295    }
296
297    fn element_id(&self) -> Option<ElementId> {
298        self.element.as_ref()?.element_id()
299    }
300
301    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
302        let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
303        {
304            let layout_id = cx.with_element_state(id, |element_state, cx| {
305                self.element
306                    .as_mut()
307                    .unwrap()
308                    .request_layout(element_state, cx)
309            });
310            (layout_id, None)
311        } else {
312            let (layout_id, frame_state) = self.element.as_mut().unwrap().request_layout(None, cx);
313            (layout_id, Some(frame_state))
314        };
315
316        self.phase = ElementDrawPhase::LayoutRequested {
317            layout_id,
318            frame_state,
319        };
320        layout_id
321    }
322
323    fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
324        match self.phase {
325            ElementDrawPhase::LayoutRequested {
326                layout_id,
327                frame_state,
328            }
329            | ElementDrawPhase::LayoutComputed {
330                layout_id,
331                frame_state,
332                ..
333            } => {
334                let bounds = cx.layout_bounds(layout_id);
335
336                if let Some(mut frame_state) = frame_state {
337                    self.element
338                        .take()
339                        .unwrap()
340                        .paint(bounds, &mut frame_state, cx);
341                    Some(frame_state)
342                } else {
343                    let element_id = self
344                        .element
345                        .as_ref()
346                        .unwrap()
347                        .element_id()
348                        .expect("if we don't have frame state, we should have element state");
349                    cx.with_element_state(element_id, |element_state, cx| {
350                        let mut element_state = element_state.unwrap();
351                        self.element
352                            .take()
353                            .unwrap()
354                            .paint(bounds, &mut element_state, cx);
355                        ((), element_state)
356                    });
357                    None
358                }
359            }
360
361            _ => panic!("must call layout before paint"),
362        }
363    }
364
365    fn measure(
366        &mut self,
367        available_space: Size<AvailableSpace>,
368        cx: &mut ElementContext,
369    ) -> Size<Pixels> {
370        if matches!(&self.phase, ElementDrawPhase::Start) {
371            self.request_layout(cx);
372        }
373
374        let layout_id = match &mut self.phase {
375            ElementDrawPhase::LayoutRequested {
376                layout_id,
377                frame_state,
378            } => {
379                cx.compute_layout(*layout_id, available_space);
380                let layout_id = *layout_id;
381                self.phase = ElementDrawPhase::LayoutComputed {
382                    layout_id,
383                    available_space,
384                    frame_state: frame_state.take(),
385                };
386                layout_id
387            }
388            ElementDrawPhase::LayoutComputed {
389                layout_id,
390                available_space: prev_available_space,
391                ..
392            } => {
393                if available_space != *prev_available_space {
394                    cx.compute_layout(*layout_id, available_space);
395                    *prev_available_space = available_space;
396                }
397                *layout_id
398            }
399            _ => panic!("cannot measure after painting"),
400        };
401
402        cx.layout_bounds(layout_id).size
403    }
404
405    fn draw(
406        mut self,
407        origin: Point<Pixels>,
408        available_space: Size<AvailableSpace>,
409        cx: &mut ElementContext,
410    ) -> Option<E::State> {
411        self.measure(available_space, cx);
412        cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
413    }
414}
415
416impl<E> ElementObject for Option<DrawableElement<E>>
417where
418    E: Element,
419    E::State: 'static,
420{
421    fn element_id(&self) -> Option<ElementId> {
422        self.as_ref().unwrap().element_id()
423    }
424
425    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
426        DrawableElement::request_layout(self.as_mut().unwrap(), cx)
427    }
428
429    fn paint(&mut self, cx: &mut ElementContext) {
430        DrawableElement::paint(self.take().unwrap(), cx);
431    }
432
433    fn measure(
434        &mut self,
435        available_space: Size<AvailableSpace>,
436        cx: &mut ElementContext,
437    ) -> Size<Pixels> {
438        DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
439    }
440
441    fn draw(
442        &mut self,
443        origin: Point<Pixels>,
444        available_space: Size<AvailableSpace>,
445        cx: &mut ElementContext,
446    ) {
447        DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
448    }
449}
450
451/// A dynamically typed element that can be used to store any element type.
452pub struct AnyElement(ArenaBox<dyn ElementObject>);
453
454impl AnyElement {
455    pub(crate) fn new<E>(element: E) -> Self
456    where
457        E: 'static + Element,
458        E::State: Any,
459    {
460        let element = ELEMENT_ARENA
461            .with_borrow_mut(|arena| arena.alloc(|| Some(DrawableElement::new(element))))
462            .map(|element| element as &mut dyn ElementObject);
463        AnyElement(element)
464    }
465
466    /// Request the layout ID of the element stored in this `AnyElement`.
467    /// Used for laying out child elements in a parent element.
468    pub fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
469        self.0.request_layout(cx)
470    }
471
472    /// Paints the element stored in this `AnyElement`.
473    pub fn paint(&mut self, cx: &mut ElementContext) {
474        self.0.paint(cx)
475    }
476
477    /// Initializes this element and performs layout within the given available space to determine its size.
478    pub fn measure(
479        &mut self,
480        available_space: Size<AvailableSpace>,
481        cx: &mut ElementContext,
482    ) -> Size<Pixels> {
483        self.0.measure(available_space, cx)
484    }
485
486    /// Initializes this element and performs layout in the available space, then paints it at the given origin.
487    pub fn draw(
488        &mut self,
489        origin: Point<Pixels>,
490        available_space: Size<AvailableSpace>,
491        cx: &mut ElementContext,
492    ) {
493        self.0.draw(origin, available_space, cx)
494    }
495
496    /// Returns the element ID of the element stored in this `AnyElement`, if any.
497    pub fn inner_id(&self) -> Option<ElementId> {
498        self.0.element_id()
499    }
500}
501
502impl Element for AnyElement {
503    type State = ();
504
505    fn request_layout(
506        &mut self,
507        _: Option<Self::State>,
508        cx: &mut ElementContext,
509    ) -> (LayoutId, Self::State) {
510        let layout_id = self.request_layout(cx);
511        (layout_id, ())
512    }
513
514    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut ElementContext) {
515        self.paint(cx)
516    }
517}
518
519impl IntoElement for AnyElement {
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    fn into_any_element(self) -> AnyElement {
531        self
532    }
533}
534
535/// The empty element, which renders nothing.
536pub type Empty = ();
537
538impl IntoElement for () {
539    type Element = Self;
540
541    fn element_id(&self) -> Option<ElementId> {
542        None
543    }
544
545    fn into_element(self) -> Self::Element {
546        self
547    }
548}
549
550impl Element for () {
551    type State = ();
552
553    fn request_layout(
554        &mut self,
555        _state: Option<Self::State>,
556        cx: &mut ElementContext,
557    ) -> (LayoutId, Self::State) {
558        (cx.request_layout(&crate::Style::default(), None), ())
559    }
560
561    fn paint(
562        &mut self,
563        _bounds: Bounds<Pixels>,
564        _state: &mut Self::State,
565        _cx: &mut ElementContext,
566    ) {
567    }
568}