Skip to main content

Element

Trait Element 

Source
pub trait Element: 'static + IntoElement {
    type RequestLayoutState: 'static;
    type PrepaintState: 'static;

    // Required methods
    fn id(&self) -> Option<ElementId>;
    fn source_location(&self) -> Option<&'static Location<'static>>;
    fn request_layout(
        &mut self,
        id: Option<&GlobalElementId>,
        inspector_id: Option<&InspectorElementId>,
        window: &mut Window,
        cx: &mut App,
    ) -> (LayoutId, Self::RequestLayoutState);
    fn prepaint(
        &mut self,
        id: Option<&GlobalElementId>,
        inspector_id: Option<&InspectorElementId>,
        bounds: Bounds<Pixels>,
        request_layout: &mut Self::RequestLayoutState,
        window: &mut Window,
        cx: &mut App,
    ) -> Self::PrepaintState;
    fn paint(
        &mut self,
        id: Option<&GlobalElementId>,
        inspector_id: Option<&InspectorElementId>,
        bounds: Bounds<Pixels>,
        request_layout: &mut Self::RequestLayoutState,
        prepaint: &mut Self::PrepaintState,
        window: &mut Window,
        cx: &mut App,
    );

    // Provided methods
    fn a11y_role(&self) -> Option<Role> { ... }
    fn is_a11y_hidden(&self) -> bool { ... }
    fn write_a11y_info(&self, _node: &mut Node) { ... }
    fn a11y_synthetic_children(
        &mut self,
        _prepaint: &mut Self::PrepaintState,
        _builder: &mut A11ySubtreeBuilder<'_>,
    ) { ... }
    fn into_any(self) -> AnyElement { ... }
}
Expand description

A part of a window’s element tree that lays itself out and paints itself.

GPUI draws each element in three phases. Each phase runs once per frame, in this order:

  1. request_layout asks Taffy for a LayoutId that describes the element’s size and position rules. It returns the element’s RequestLayoutState. The element has no bounds yet.
  2. prepaint receives the element’s final Bounds and returns its PrepaintState. Insert hitboxes and compute anything that paint needs here.
  3. paint receives the bounds and both states, and draws. Add primitives to the scene and register event listeners here.

The two associated state types carry data from one phase to the next:

  • RequestLayoutState is produced by request_layout, and prepaint and paint each get a &mut to it. Use it for what layout produced, such as the ids of child layouts or a measured text shape.
  • PrepaintState is produced by prepaint, and paint gets a &mut to it. Use it for what needs the final bounds, such as a Hitbox or the position of a cursor.

Use () for a state type when the element has nothing to pass on.

A parent calls its children’s phases from inside its own. A child’s request_layout therefore runs during its parent’s, and its prepaint and paint run while the parent’s do. Call Window::insert_hitbox from prepaint, and Window::on_mouse_event or Window::paint_quad from paint. Window methods check the phase they run in with debug assertions, so calling one in the wrong phase fails in debug builds.

Element::id returns the element’s identity, if it has one. An element with an id receives a GlobalElementId in all three phases, so it can read and write state that survives from frame to frame. An element without one receives None. See element identity.

§Example

This element draws nothing. It records the phases it runs in, and the example checks their order:

use std::{cell::RefCell, rc::Rc};

use gpui::{
    App, Bounds, Context, Element, ElementId, GlobalElementId, InspectorElementId,
    IntoElement, LayoutId, Pixels, Render, Style, TestApp, Window, div, prelude::*,
};

#[derive(Clone, Default)]
struct Log(Rc<RefCell<Vec<&'static str>>>);

struct Probe {
    log: Log,
}

impl IntoElement for Probe {
    type Element = Self;

    fn into_element(self) -> Self::Element {
        self
    }
}

impl Element for Probe {
    type RequestLayoutState = ();
    type PrepaintState = Bounds<Pixels>;

    fn id(&self) -> Option<ElementId> {
        None
    }

    fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
        None
    }

    fn request_layout(
        &mut self,
        _id: Option<&GlobalElementId>,
        _inspector_id: Option<&InspectorElementId>,
        window: &mut Window,
        cx: &mut App,
    ) -> (LayoutId, Self::RequestLayoutState) {
        self.log.0.borrow_mut().push("request_layout");
        (window.request_layout(Style::default(), [], cx), ())
    }

    fn prepaint(
        &mut self,
        _id: Option<&GlobalElementId>,
        _inspector_id: Option<&InspectorElementId>,
        bounds: Bounds<Pixels>,
        _request_layout: &mut Self::RequestLayoutState,
        _window: &mut Window,
        _cx: &mut App,
    ) -> Self::PrepaintState {
        self.log.0.borrow_mut().push("prepaint");
        bounds
    }

    fn paint(
        &mut self,
        _id: Option<&GlobalElementId>,
        _inspector_id: Option<&InspectorElementId>,
        _bounds: Bounds<Pixels>,
        _request_layout: &mut Self::RequestLayoutState,
        _prepaint: &mut Self::PrepaintState,
        _window: &mut Window,
        _cx: &mut App,
    ) {
        self.log.0.borrow_mut().push("paint");
    }
}

struct ProbeView {
    log: Log,
}

impl Render for ProbeView {
    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
        div().size_full().child(Probe { log: self.log.clone() })
    }
}

let log = Log::default();
let mut app = TestApp::new();
let mut window = app.open_window({
    let log = log.clone();
    move |_window, _cx| ProbeView { log }
});

log.0.borrow_mut().clear();
// Notify the view so the next draw renders it again instead of reusing its output.
window.update(|_, _, cx| cx.notify());
window.draw();
assert_eq!(*log.0.borrow(), ["request_layout", "prepaint", "paint"]);

