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