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:
request_layoutasks Taffy for aLayoutIdthat describes the element’s size and position rules. It returns the element’sRequestLayoutState. The element has no bounds yet.prepaintreceives the element’s finalBoundsand returns itsPrepaintState. Insert hitboxes and compute anything thatpaintneeds here.paintreceives 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:
RequestLayoutStateis produced byrequest_layout, andprepaintandpainteach get a&mutto it. Use it for what layout produced, such as the ids of child layouts or a measured text shape.PrepaintStateis produced byprepaint, andpaintgets a&mutto it. Use it for what needs the final bounds, such as aHitboxor 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§
Sourcetype RequestLayoutState: 'static
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.
Sourcetype PrepaintState: 'static
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§
Sourcefn id(&self) -> Option<ElementId>
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.
Sourcefn source_location(&self) -> Option<&'static Location<'static>>
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.
Sourcefn request_layout(
&mut self,
id: Option<&GlobalElementId>,
inspector_id: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState)
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.
Sourcefn 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 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.
Sourcefn 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,
)
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§
Sourcefn a11y_role(&self) -> Option<Role>
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.
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.
Sourcefn write_a11y_info(&self, _node: &mut Node)
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.
Sourcefn a11y_synthetic_children(
&mut self,
_prepaint: &mut Self::PrepaintState,
_builder: &mut A11ySubtreeBuilder<'_>,
)
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.
Sourcefn into_any(self) -> AnyElement
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".