For a complete element, see custom_element. It is a slider that inserts a hitbox in prepaint and handles dragging in paint.

Required Associated Types§

Source

type RequestLayoutState: 'static

The state that Element::request_layout returns for the later phases.

prepaint and paint each receive a &mut reference to it. Use () if the element needs no such state.

Source

type PrepaintState: 'static

The state that Element::prepaint returns for Element::paint.

paint receives a &mut reference to it. Use () if the element needs no such state.

Required Methods§

Source

fn id(&self) -> Option<ElementId>

Returns the element’s identity, or None if it has none.

Return an id from any element whose state must survive from one frame to the next, such as a hover, a drag or a scroll offset. GPUI finds that state again by the id’s path from the window root. The id must be stable on every frame; two elements with the same id path share state and accessibility identity. The path includes every ancestor that has an id.

Source

fn source_location(&self) -> Option<&'static Location<'static>>

Returns where this element was created, for the inspector and debug output.

GPUI uses this only in debug builds and with the inspector feature. Return None when the location doesn’t matter. To capture it, mark the constructor #[track_caller] and store std::panic::Location::caller.

Source

fn request_layout( &mut self, id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, window: &mut Window, cx: &mut App, ) -> (LayoutId, Self::RequestLayoutState)

Requests a layout node from Taffy, and returns it with the state the later phases need.

Describe the element’s size and position rules here: build a Style and pass it to Window::request_layout, along with the layout ids of any children. Lay out the children you draw here too, by calling their request_layout before this element’s own. The element has no bounds in this phase.

Source

fn prepaint( &mut self, id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds<Pixels>, request_layout: &mut Self::RequestLayoutState, window: &mut Window, cx: &mut App, ) -> Self::PrepaintState

Runs once the element’s bounds are known, and returns the state that paint needs.

bounds is the element’s final rectangle in window coordinates, in logical pixels. Insert hitboxes with Window::insert_hitbox here, so that the next mouse event can be tested against them. Compute anything paint needs too. Calls that draw, such as Window::paint_quad, belong in paint.

Source

fn paint( &mut self, id: Option<&GlobalElementId>, inspector_id: Option<&InspectorElementId>, bounds: Bounds<Pixels>, request_layout: &mut Self::RequestLayoutState, prepaint: &mut Self::PrepaintState, window: &mut Window, cx: &mut App, )

Draws the element. Runs after prepaint, once per frame.

bounds is the rectangle that prepaint received. Add primitives with the painting methods on Window, such as Window::paint_quad, and register event listeners with Window::on_mouse_event and related methods. Listeners stay registered until the next frame replaces them, so an event is always delivered against the frame the user saw.

Provided Methods§

Source

fn a11y_role(&self) -> Option<Role>

Returns the accessible role for this element, if any. Elements that return None have no accessibility node unless Element::is_a11y_hidden returns true.

Note: inclusion in accessibility tree requires non-None id.

See the accessibility guide for an overview.

Source

fn is_a11y_hidden(&self) -> bool

Whether assistive technology should ignore this element and its descendants.

GPUI gives a hidden element without a role an internal container node. AccessKit uses that node to omit the subtree.

Source

fn write_a11y_info(&self, _node: &mut Node)

Write accessibility properties to the given node. GPUI calls this only when Element::a11y_role returns Some or Element::is_a11y_hidden returns true.

See the accessibility guide for an overview.

Source

fn a11y_synthetic_children( &mut self, _prepaint: &mut Self::PrepaintState, _builder: &mut A11ySubtreeBuilder<'_>, )

Add synthetic child nodes to an Element. The element must have an .id(). It must also have a .role() or return true from Element::is_a11y_hidden.

Some elements may want to inject accessibility nodes that do not correspond to any GPUI element. For example, a custom text field element may want to inject synthetic child nodes for the text content.

See Expose elements that GPUI doesn’t draw in the accessibility guide for more detail.

Source

fn into_any(self) -> AnyElement

Convert this element into a dynamically-typed AnyElement.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementations on Foreign Types§

Source§

impl Element for &'static str

Source§

type RequestLayoutState = TextLayout

Source§

type PrepaintState = ()

Source§

fn id(&self) -> Option<ElementId>

Source§

fn source_location(&self) -> Option<&'static Location<'static>>

Source§

fn request_layout( &mut self, _id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, window: &mut Window, cx: &mut App, ) -> (LayoutId, Self::RequestLayoutState)

Source§

fn prepaint( &mut self, _id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, bounds: Bounds<Pixels>, text_layout: &mut Self::RequestLayoutState, window: &mut Window, _cx: &mut App, )

Source§

fn paint( &mut self, _id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, _bounds: Bounds<Pixels>, text_layout: &mut TextLayout, _: &mut (), window: &mut Window, cx: &mut App, )

Implementors§

Source§

impl Element for Anchored

Source§

impl Element for AnyElement

Source§

impl Element for ContainerQuery

Source§

impl Element for Deferred

Source§

impl Element for Div

Source§

impl Element for Empty

Source§

impl Element for ImageCacheElement

Source§

impl Element for Img

Source§

impl Element for InteractiveText

Source§

impl Element for List

Source§

impl Element for SharedString

Source§

impl Element for StyledText

Source§

impl Element for Surface

Source§

impl Element for Svg

Source§

impl Element for Text

Source§

impl Element for UniformList

Source§

impl<E: IntoElement + 'static> Element for AnimationElement<E>

Source§

impl<E: IntoElement + 'static> Element for SpringAnimationElement<E>

Source§

impl<E> Element for Stateful<E>
where E: Element,

Source§

impl<T: 'static> Element for Canvas<T>