gpui/element.rs
1//! Elements lay out and paint everything in a window. They form a tree, and their layout follows
2//! the web layout rules implemented by [taffy](https://github.com/DioxusLabs/taffy). Most of the
3//! time you won't use this module directly. The elements behind [`div`](crate::div) and friends have their
4//! own APIs, and GPUI uses the traits in this module to turn the element tree into pixels.
5//!
6//! # Element basics
7//!
8//! GPUI builds the element tree by calling [`Render::render`] on the root view of a window. It
9//! then lays the tree out with Taffy and paints it, one [`Element`] at a time. When the frame is
10//! done, the tree and the callbacks it registered are dropped, and the next frame builds a new one.
11//! [The frame](crate::_documentation::concepts::the_frame) describes this sequence in full.
12//!
13//! Elements hold no state between frames, because they are rebuilt every frame. State that
14//! must survive a frame lives in an entity, or in element state keyed by a [`GlobalElementId`].
15//! An element can also pass small pieces of state from one of its phases to the next through its
16//! associated types. [`Element`] explains the three phases.
17//!
18//! # Implementing your own elements
19//!
20//! Elements are the low-level, imperative API of GPUI. Most elements stay inside the bounds their
21//! parent gives them, which keeps GPUI's layout guarantees. [`deferred`](crate::deferred) lets an
22//! element paint outside those bounds, which overlays and popups need. Use that power with care.
23//!
24//! Most of the time you don't need a custom element. Build components from the built-in elements
25//! instead, with [`RenderOnce`] and `#[derive(IntoElement)]`. Write an [`Element`] only when you
26//! need your own layout or painting, such as a custom layout algorithm or a code editor. The
27//! [`custom_element`](crate::_documentation::examples::custom_element) example is a complete one.
28
29#[cfg(any(feature = "inspector", debug_assertions))]
30use crate::InspectorElementPath;
31use crate::{
32 A11ySubtreeBuilder, App, ArenaBox, AvailableSpace, Bounds, Context, DispatchNodeId, ElementId,
33 FocusHandle, InspectorElementId, LayoutId, Pixels, Point, Size, Style, Window,
34 util::FluentBuilder, window::with_element_arena,
35};
36use std::{
37 any::Any,
38 fmt::{self, Debug, Display},
39 mem, panic,
40 sync::Arc,
41};
42
43/// A part of a window's element tree that lays itself out and paints itself.
44///
45/// GPUI draws each element in three phases. Each phase runs once per frame, in this order:
46///
47/// 1. [`request_layout`](Element::request_layout) asks Taffy for a [`LayoutId`] that describes the
48/// element's size and position rules. It returns the element's
49/// [`RequestLayoutState`](Element::RequestLayoutState). The element has no bounds yet.
50/// 2. [`prepaint`](Element::prepaint) receives the element's final [`Bounds`] and returns its
51/// [`PrepaintState`](Element::PrepaintState). Insert hitboxes and compute anything that `paint`
52/// needs here.
53/// 3. [`paint`](Element::paint) receives the bounds and both states, and draws. Add primitives to
54/// the scene and register event listeners here.
55///
56/// The two associated state types carry data from one phase to the next:
57///
58/// * `RequestLayoutState` is produced by `request_layout`, and `prepaint` and `paint` each get a
59/// `&mut` to it. Use it for what layout produced, such as the ids of child layouts or a measured
60/// text shape.
61/// * `PrepaintState` is produced by `prepaint`, and `paint` gets a `&mut` to it. Use it for what
62/// needs the final bounds, such as a [`Hitbox`](crate::Hitbox) or the position of a cursor.
63///
64/// Use `()` for a state type when the element has nothing to pass on.
65///
66/// A parent calls its children's phases from inside its own. A child's `request_layout` therefore
67/// runs during its parent's, and its `prepaint` and `paint` run while the parent's do. Call
68/// [`Window::insert_hitbox`] from `prepaint`, and [`Window::on_mouse_event`] or
69/// [`Window::paint_quad`] from `paint`. Window methods check the phase they run in with debug
70/// assertions, so calling one in the wrong phase fails in debug builds.
71///
72/// [`Element::id`] returns the element's identity, if it has one. An element with an id receives a
73/// [`GlobalElementId`] in all three phases, so it can read and write state that survives from frame
74/// to frame. An element without one receives `None`. See
75/// [element identity](crate::_documentation::concepts::elements#identity-and-element-state).
76///
77/// # Example
78///
79/// This element draws nothing. It records the phases it runs in, and the example checks their
80/// order:
81///
82/// ```
83/// use std::{cell::RefCell, rc::Rc};
84///
85/// use gpui::{
86/// App, Bounds, Context, Element, ElementId, GlobalElementId, InspectorElementId,
87/// IntoElement, LayoutId, Pixels, Render, Style, TestApp, Window, div, prelude::*,
88/// };
89///
90/// #[derive(Clone, Default)]
91/// struct Log(Rc<RefCell<Vec<&'static str>>>);
92///
93/// struct Probe {
94/// log: Log,
95/// }
96///
97/// impl IntoElement for Probe {
98/// type Element = Self;
99///
100/// fn into_element(self) -> Self::Element {
101/// self
102/// }
103/// }
104///
105/// impl Element for Probe {
106/// type RequestLayoutState = ();
107/// type PrepaintState = Bounds<Pixels>;
108///
109/// fn id(&self) -> Option<ElementId> {
110/// None
111/// }
112///
113/// fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
114/// None
115/// }
116///
117/// fn request_layout(
118/// &mut self,
119/// _id: Option<&GlobalElementId>,
120/// _inspector_id: Option<&InspectorElementId>,
121/// window: &mut Window,
122/// cx: &mut App,
123/// ) -> (LayoutId, Self::RequestLayoutState) {
124/// self.log.0.borrow_mut().push("request_layout");
125/// (window.request_layout(Style::default(), [], cx), ())
126/// }
127///
128/// fn prepaint(
129/// &mut self,
130/// _id: Option<&GlobalElementId>,
131/// _inspector_id: Option<&InspectorElementId>,
132/// bounds: Bounds<Pixels>,
133/// _request_layout: &mut Self::RequestLayoutState,
134/// _window: &mut Window,
135/// _cx: &mut App,
136/// ) -> Self::PrepaintState {
137/// self.log.0.borrow_mut().push("prepaint");
138/// bounds
139/// }
140///
141/// fn paint(
142/// &mut self,
143/// _id: Option<&GlobalElementId>,
144/// _inspector_id: Option<&InspectorElementId>,
145/// _bounds: Bounds<Pixels>,
146/// _request_layout: &mut Self::RequestLayoutState,
147/// _prepaint: &mut Self::PrepaintState,
148/// _window: &mut Window,
149/// _cx: &mut App,
150/// ) {
151/// self.log.0.borrow_mut().push("paint");
152/// }
153/// }
154///
155/// struct ProbeView {
156/// log: Log,
157/// }
158///
159/// impl Render for ProbeView {
160/// fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
161/// div().size_full().child(Probe { log: self.log.clone() })
162/// }
163/// }
164///
165/// let log = Log::default();
166/// let mut app = TestApp::new();
167/// let mut window = app.open_window({
168/// let log = log.clone();
169/// move |_window, _cx| ProbeView { log }
170/// });
171///
172/// log.0.borrow_mut().clear();
173/// // Notify the view so the next draw renders it again instead of reusing its output.
174/// window.update(|_, _, cx| cx.notify());
175/// window.draw();
176/// assert_eq!(*log.0.borrow(), ["request_layout", "prepaint", "paint"]);
177/// ```
178///
179/// For a complete element, see
180/// [`custom_element`](crate::_documentation::examples::custom_element). It is a slider that inserts
181/// a hitbox in `prepaint` and handles dragging in `paint`.
182pub trait Element: 'static + IntoElement {
183 /// The state that [`Element::request_layout`] returns for the later phases.
184 ///
185 /// `prepaint` and `paint` each receive a `&mut` reference to it. Use `()` if the element needs
186 /// no such state.
187 type RequestLayoutState: 'static;
188
189 /// The state that [`Element::prepaint`] returns for [`Element::paint`].
190 ///
191 /// `paint` receives a `&mut` reference to it. Use `()` if the element needs no such state.
192 type PrepaintState: 'static;
193
194 /// Returns the element's identity, or `None` if it has none.
195 ///
196 /// Return an id from any element whose state must survive from one frame to the next, such as
197 /// a hover, a drag or a scroll offset. GPUI finds that state again by the id's path from the
198 /// window root. The id must be stable on every frame; two elements with the same id path share
199 /// state and accessibility identity. The path includes every ancestor that has an id.
200 fn id(&self) -> Option<ElementId>;
201
202 /// Returns where this element was created, for the inspector and debug output.
203 ///
204 /// GPUI uses this only in debug builds and with the `inspector` feature. Return `None` when the
205 /// location doesn't matter. To capture it, mark the constructor `#[track_caller]` and store
206 /// [`std::panic::Location::caller`].
207 fn source_location(&self) -> Option<&'static panic::Location<'static>>;
208
209 /// Requests a layout node from Taffy, and returns it with the state the later phases need.
210 ///
211 /// Describe the element's size and position rules here: build a [`Style`] and pass it to
212 /// [`Window::request_layout`], along with the layout ids of any children. Lay out the children
213 /// you draw here too, by calling their `request_layout` before this element's own. The element
214 /// has no bounds in this phase.
215 fn request_layout(
216 &mut self,
217 id: Option<&GlobalElementId>,
218 inspector_id: Option<&InspectorElementId>,
219 window: &mut Window,
220 cx: &mut App,
221 ) -> (LayoutId, Self::RequestLayoutState);
222
223 /// Runs once the element's bounds are known, and returns the state that `paint` needs.
224 ///
225 /// `bounds` is the element's final rectangle in window coordinates, in logical pixels. Insert
226 /// hitboxes with [`Window::insert_hitbox`] here, so that the next mouse event can be tested
227 /// against them. Compute anything `paint` needs too. Calls that draw, such as
228 /// [`Window::paint_quad`], belong in `paint`.
229 fn prepaint(
230 &mut self,
231 id: Option<&GlobalElementId>,
232 inspector_id: Option<&InspectorElementId>,
233 bounds: Bounds<Pixels>,
234 request_layout: &mut Self::RequestLayoutState,
235 window: &mut Window,
236 cx: &mut App,
237 ) -> Self::PrepaintState;
238
239 /// Draws the element. Runs after `prepaint`, once per frame.
240 ///
241 /// `bounds` is the rectangle that `prepaint` received. Add primitives with the painting methods
242 /// on [`Window`], such as [`Window::paint_quad`], and register event listeners with
243 /// [`Window::on_mouse_event`] and related methods. Listeners stay registered until the next
244 /// frame replaces them, so an event is always delivered against the frame the user saw.
245 fn paint(
246 &mut self,
247 id: Option<&GlobalElementId>,
248 inspector_id: Option<&InspectorElementId>,
249 bounds: Bounds<Pixels>,
250 request_layout: &mut Self::RequestLayoutState,
251 prepaint: &mut Self::PrepaintState,
252 window: &mut Window,
253 cx: &mut App,
254 );
255
256 /// Returns the accessible role for this element, if any.
257 /// Elements that return `None` have no accessibility node unless
258 /// [`Element::is_a11y_hidden`] returns `true`.
259 ///
260 /// Note: inclusion in accessibility tree requires non-`None` [`id`][Element::id].
261 ///
262 /// See the [accessibility guide](crate::_documentation::guides::accessibility) for an overview.
263 fn a11y_role(&self) -> Option<accesskit::Role> {
264 None
265 }
266
267 /// Whether assistive technology should ignore this element and its
268 /// descendants.
269 ///
270 /// GPUI gives a hidden element without a role an internal container node.
271 /// AccessKit uses that node to omit the subtree.
272 fn is_a11y_hidden(&self) -> bool {
273 false
274 }
275
276 /// Write accessibility properties to the given node.
277 /// GPUI calls this only when [`Element::a11y_role`] returns `Some` or
278 /// [`Element::is_a11y_hidden`] returns `true`.
279 ///
280 /// See the [accessibility guide](crate::_documentation::guides::accessibility) for an overview.
281 fn write_a11y_info(&self, _node: &mut accesskit::Node) {}
282
283 /// Add synthetic child nodes to an [`Element`]. The element must have an
284 /// [`.id()`][Element::id]. It must also have a
285 /// [`.role()`][Element::a11y_role] or return `true` from
286 /// [`Element::is_a11y_hidden`].
287 ///
288 /// Some elements may want to inject accessibility nodes that do not
289 /// correspond to any GPUI element. For example, a custom text field element
290 /// may want to inject synthetic child nodes for the text content.
291 ///
292 /// See [Expose elements that GPUI doesn't draw](crate::_documentation::guides::accessibility#expose-elements-that-gpui-doesnt-draw-as-nodes) in
293 /// the accessibility guide for more detail.
294 fn a11y_synthetic_children(
295 &mut self,
296 _prepaint: &mut Self::PrepaintState,
297 _builder: &mut A11ySubtreeBuilder,
298 ) {
299 }
300
301 /// Convert this element into a dynamically-typed [`AnyElement`].
302 fn into_any(self) -> AnyElement {
303 AnyElement::new(self)
304 }
305}
306
307/// A type that can be converted into an [`Element`], so it can be a child of another element.
308///
309/// [`ParentElement::child`] takes any `IntoElement`. Strings, [`Entity`](crate::Entity) views and every element
310/// implement it. Derive it with `#[derive(IntoElement)]` on a [`RenderOnce`] component to make the
311/// component usable wherever an element is.
312///
313/// # Example
314///
315/// ```
316/// use gpui::{IntoElement, div, prelude::*};
317///
318/// let row = div().child("Name").child(String::from("Ada"));
319/// let _element = row.into_element();
320/// ```
321pub trait IntoElement: Sized {
322 /// The element type that this type converts into.
323 ///
324 /// Strings convert into text elements. A [`RenderOnce`] component that derives `IntoElement`
325 /// converts into the element that renders it.
326 type Element: Element;
327
328 /// Converts `self` into an [`Element`].
329 ///
330 /// GPUI calls this when it needs the element. You rarely call it yourself, but it is the method
331 /// to implement.
332 fn into_element(self) -> Self::Element;
333
334 /// Converts `self` into an [`AnyElement`], with its concrete type erased.
335 ///
336 /// Use this when two branches return different element types, or a list holds mixed types.
337 fn into_any_element(self) -> AnyElement {
338 self.into_element().into_any()
339 }
340}
341
342impl<T: IntoElement> FluentBuilder for T {}
343
344/// A view: an entity that GPUI draws in a window.
345///
346/// Implement `Render` for the state of a view. GPUI can then draw an [`Entity`](crate::Entity) of that type as an
347/// element, and [`Context::notify`] redraws it. Use a view when a part of the interface has state
348/// that must survive from frame to frame. For stateless pieces, use [`RenderOnce`].
349///
350/// GPUI calls `render` each time it draws the window and reaches this view. The view keeps its
351/// state in `self`, not in the elements it returns. Those elements are consumed by the frame and
352/// dropped, and the next call builds new ones. So `render` should only read state and build
353/// elements from it. GPUI skips the call while nothing the view read has changed; see
354/// [retained view rendering](crate::_documentation::concepts::retained_rendering).
355///
356/// # Example
357///
358/// ```
359/// use std::{cell::Cell, rc::Rc};
360///
361/// use gpui::{Context, IntoElement, Render, TestApp, Window, div, prelude::*};
362///
363/// struct Greeting {
364/// name: String,
365/// renders: Rc<Cell<usize>>,
366/// }
367///
368/// impl Render for Greeting {
369/// fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
370/// self.renders.set(self.renders.get() + 1);
371/// div().child(format!("Hello, {}", self.name))
372/// }
373/// }
374///
375/// let renders = Rc::new(Cell::new(0));
376/// let mut app = TestApp::new();
377/// let mut window = app.open_window({
378/// let renders = renders.clone();
379/// move |_window, _cx| Greeting {
380/// name: "Ada".into(),
381/// renders,
382/// }
383/// });
384///
385/// renders.set(0);
386/// // Notify the view so the next draw renders it again instead of reusing its output.
387/// window.update(|_, _, cx| cx.notify());
388/// window.draw();
389/// assert_eq!(renders.get(), 1);
390/// ```
391pub trait Render: 'static + Sized {
392 /// Builds the view's element tree from its current state.
393 ///
394 /// `window` is the window being drawn, and `cx` gives access to this view through
395 /// [`Context<Self>`](Context). Return the elements the view shows. Don't change state in this
396 /// method, because GPUI may call it many times, or skip it, for one change.
397 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement;
398}
399
400impl Render for Empty {
401 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
402 Empty
403 }
404}
405
406/// A component: a value that describes a pattern of elements and renders it once.
407///
408/// Use `RenderOnce` for reusable pieces of interface that keep no state of their own. Build the
409/// component from plain data, and derive [`IntoElement`] on it so it can be a child of an element.
410/// The component's `render` takes `self` by value, and the component is consumed when it is drawn.
411/// A component can't call [`Context::notify`], because it has no entity. To report events, store a
412/// callback in the component and let the parent view decide what the callback does. Use [`Render`]
413/// for a piece of interface that holds state.
414///
415/// Elements that need state across frames need ids. Pass the id in from the parent, and use it
416/// with [`InteractiveElement::id`](crate::InteractiveElement::id). GPUI scopes a component's elements under the component's type
417/// name, so two instances of the same component side by side would otherwise share their state.
418///
419/// # Example
420///
421/// ```
422/// use std::{cell::Cell, rc::Rc};
423///
424/// use gpui::{App, Context, IntoElement, Render, RenderOnce, TestApp, Window, div, prelude::*};
425///
426/// #[derive(IntoElement)]
427/// struct Badge {
428/// renders: Rc<Cell<usize>>,
429/// }
430///
431/// impl RenderOnce for Badge {
432/// fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
433/// self.renders.set(self.renders.get() + 1);
434/// div().child("3")
435/// }
436/// }
437///
438/// struct Inbox {
439/// renders: Rc<Cell<usize>>,
440/// }
441///
442/// impl Render for Inbox {
443/// fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
444/// div().child(Badge {
445/// renders: self.renders.clone(),
446/// })
447/// }
448/// }
449///
450/// let renders = Rc::new(Cell::new(0));
451/// let mut app = TestApp::new();
452/// let mut window = app.open_window({
453/// let renders = renders.clone();
454/// move |_window, _cx| Inbox { renders }
455/// });
456///
457/// renders.set(0);
458/// // Notify the view so the next draw renders it again instead of reusing its output.
459/// window.update(|_, _, cx| cx.notify());
460/// window.draw();
461/// assert_eq!(renders.get(), 1);
462/// ```
463pub trait RenderOnce: 'static {
464 /// Builds the component's element tree. Takes `self` by value, so the component is used up.
465 ///
466 /// GPUI calls this once for each time the component appears in a frame. Return the elements
467 /// the component shows.
468 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement;
469}
470
471/// A parent element whose children all have one concrete type.
472///
473/// Use it when a container's children carry a shared type, such as rows with their own data.
474/// Most containers use [`ParentElement`], which accepts any [`IntoElement`].
475pub trait ParentElementTyped {
476 /// The type of child that this element accepts.
477 type Child;
478
479 /// Adds the given children to the end of this element's children.
480 fn extend(&mut self, elements: impl IntoIterator<Item = Self::Child>);
481
482 /// Adds one child to this element, and returns the element for chaining.
483 fn child(mut self, child: Self::Child) -> Self
484 where
485 Self: Sized,
486 {
487 self.extend(std::iter::once(child));
488 self
489 }
490
491 /// Adds several children to this element, in iteration order, and returns the element.
492 fn children(mut self, children: impl IntoIterator<Item = Self::Child>) -> Self
493 where
494 Self: Sized,
495 {
496 self.extend(children);
497 self
498 }
499}
500
501/// An element that can hold any number of children of any type.
502///
503/// This is the trait behind `.child()` and `.children()` on containers such as [`div`](crate::div). Each
504/// child is anything that implements [`IntoElement`], so strings, views and other elements can be
505/// mixed in one container.
506pub trait ParentElement {
507 /// Adds the given children to the end of this element's children.
508 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>);
509
510 /// Adds one child to this element, and returns the element for chaining.
511 fn child(mut self, child: impl IntoElement) -> Self
512 where
513 Self: Sized,
514 {
515 self.extend(std::iter::once(child.into_any_element()));
516 self
517 }
518
519 /// Adds several children to this element, in iteration order, and returns the element.
520 fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
521 where
522 Self: Sized,
523 {
524 self.extend(children.into_iter().map(|child| child.into_any_element()));
525 self
526 }
527}
528
529/// A globally unique identifier for an element, used to track state across frames.
530///
531/// The hash of the id path is computed once, when the id is built. Every
532/// element with an id is looked up in the element-state maps of two frames
533/// and recorded as accessed on every frame it is drawn, and each of those
534/// hashes the whole path — every ancestor's id, string names byte by byte —
535/// so on a deep tree the hashing alone was a visible share of a frame.
536#[derive(Clone, Debug)]
537pub struct GlobalElementId {
538 ids: Arc<[ElementId]>,
539 hash: u64,
540}
541
542/// The hash of an empty id path; every path hash is folded from it with
543/// [`extend_path_hash`], one id at a time, so the window can keep the hash
544/// of each prefix of its id stack and never re-hash a path.
545pub(crate) const EMPTY_PATH_HASH: u64 = 0;
546
547/// The hash of the path `prefix` + `id`, given the hash of `prefix`.
548pub(crate) fn extend_path_hash(prefix: u64, id: &ElementId) -> u64 {
549 use std::hash::{Hash, Hasher};
550 let mut hasher = collections::FxHasher::with_seed(prefix as usize);
551 id.hash(&mut hasher);
552 hasher.finish()
553}
554
555impl GlobalElementId {
556 pub(crate) fn new(ids: &[ElementId]) -> Self {
557 let hash = ids.iter().fold(EMPTY_PATH_HASH, extend_path_hash);
558 Self::with_hash(ids, hash)
559 }
560
561 /// `ids` with its hash already known, as the window's id stack keeps it.
562 pub(crate) fn with_hash(ids: &[ElementId], hash: u64) -> Self {
563 debug_assert_eq!(hash, ids.iter().fold(EMPTY_PATH_HASH, extend_path_hash));
564 Self {
565 ids: Arc::from(ids),
566 hash,
567 }
568 }
569
570 pub(crate) fn accesskit_node_id(&self) -> accesskit::NodeId {
571 accesskit::NodeId(self.hash)
572 }
573}
574
575impl Default for GlobalElementId {
576 fn default() -> Self {
577 Self::new(&[])
578 }
579}
580
581impl std::ops::Deref for GlobalElementId {
582 type Target = [ElementId];
583
584 fn deref(&self) -> &Self::Target {
585 &self.ids
586 }
587}
588
589impl PartialEq for GlobalElementId {
590 fn eq(&self, other: &Self) -> bool {
591 self.hash == other.hash && (Arc::ptr_eq(&self.ids, &other.ids) || self.ids == other.ids)
592 }
593}
594
595impl Eq for GlobalElementId {}
596
597impl std::hash::Hash for GlobalElementId {
598 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
599 state.write_u64(self.hash);
600 }
601}
602impl Display for GlobalElementId {
603 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604 for (i, element_id) in self.ids.iter().enumerate() {
605 if i > 0 {
606 write!(f, ".")?;
607 }
608 write!(f, "{}", element_id)?;
609 }
610 Ok(())
611 }
612}
613
614trait ElementObject {
615 fn inner_element(&mut self) -> &mut dyn Any;
616
617 fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId;
618
619 fn prepaint(&mut self, window: &mut Window, cx: &mut App);
620
621 fn paint(&mut self, window: &mut Window, cx: &mut App);
622
623 fn layout_as_root(
624 &mut self,
625 available_space: Size<AvailableSpace>,
626 window: &mut Window,
627 cx: &mut App,
628 ) -> Size<Pixels>;
629}
630
631/// An [`Element`] together with the state GPUI keeps while drawing it in one frame.
632///
633/// GPUI creates a `Drawable` for each element in the tree, and an [`AnyElement`] holds one.
634/// Its phase state moves through the frame's phases in order. You don't construct it yourself.
635/// Use `element` to reach the wrapped element.
636pub struct Drawable<E: Element> {
637 /// The wrapped element.
638 pub element: E,
639 phase: ElementDrawPhase<E::RequestLayoutState, E::PrepaintState>,
640}
641
642#[derive(Default)]
643enum ElementDrawPhase<RequestLayoutState, PrepaintState> {
644 #[default]
645 Start,
646 RequestLayout {
647 layout_id: LayoutId,
648 global_id: Option<GlobalElementId>,
649 inspector_id: Option<InspectorElementId>,
650 request_layout: RequestLayoutState,
651 },
652 LayoutComputed {
653 layout_id: LayoutId,
654 global_id: Option<GlobalElementId>,
655 inspector_id: Option<InspectorElementId>,
656 available_space: Size<AvailableSpace>,
657 request_layout: RequestLayoutState,
658 },
659 Prepaint {
660 node_id: DispatchNodeId,
661 global_id: Option<GlobalElementId>,
662 inspector_id: Option<InspectorElementId>,
663 bounds: Bounds<Pixels>,
664 request_layout: RequestLayoutState,
665 prepaint: PrepaintState,
666 inline_fragments: Option<Arc<[Bounds<Pixels>]>>,
667 },
668 Painted,
669}
670
671impl<E: Element> Drawable<E> {
672 pub(crate) fn new(element: E) -> Self {
673 Drawable {
674 element,
675 phase: ElementDrawPhase::Start,
676 }
677 }
678
679 fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
680 match mem::take(&mut self.phase) {
681 ElementDrawPhase::Start => {
682 let global_id = self
683 .element
684 .id()
685 .map(|element_id| prepare_element_id(element_id, window));
686
687 let inspector_id;
688 #[cfg(any(feature = "inspector", debug_assertions))]
689 {
690 inspector_id = if window.inspector_enabled() {
691 self.element
692 .source_location()
693 .map(|source| prepare_inspector_id(source, window))
694 } else {
695 None
696 };
697 }
698 #[cfg(not(any(feature = "inspector", debug_assertions)))]
699 {
700 inspector_id = None;
701 }
702
703 #[cfg(all(
704 feature = "hot-patching",
705 debug_assertions,
706 not(target_family = "wasm")
707 ))]
708 let (layout_id, request_layout) = subsecond::HotFn::current(E::request_layout)
709 .call((
710 &mut self.element,
711 global_id.as_ref(),
712 inspector_id.as_ref(),
713 window,
714 cx,
715 ));
716 #[cfg(not(all(
717 feature = "hot-patching",
718 debug_assertions,
719 not(target_family = "wasm")
720 )))]
721 let (layout_id, request_layout) = self.element.request_layout(
722 global_id.as_ref(),
723 inspector_id.as_ref(),
724 window,
725 cx,
726 );
727
728 if global_id.is_some() {
729 window.element_id_stack.pop();
730 }
731
732 self.phase = ElementDrawPhase::RequestLayout {
733 layout_id,
734 global_id,
735 inspector_id,
736 request_layout,
737 };
738 layout_id
739 }
740 _ => panic!("must call request_layout only once"),
741 }
742 }
743
744 pub(crate) fn prepaint(&mut self, window: &mut Window, cx: &mut App) {
745 match mem::take(&mut self.phase) {
746 ElementDrawPhase::RequestLayout {
747 layout_id,
748 global_id,
749 inspector_id,
750 mut request_layout,
751 }
752 | ElementDrawPhase::LayoutComputed {
753 layout_id,
754 global_id,
755 inspector_id,
756 mut request_layout,
757 ..
758 } => {
759 if let Some(element_id) = self.element.id() {
760 window.element_id_stack.push(element_id);
761 debug_assert_eq!(&**global_id.as_ref().unwrap(), &*window.element_id_stack);
762 }
763
764 let bounds = window.layout_bounds(layout_id);
765 let mut pushed_a11y_node = false;
766 if window.a11y.is_active() {
767 if let Some(global_id) = global_id.as_ref() {
768 let hidden = self.element.is_a11y_hidden();
769 let role = self.element.a11y_role();
770 if role.is_some() || hidden {
771 let role = role.unwrap_or(accesskit::Role::GenericContainer);
772 let node_id = global_id.accesskit_node_id();
773 let mut node = accesskit::Node::new(role);
774 let scale = window.scale_factor();
775 node.set_bounds(accesskit::Rect {
776 x0: (bounds.origin.x.0 * scale) as f64,
777 y0: (bounds.origin.y.0 * scale) as f64,
778 x1: ((bounds.origin.x.0 + bounds.size.width.0) * scale) as f64,
779 y1: ((bounds.origin.y.0 + bounds.size.height.0) * scale) as f64,
780 });
781 self.element.write_a11y_info(&mut node);
782 if hidden {
783 node.set_hidden();
784 }
785 window.a11y.node_bounds.insert(node_id, bounds);
786 pushed_a11y_node = window.a11y.nodes.push(node_id, node);
787 #[cfg(debug_assertions)]
788 if pushed_a11y_node {
789 let view = window
790 .a11y
791 .view_type_names
792 .get(&window.current_view())
793 .copied();
794 let source_location = self.element.source_location();
795 window.a11y.nodes.record_node_info(
796 node_id,
797 crate::window::a11y::debug::NodeDebugInfo {
798 synthetic: false,
799 view,
800 element_id: global_id.last().map(|id| format!("{id:?}")),
801 source_location,
802 },
803 );
804 }
805 }
806 }
807 }
808
809 let node_id = window.next_frame.dispatch_tree.push_node();
810
811 let inline_fragments = window.inline_fragments(layout_id);
812 let previous_fragments = std::mem::replace(
813 &mut window.current_inline_fragments,
814 inline_fragments.clone(),
815 );
816
817 let mut prepaint = window.with_layout_direction_context(layout_id, |window| {
818 #[cfg(all(
819 feature = "hot-patching",
820 debug_assertions,
821 not(target_family = "wasm")
822 ))]
823 {
824 subsecond::HotFn::current(E::prepaint).call((
825 &mut self.element,
826 global_id.as_ref(),
827 inspector_id.as_ref(),
828 bounds,
829 &mut request_layout,
830 window,
831 cx,
832 ))
833 }
834
835 #[cfg(not(all(
836 feature = "hot-patching",
837 debug_assertions,
838 not(target_family = "wasm")
839 )))]
840 {
841 self.element.prepaint(
842 global_id.as_ref(),
843 inspector_id.as_ref(),
844 bounds,
845 &mut request_layout,
846 window,
847 cx,
848 )
849 }
850 });
851
852 window.current_inline_fragments = previous_fragments;
853 window.next_frame.dispatch_tree.pop_node();
854
855 if pushed_a11y_node {
856 if let Some(global_id) = global_id.as_ref() {
857 #[cfg(debug_assertions)]
858 let creator = crate::window::a11y::debug::NodeCreator {
859 view: window
860 .a11y
861 .view_type_names
862 .get(&window.current_view())
863 .copied(),
864 element_id: global_id.last().map(|id| format!("{id:?}")),
865 source_location: self.element.source_location(),
866 };
867 let mut builder = A11ySubtreeBuilder::new(
868 global_id.accesskit_node_id(),
869 &mut window.a11y.nodes,
870 );
871 #[cfg(debug_assertions)]
872 {
873 builder = builder.with_creator(creator);
874 }
875 self.element
876 .a11y_synthetic_children(&mut prepaint, &mut builder);
877 }
878 window.a11y.nodes.pop();
879 }
880
881 if global_id.is_some() {
882 window.element_id_stack.pop();
883 }
884
885 self.phase = ElementDrawPhase::Prepaint {
886 node_id,
887 global_id,
888 inspector_id,
889 bounds,
890 request_layout,
891 prepaint,
892 inline_fragments,
893 };
894 }
895 _ => panic!("must call request_layout before prepaint"),
896 }
897 }
898
899 pub(crate) fn paint(
900 &mut self,
901 window: &mut Window,
902 cx: &mut App,
903 ) -> (E::RequestLayoutState, E::PrepaintState) {
904 match mem::take(&mut self.phase) {
905 ElementDrawPhase::Prepaint {
906 node_id,
907 global_id,
908 inspector_id,
909 bounds,
910 mut request_layout,
911 mut prepaint,
912 inline_fragments,
913 ..
914 } => {
915 if let Some(element_id) = self.element.id() {
916 window.element_id_stack.push(element_id);
917 debug_assert_eq!(&**global_id.as_ref().unwrap(), &*window.element_id_stack);
918 }
919
920 window.next_frame.dispatch_tree.set_active_node(node_id);
921
922 let previous_fragments =
923 std::mem::replace(&mut window.current_inline_fragments, inline_fragments);
924
925 #[cfg(all(
926 feature = "hot-patching",
927 debug_assertions,
928 not(target_family = "wasm")
929 ))]
930 subsecond::HotFn::current(E::paint).call((
931 &mut self.element,
932 global_id.as_ref(),
933 inspector_id.as_ref(),
934 bounds,
935 &mut request_layout,
936 &mut prepaint,
937 window,
938 cx,
939 ));
940 #[cfg(not(all(
941 feature = "hot-patching",
942 debug_assertions,
943 not(target_family = "wasm")
944 )))]
945 self.element.paint(
946 global_id.as_ref(),
947 inspector_id.as_ref(),
948 bounds,
949 &mut request_layout,
950 &mut prepaint,
951 window,
952 cx,
953 );
954
955 if global_id.is_some() {
956 window.element_id_stack.pop();
957 }
958
959 window.current_inline_fragments = previous_fragments;
960
961 self.phase = ElementDrawPhase::Painted;
962 (request_layout, prepaint)
963 }
964 _ => panic!("must call prepaint before paint"),
965 }
966 }
967
968 fn compute_layout_as_root(
969 &mut self,
970 available_space: Size<AvailableSpace>,
971 window: &mut Window,
972 cx: &mut App,
973 ) -> (LayoutId, Size<Pixels>) {
974 if matches!(&self.phase, ElementDrawPhase::Start) {
975 self.request_layout(window, cx);
976 }
977
978 let layout_id = match mem::take(&mut self.phase) {
979 ElementDrawPhase::RequestLayout {
980 layout_id,
981 global_id,
982 inspector_id,
983 request_layout,
984 } => {
985 window.compute_layout(layout_id, available_space, cx);
986 self.phase = ElementDrawPhase::LayoutComputed {
987 layout_id,
988 global_id,
989 inspector_id,
990 available_space,
991 request_layout,
992 };
993 layout_id
994 }
995 ElementDrawPhase::LayoutComputed {
996 layout_id,
997 global_id,
998 inspector_id,
999 available_space: prev_available_space,
1000 request_layout,
1001 } => {
1002 if available_space != prev_available_space {
1003 window.compute_layout(layout_id, available_space, cx);
1004 }
1005 self.phase = ElementDrawPhase::LayoutComputed {
1006 layout_id,
1007 global_id,
1008 inspector_id,
1009 available_space,
1010 request_layout,
1011 };
1012 layout_id
1013 }
1014 _ => panic!("cannot measure after painting"),
1015 };
1016
1017 (layout_id, window.layout_bounds(layout_id).size)
1018 }
1019
1020 pub(crate) fn layout_as_root(
1021 &mut self,
1022 available_space: Size<AvailableSpace>,
1023 window: &mut Window,
1024 cx: &mut App,
1025 ) -> Size<Pixels> {
1026 self.compute_layout_as_root(available_space, window, cx).1
1027 }
1028}
1029
1030impl<E> ElementObject for Drawable<E>
1031where
1032 E: Element,
1033 E::RequestLayoutState: 'static,
1034{
1035 fn inner_element(&mut self) -> &mut dyn Any {
1036 &mut self.element
1037 }
1038
1039 #[inline]
1040 fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
1041 Drawable::request_layout(self, window, cx)
1042 }
1043
1044 #[inline]
1045 fn prepaint(&mut self, window: &mut Window, cx: &mut App) {
1046 Drawable::prepaint(self, window, cx);
1047 }
1048
1049 #[inline]
1050 fn paint(&mut self, window: &mut Window, cx: &mut App) {
1051 Drawable::paint(self, window, cx);
1052 }
1053
1054 #[inline]
1055 fn layout_as_root(
1056 &mut self,
1057 available_space: Size<AvailableSpace>,
1058 window: &mut Window,
1059 cx: &mut App,
1060 ) -> Size<Pixels> {
1061 Drawable::layout_as_root(self, available_space, window, cx)
1062 }
1063}
1064
1065/// An element with its concrete type erased, so that elements of different types can share one
1066/// type.
1067///
1068/// Use it when a function must return one type but its branches build different elements, or when
1069/// a list holds elements of mixed types. Create one with [`IntoElement::into_any_element`] or
1070/// [`Element::into_any`]. Its methods run the same phases as the element it wraps.
1071///
1072/// # Example
1073///
1074/// ```
1075/// use gpui::{AnyElement, Div, IntoElement, div, prelude::*};
1076///
1077/// fn label(wide: bool) -> AnyElement {
1078/// if wide {
1079/// div().w_full().child("Wide").into_any_element()
1080/// } else {
1081/// "Narrow".into_any_element()
1082/// }
1083/// }
1084///
1085/// let mut wide = label(true);
1086/// assert!(wide.downcast_mut::<Div>().is_some());
1087///
1088/// let mut narrow = label(false);
1089/// assert!(narrow.downcast_mut::<Div>().is_none());
1090/// ```
1091pub struct AnyElement(ArenaBox<dyn ElementObject>);
1092
1093impl AnyElement {
1094 pub(crate) fn new<E>(element: E) -> Self
1095 where
1096 E: 'static + Element,
1097 E::RequestLayoutState: Any,
1098 {
1099 let element = with_element_arena(|arena| arena.alloc(|| Drawable::new(element)))
1100 .map(|element| element as &mut dyn ElementObject);
1101 AnyElement(element)
1102 }
1103
1104 /// Returns a mutable reference to the wrapped element if it has type `T`, or `None` otherwise.
1105 pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
1106 self.0.inner_element().downcast_mut::<T>()
1107 }
1108
1109 /// Runs the request-layout phase on the wrapped element, and returns its layout id.
1110 ///
1111 /// Call this from a parent's `request_layout`, before the parent's own layout is requested.
1112 pub fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
1113 self.0.request_layout(window, cx)
1114 }
1115
1116 /// Runs the prepaint phase on the wrapped element, with the bounds that layout gave it.
1117 ///
1118 /// Returns the [`FocusHandle`] of a focused element in this subtree, if one became focused
1119 /// during this call. Otherwise returns `None`.
1120 pub fn prepaint(&mut self, window: &mut Window, cx: &mut App) -> Option<FocusHandle> {
1121 let focus_assigned = window.next_frame.focus.is_some();
1122
1123 self.0.prepaint(window, cx);
1124
1125 if !focus_assigned && let Some(focus_id) = window.next_frame.focus {
1126 return FocusHandle::for_id(focus_id, &cx.focus_handles);
1127 }
1128
1129 None
1130 }
1131
1132 /// Runs the paint phase on the wrapped element. Call it after [`AnyElement::prepaint`].
1133 pub fn paint(&mut self, window: &mut Window, cx: &mut App) {
1134 self.0.paint(window, cx);
1135 }
1136
1137 /// Lays out this element as the root of its own layout, within `available_space`, and returns
1138 /// its size.
1139 ///
1140 /// The element is not placed in the window by this call. Use it to measure an element before
1141 /// placing it with [`AnyElement::prepaint_at`]. If the layout hasn't been requested yet, this
1142 /// method requests it first.
1143 pub fn layout_as_root(
1144 &mut self,
1145 available_space: Size<AvailableSpace>,
1146 window: &mut Window,
1147 cx: &mut App,
1148 ) -> Size<Pixels> {
1149 self.0.layout_as_root(available_space, window, cx)
1150 }
1151
1152 /// Runs the prepaint phase with `origin` as the element's origin, in window coordinates.
1153 ///
1154 /// Call [`AnyElement::layout_as_root`] first, or use [`AnyElement::prepaint_as_root`], which
1155 /// does both. Returns the focus handle of a focused element in the subtree that became focused
1156 /// during this call, if any.
1157 pub fn prepaint_at(
1158 &mut self,
1159 origin: Point<Pixels>,
1160 window: &mut Window,
1161 cx: &mut App,
1162 ) -> Option<FocusHandle> {
1163 window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx))
1164 }
1165
1166 /// Lays out this element as a root within `available_space`, then prepaints it at `origin`.
1167 ///
1168 /// Use it for an element that sits outside its parent's layout, such as a popover placed at a
1169 /// window position. Returns the focus handle of a focused element in the subtree that became
1170 /// focused during this call, if any.
1171 pub fn prepaint_as_root(
1172 &mut self,
1173 origin: Point<Pixels>,
1174 available_space: Size<AvailableSpace>,
1175 window: &mut Window,
1176 cx: &mut App,
1177 ) -> Option<FocusHandle> {
1178 self.layout_as_root(available_space, window, cx);
1179 window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx))
1180 }
1181}
1182
1183impl Element for AnyElement {
1184 type RequestLayoutState = ();
1185 type PrepaintState = ();
1186
1187 fn id(&self) -> Option<ElementId> {
1188 None
1189 }
1190
1191 fn source_location(&self) -> Option<&'static panic::Location<'static>> {
1192 None
1193 }
1194
1195 fn request_layout(
1196 &mut self,
1197 _: Option<&GlobalElementId>,
1198 _inspector_id: Option<&InspectorElementId>,
1199 window: &mut Window,
1200 cx: &mut App,
1201 ) -> (LayoutId, Self::RequestLayoutState) {
1202 let layout_id = self.request_layout(window, cx);
1203 (layout_id, ())
1204 }
1205
1206 fn prepaint(
1207 &mut self,
1208 _: Option<&GlobalElementId>,
1209 _inspector_id: Option<&InspectorElementId>,
1210 _: Bounds<Pixels>,
1211 _: &mut Self::RequestLayoutState,
1212 window: &mut Window,
1213 cx: &mut App,
1214 ) {
1215 self.prepaint(window, cx);
1216 }
1217
1218 fn paint(
1219 &mut self,
1220 _: Option<&GlobalElementId>,
1221 _inspector_id: Option<&InspectorElementId>,
1222 _: Bounds<Pixels>,
1223 _: &mut Self::RequestLayoutState,
1224 _: &mut Self::PrepaintState,
1225 window: &mut Window,
1226 cx: &mut App,
1227 ) {
1228 self.paint(window, cx);
1229 }
1230}
1231
1232impl IntoElement for AnyElement {
1233 type Element = Self;
1234
1235 fn into_element(self) -> Self::Element {
1236 self
1237 }
1238
1239 fn into_any_element(self) -> AnyElement {
1240 self
1241 }
1242}
1243
1244/// An element that draws nothing and takes up no space.
1245///
1246/// Return it where a view or component must produce an element but has nothing to show. Its
1247/// layout has `display: none`, so it doesn't affect its siblings.
1248pub struct Empty;
1249
1250impl IntoElement for Empty {
1251 type Element = Self;
1252
1253 fn into_element(self) -> Self::Element {
1254 self
1255 }
1256}
1257
1258impl Element for Empty {
1259 type RequestLayoutState = ();
1260 type PrepaintState = ();
1261
1262 fn id(&self) -> Option<ElementId> {
1263 None
1264 }
1265
1266 fn source_location(&self) -> Option<&'static panic::Location<'static>> {
1267 None
1268 }
1269
1270 fn request_layout(
1271 &mut self,
1272 _id: Option<&GlobalElementId>,
1273 _inspector_id: Option<&InspectorElementId>,
1274 window: &mut Window,
1275 cx: &mut App,
1276 ) -> (LayoutId, Self::RequestLayoutState) {
1277 (
1278 window.request_layout(
1279 Style {
1280 display: crate::Display::None,
1281 ..Default::default()
1282 },
1283 None,
1284 cx,
1285 ),
1286 (),
1287 )
1288 }
1289
1290 fn prepaint(
1291 &mut self,
1292 _id: Option<&GlobalElementId>,
1293 _inspector_id: Option<&InspectorElementId>,
1294 _bounds: Bounds<Pixels>,
1295 _state: &mut Self::RequestLayoutState,
1296 _window: &mut Window,
1297 _cx: &mut App,
1298 ) {
1299 }
1300
1301 fn paint(
1302 &mut self,
1303 _id: Option<&GlobalElementId>,
1304 _inspector_id: Option<&InspectorElementId>,
1305 _bounds: Bounds<Pixels>,
1306 _request_layout: &mut Self::RequestLayoutState,
1307 _prepaint: &mut Self::PrepaintState,
1308 _window: &mut Window,
1309 _cx: &mut App,
1310 ) {
1311 }
1312}
1313
1314#[inline(never)]
1315fn prepare_element_id(element_id: ElementId, window: &mut Window) -> GlobalElementId {
1316 window.element_id_stack.push(element_id);
1317 window.element_id_stack.global_id()
1318}
1319
1320#[cfg(any(feature = "inspector", debug_assertions))]
1321#[inline(never)]
1322fn prepare_inspector_id(
1323 source: &'static panic::Location<'static>,
1324 window: &mut Window,
1325) -> InspectorElementId {
1326 let path = InspectorElementPath {
1327 global_id: window.element_id_stack.global_id(),
1328 source_location: source,
1329 };
1330 window.build_inspector_element_id(path)
1331}
1332
1333#[cfg(test)]
1334mod global_element_id_tests {
1335 use super::*;
1336 use std::hash::{Hash, Hasher};
1337
1338 fn path(depth: usize, leaf: u64) -> Vec<ElementId> {
1339 let mut ids: Vec<ElementId> = (0..depth as u64)
1340 .map(|level| ElementId::NamedInteger("some-container-element".into(), level))
1341 .collect();
1342 ids.push(ElementId::NamedInteger("leaf".into(), leaf));
1343 ids
1344 }
1345
1346 fn fx_hash(value: &impl Hash) -> u64 {
1347 let mut hasher = collections::FxHasher::default();
1348 value.hash(&mut hasher);
1349 hasher.finish()
1350 }
1351
1352 #[test]
1353 fn equal_paths_are_equal_and_hash_alike() {
1354 let a = GlobalElementId::new(&path(8, 1));
1355 let b = GlobalElementId::new(&path(8, 1));
1356 let c = GlobalElementId::new(&path(8, 2));
1357 let shallower = GlobalElementId::new(&path(7, 1));
1358 assert_eq!(a, b);
1359 assert_eq!(fx_hash(&a), fx_hash(&b));
1360 assert_eq!(a.accesskit_node_id(), b.accesskit_node_id());
1361 assert_ne!(a, c);
1362 assert_ne!(a, shallower);
1363 assert_ne!(fx_hash(&a), fx_hash(&c));
1364 assert_eq!(&*a, path(8, 1).as_slice());
1365 assert_eq!(a.to_string(), b.to_string());
1366 assert_eq!(GlobalElementId::default(), GlobalElementId::new(&[]));
1367 }
1368
1369 /// A window of `items` columns of `depth` nested stateful divs — the
1370 /// shape of a list of cards — drawn from scratch on every frame.
1371 struct DeepStatefulTree {
1372 items: usize,
1373 depth: usize,
1374 }
1375
1376 impl crate::Render for DeepStatefulTree {
1377 fn render(&mut self, _: &mut Window, _: &mut crate::Context<Self>) -> impl IntoElement {
1378 use crate::{InteractiveElement as _, ParentElement as _, Styled as _, div, px};
1379 let depth = self.depth;
1380 div()
1381 .flex()
1382 .flex_wrap()
1383 .size_full()
1384 .children((0..self.items).map(move |item| {
1385 let mut element = div().id(("leaf", item as u64)).size(px(2.));
1386 for level in (0..depth).rev() {
1387 element = div()
1388 .id(("some-container-element", level as u64))
1389 .child(element);
1390 }
1391 element.id(("item", item as u64)).size(px(4.))
1392 }))
1393 }
1394 }
1395
1396 /// Frame cost of a tree of stateful elements: every one of them builds a
1397 /// `GlobalElementId` and looks its state up in two frames' maps.
1398 ///
1399 /// `cargo test -p gpui --release --lib global_element_id_tests::frame_cost -- --ignored --nocapture`
1400 #[crate::test]
1401 #[ignore = "prints timings; run by hand"]
1402 fn frame_cost(cx: &mut crate::TestAppContext) {
1403 use crate::AppContext as _;
1404 for (items, depth) in [(500, 4), (500, 12), (2000, 12)] {
1405 let window = cx.add_window(move |_, _| DeepStatefulTree { items, depth });
1406 let window = crate::AnyWindowHandle::from(window);
1407 let mut frame = |cx: &mut crate::TestAppContext| {
1408 cx.update_window(window, |_, window, cx| {
1409 window.refresh();
1410 let started = std::time::Instant::now();
1411 window.draw(cx).clear(cx);
1412 started.elapsed()
1413 })
1414 .unwrap()
1415 };
1416 for _ in 0..5 {
1417 frame(cx);
1418 }
1419 let mut samples: Vec<_> = (0..40).map(|_| frame(cx)).collect();
1420 samples.sort();
1421 let median = samples[samples.len() / 2];
1422 let stateful = items * (depth + 2);
1423 eprintln!(
1424 "{items} items x {depth} deep ({stateful} stateful elements): median frame {:.2} ms, {:.0} ns per stateful element",
1425 median.as_secs_f64() * 1e3,
1426 median.as_nanos() as f64 / stateful as f64,
1427 );
1428 }
1429 }
1430
1431 /// `cargo test -p gpui --release --lib global_element_id_tests::hash_cost -- --ignored --nocapture`
1432 #[test]
1433 #[ignore = "prints timings; run by hand"]
1434 fn hash_cost() {
1435 let ids = path(11, 7);
1436 let id = GlobalElementId::new(&ids);
1437 let n = 1_000_000u64;
1438 let mut acc = 0u64;
1439 let started = std::time::Instant::now();
1440 for _ in 0..n {
1441 acc ^= fx_hash(&std::hint::black_box(ids.as_slice()));
1442 }
1443 let walk = started.elapsed();
1444 let started = std::time::Instant::now();
1445 for _ in 0..n {
1446 acc ^= fx_hash(std::hint::black_box(&id));
1447 }
1448 let cached = started.elapsed();
1449 let started = std::time::Instant::now();
1450 for _ in 0..n {
1451 acc ^= GlobalElementId::new(std::hint::black_box(&ids)).hash;
1452 }
1453 let build = started.elapsed();
1454 eprintln!(
1455 "12-deep path: hash by walking ids {:.1} ns, cached {:.1} ns, build (clone + hash once) {:.1} ns [{acc}]",
1456 walk.as_nanos() as f64 / n as f64,
1457 cached.as_nanos() as f64 / n as f64,
1458 build.as_nanos() as f64 / n as f64,
1459 );
1460 }
1461}