1//! Elements are the workhorses of GPUI. They are responsible for laying out and painting all of
2//! the contents of a window. Elements form a tree and are laid out according to the web layout
3//! standards as implemented by [taffy](https://github.com/DioxusLabs/taffy). Most of the time,
4//! you won't need to interact with this module or these APIs directly. Elements provide their
5//! own APIs and GPUI, or other element implementation, uses the APIs in this module to convert
6//! that element tree into the pixels you see on the screen.
7//!
8//! # Element Basics
9//!
10//! Elements are constructed by calling [`Render::render()`] on the root view of the window, which
11//! which recursively constructs the element tree from the current state of the application,.
12//! These elements are then laid out by Taffy, and painted to the screen according to their own
13//! implementation of [`Element::paint()`]. Before the start of the next frame, the entire element
14//! tree and any callbacks they have registered with GPUI are dropped and the process repeats.
15//!
16//! But some state is too simple and voluminous to store in every view that needs it, e.g.
17//! whether a hover has been started or not. For this, GPUI provides the [`Element::State`], associated type.
18//!
19//! # Implementing your own elements
20//!
21//! Elements are intended to be the low level, imperative API to GPUI. They are responsible for upholding,
22//! or breaking, GPUI's features as they deem necessary. As an example, most GPUI elements are expected
23//! to stay in the bounds that their parent element gives them. But with [`WindowContext::break_content_mask`],
24//! you can ignore this restriction and paint anywhere inside of the window's bounds. This is useful for overlays
25//! and popups and anything else that shows up 'on top' of other elements.
26//! With great power, comes great responsibility.
27//!
28//! However, most of the time, you won't need to implement your own elements. GPUI provides a number of
29//! elements that should cover most common use cases out of the box and it's recommended that you use those
30//! to construct `components`, using the [`RenderOnce`] trait and the `#[derive(IntoElement)]` macro. Only implement
31//! elements when you need to take manual control of the layout and painting process, such as when using
32//! your own custom layout algorithm or rendering a code editor.
33
34use crate::{
35 util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementId, LayoutId,
36 Pixels, Point, Size, Style, ViewContext, WindowContext, ELEMENT_ARENA,
37};
38use derive_more::{Deref, DerefMut};
39pub(crate) use smallvec::SmallVec;
40use std::{any::Any, fmt::Debug, mem};
41
42/// Implemented by types that participate in laying out and painting the contents of a window.
43/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
44/// You can create custom elements by implementing this trait, see the module-level documentation
45/// for more details.
46pub trait Element: 'static + IntoElement {
47 /// The type of state returned from [`Element::request_layout`]. A mutable reference to this state is subsequently
48 /// provided to [`Element::prepaint`] and [`Element::paint`].
49 type RequestLayoutState: 'static;
50
51 /// The type of state returned from [`Element::prepaint`]. A mutable reference to this state is subsequently
52 /// provided to [`Element::paint`].
53 type PrepaintState: 'static;
54
55 /// If this element has a unique identifier, return it here. This is used to track elements across frames, and
56 /// will cause a GlobalElementId to be passed to the request_layout, prepaint, and paint methods.
57 ///
58 /// The global id can in turn be used to access state that's connected to an element with the same id across
59 /// frames. This id must be unique among children of the first containing element with an id.
60 fn id(&self) -> Option<ElementId>;
61
62 /// Before an element can be painted, we need to know where it's going to be and how big it is.
63 /// Use this method to request a layout from Taffy and initialize the element's state.
64 fn request_layout(
65 &mut self,
66 id: Option<&GlobalElementId>,
67 cx: &mut WindowContext,
68 ) -> (LayoutId, Self::RequestLayoutState);
69
70 /// After laying out an element, we need to commit its bounds to the current frame for hitbox
71 /// purposes. The state argument is the same state that was returned from [`Element::request_layout()`].
72 fn prepaint(
73 &mut self,
74 id: Option<&GlobalElementId>,
75 bounds: Bounds<Pixels>,
76 request_layout: &mut Self::RequestLayoutState,
77 cx: &mut WindowContext,
78 ) -> Self::PrepaintState;
79
80 /// Once layout has been completed, this method will be called to paint the element to the screen.
81 /// The state argument is the same state that was returned from [`Element::request_layout()`].
82 fn paint(
83 &mut self,
84 id: Option<&GlobalElementId>,
85 bounds: Bounds<Pixels>,
86 request_layout: &mut Self::RequestLayoutState,
87 prepaint: &mut Self::PrepaintState,
88 cx: &mut WindowContext,
89 );
90
91 /// Convert this element into a dynamically-typed [`AnyElement`].
92 fn into_any(self) -> AnyElement {
93 AnyElement::new(self)
94 }
95}
96
97/// Implemented by any type that can be converted into an element.
98pub trait IntoElement: Sized {
99 /// The specific type of element into which the implementing type is converted.
100 /// Useful for converting other types into elements automatically, like Strings
101 type Element: Element;
102
103 /// Convert self into a type that implements [`Element`].
104 fn into_element(self) -> Self::Element;
105
106 /// Convert self into a dynamically-typed [`AnyElement`].
107 fn into_any_element(self) -> AnyElement {
108 self.into_element().into_any()
109 }
110}
111
112impl<T: IntoElement> FluentBuilder for T {}
113
114/// An object that can be drawn to the screen. This is the trait that distinguishes `Views` from
115/// models. Views are drawn to the screen and care about the current window's state, models are not and do not.
116pub trait Render: 'static + Sized {
117 /// Render this view into an element tree.
118 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
119}
120
121impl Render for Empty {
122 fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
123 Empty
124 }
125}
126
127/// You can derive [`IntoElement`] on any type that implements this trait.
128/// It is used to construct reusable `components` out of plain data. Think of
129/// components as a recipe for a certain pattern of elements. RenderOnce allows
130/// you to invoke this pattern, without breaking the fluent builder pattern of
131/// the element APIs.
132pub trait RenderOnce: 'static {
133 /// Render this component into an element tree. Note that this method
134 /// takes ownership of self, as compared to [`Render::render()`] method
135 /// which takes a mutable reference.
136 fn render(self, cx: &mut WindowContext) -> impl IntoElement;
137}
138
139/// This is a helper trait to provide a uniform interface for constructing elements that
140/// can accept any number of any kind of child elements
141pub trait ParentElement {
142 /// Extend this element's children with the given child elements.
143 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>);
144
145 /// Add a single child element to this element.
146 fn child(mut self, child: impl IntoElement) -> Self
147 where
148 Self: Sized,
149 {
150 self.extend(std::iter::once(child.into_element().into_any()));
151 self
152 }
153
154 /// Add multiple child elements to this element.
155 fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
156 where
157 Self: Sized,
158 {
159 self.extend(children.into_iter().map(|child| child.into_any_element()));
160 self
161 }
162}
163
164/// An element for rendering components. An implementation detail of the [`IntoElement`] derive macro
165/// for [`RenderOnce`]
166#[doc(hidden)]
167pub struct Component<C: RenderOnce>(Option<C>);
168
169impl<C: RenderOnce> Component<C> {
170 /// Create a new component from the given RenderOnce type.
171 pub fn new(component: C) -> Self {
172 Component(Some(component))
173 }
174}
175
176impl<C: RenderOnce> Element for Component<C> {
177 type RequestLayoutState = AnyElement;
178 type PrepaintState = ();
179
180 fn id(&self) -> Option<ElementId> {
181 None
182 }
183
184 fn request_layout(
185 &mut self,
186 _id: Option<&GlobalElementId>,
187 cx: &mut WindowContext,
188 ) -> (LayoutId, Self::RequestLayoutState) {
189 let mut element = self.0.take().unwrap().render(cx).into_any_element();
190 let layout_id = element.request_layout(cx);
191 (layout_id, element)
192 }
193
194 fn prepaint(
195 &mut self,
196 _id: Option<&GlobalElementId>,
197 _: Bounds<Pixels>,
198 element: &mut AnyElement,
199 cx: &mut WindowContext,
200 ) {
201 element.prepaint(cx);
202 }
203
204 fn paint(
205 &mut self,
206 _id: Option<&GlobalElementId>,
207 _: Bounds<Pixels>,
208 element: &mut Self::RequestLayoutState,
209 _: &mut Self::PrepaintState,
210 cx: &mut WindowContext,
211 ) {
212 element.paint(cx)
213 }
214}
215
216impl<C: RenderOnce> IntoElement for Component<C> {
217 type Element = Self;
218
219 fn into_element(self) -> Self::Element {
220 self
221 }
222}
223
224/// A globally unique identifier for an element, used to track state across frames.
225#[derive(Deref, DerefMut, Default, Debug, Eq, PartialEq, Hash)]
226pub struct GlobalElementId(pub(crate) SmallVec<[ElementId; 32]>);
227
228trait ElementObject {
229 fn inner_element(&mut self) -> &mut dyn Any;
230
231 fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
232
233 fn prepaint(&mut self, cx: &mut WindowContext);
234
235 fn paint(&mut self, cx: &mut WindowContext);
236
237 fn layout_as_root(
238 &mut self,
239 available_space: Size<AvailableSpace>,
240 cx: &mut WindowContext,
241 ) -> Size<Pixels>;
242}
243
244/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
245pub struct Drawable<E: Element> {
246 /// The drawn element.
247 pub element: E,
248 phase: ElementDrawPhase<E::RequestLayoutState, E::PrepaintState>,
249}
250
251#[derive(Default)]
252enum ElementDrawPhase<RequestLayoutState, PrepaintState> {
253 #[default]
254 Start,
255 RequestLayout {
256 layout_id: LayoutId,
257 global_id: Option<GlobalElementId>,
258 request_layout: RequestLayoutState,
259 },
260 LayoutComputed {
261 layout_id: LayoutId,
262 global_id: Option<GlobalElementId>,
263 available_space: Size<AvailableSpace>,
264 request_layout: RequestLayoutState,
265 },
266 Prepaint {
267 node_id: DispatchNodeId,
268 global_id: Option<GlobalElementId>,
269 bounds: Bounds<Pixels>,
270 request_layout: RequestLayoutState,
271 prepaint: PrepaintState,
272 },
273 Painted,
274}
275
276/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
277impl<E: Element> Drawable<E> {
278 pub(crate) fn new(element: E) -> Self {
279 Drawable {
280 element,
281 phase: ElementDrawPhase::Start,
282 }
283 }
284
285 fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
286 match mem::take(&mut self.phase) {
287 ElementDrawPhase::Start => {
288 let global_id = self.element.id().map(|element_id| {
289 cx.window.element_id_stack.push(element_id);
290 GlobalElementId(cx.window.element_id_stack.clone())
291 });
292
293 let (layout_id, request_layout) =
294 self.element.request_layout(global_id.as_ref(), cx);
295
296 if global_id.is_some() {
297 cx.window.element_id_stack.pop();
298 }
299
300 self.phase = ElementDrawPhase::RequestLayout {
301 layout_id,
302 global_id,
303 request_layout,
304 };
305 layout_id
306 }
307 _ => panic!("must call request_layout only once"),
308 }
309 }
310
311 pub(crate) fn prepaint(&mut self, cx: &mut WindowContext) {
312 match mem::take(&mut self.phase) {
313 ElementDrawPhase::RequestLayout {
314 layout_id,
315 global_id,
316 mut request_layout,
317 }
318 | ElementDrawPhase::LayoutComputed {
319 layout_id,
320 global_id,
321 mut request_layout,
322 ..
323 } => {
324 if let Some(element_id) = self.element.id() {
325 cx.window.element_id_stack.push(element_id);
326 debug_assert_eq!(global_id.as_ref().unwrap().0, cx.window.element_id_stack);
327 }
328
329 let bounds = cx.layout_bounds(layout_id);
330 let node_id = cx.window.next_frame.dispatch_tree.push_node();
331 let prepaint =
332 self.element
333 .prepaint(global_id.as_ref(), bounds, &mut request_layout, cx);
334 cx.window.next_frame.dispatch_tree.pop_node();
335
336 if global_id.is_some() {
337 cx.window.element_id_stack.pop();
338 }
339
340 self.phase = ElementDrawPhase::Prepaint {
341 node_id,
342 global_id,
343 bounds,
344 request_layout,
345 prepaint,
346 };
347 }
348 _ => panic!("must call request_layout before prepaint"),
349 }
350 }
351
352 pub(crate) fn paint(
353 &mut self,
354 cx: &mut WindowContext,
355 ) -> (E::RequestLayoutState, E::PrepaintState) {
356 match mem::take(&mut self.phase) {
357 ElementDrawPhase::Prepaint {
358 node_id,
359 global_id,
360 bounds,
361 mut request_layout,
362 mut prepaint,
363 ..
364 } => {
365 if let Some(element_id) = self.element.id() {
366 cx.window.element_id_stack.push(element_id);
367 debug_assert_eq!(global_id.as_ref().unwrap().0, cx.window.element_id_stack);
368 }
369
370 cx.window.next_frame.dispatch_tree.set_active_node(node_id);
371 self.element.paint(
372 global_id.as_ref(),
373 bounds,
374 &mut request_layout,
375 &mut prepaint,
376 cx,
377 );
378
379 if global_id.is_some() {
380 cx.window.element_id_stack.pop();
381 }
382
383 self.phase = ElementDrawPhase::Painted;
384 (request_layout, prepaint)
385 }
386 _ => panic!("must call prepaint before paint"),
387 }
388 }
389
390 pub(crate) fn layout_as_root(
391 &mut self,
392 available_space: Size<AvailableSpace>,
393 cx: &mut WindowContext,
394 ) -> Size<Pixels> {
395 if matches!(&self.phase, ElementDrawPhase::Start) {
396 self.request_layout(cx);
397 }
398
399 let layout_id = match mem::take(&mut self.phase) {
400 ElementDrawPhase::RequestLayout {
401 layout_id,
402 global_id,
403 request_layout,
404 } => {
405 cx.compute_layout(layout_id, available_space);
406 self.phase = ElementDrawPhase::LayoutComputed {
407 layout_id,
408 global_id,
409 available_space,
410 request_layout,
411 };
412 layout_id
413 }
414 ElementDrawPhase::LayoutComputed {
415 layout_id,
416 global_id,
417 available_space: prev_available_space,
418 request_layout,
419 } => {
420 if available_space != prev_available_space {
421 cx.compute_layout(layout_id, available_space);
422 }
423 self.phase = ElementDrawPhase::LayoutComputed {
424 layout_id,
425 global_id,
426 available_space,
427 request_layout,
428 };
429 layout_id
430 }
431 _ => panic!("cannot measure after painting"),
432 };
433
434 cx.layout_bounds(layout_id).size
435 }
436}
437
438impl<E> ElementObject for Drawable<E>
439where
440 E: Element,
441 E::RequestLayoutState: 'static,
442{
443 fn inner_element(&mut self) -> &mut dyn Any {
444 &mut self.element
445 }
446
447 fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
448 Drawable::request_layout(self, cx)
449 }
450
451 fn prepaint(&mut self, cx: &mut WindowContext) {
452 Drawable::prepaint(self, cx);
453 }
454
455 fn paint(&mut self, cx: &mut WindowContext) {
456 Drawable::paint(self, cx);
457 }
458
459 fn layout_as_root(
460 &mut self,
461 available_space: Size<AvailableSpace>,
462 cx: &mut WindowContext,
463 ) -> Size<Pixels> {
464 Drawable::layout_as_root(self, available_space, cx)
465 }
466}
467
468/// A dynamically typed element that can be used to store any element type.
469pub struct AnyElement(ArenaBox<dyn ElementObject>);
470
471impl AnyElement {
472 pub(crate) fn new<E>(element: E) -> Self
473 where
474 E: 'static + Element,
475 E::RequestLayoutState: Any,
476 {
477 let element = ELEMENT_ARENA
478 .with_borrow_mut(|arena| arena.alloc(|| Drawable::new(element)))
479 .map(|element| element as &mut dyn ElementObject);
480 AnyElement(element)
481 }
482
483 /// Attempt to downcast a reference to the boxed element to a specific type.
484 pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
485 self.0.inner_element().downcast_mut::<T>()
486 }
487
488 /// Request the layout ID of the element stored in this `AnyElement`.
489 /// Used for laying out child elements in a parent element.
490 pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
491 self.0.request_layout(cx)
492 }
493
494 /// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and
495 /// request autoscroll before the final paint pass is confirmed.
496 pub fn prepaint(&mut self, cx: &mut WindowContext) {
497 self.0.prepaint(cx)
498 }
499
500 /// Paints the element stored in this `AnyElement`.
501 pub fn paint(&mut self, cx: &mut WindowContext) {
502 self.0.paint(cx)
503 }
504
505 /// Performs layout for this element within the given available space and returns its size.
506 pub fn layout_as_root(
507 &mut self,
508 available_space: Size<AvailableSpace>,
509 cx: &mut WindowContext,
510 ) -> Size<Pixels> {
511 self.0.layout_as_root(available_space, cx)
512 }
513
514 /// Prepaints this element at the given absolute origin.
515 pub fn prepaint_at(&mut self, origin: Point<Pixels>, cx: &mut WindowContext) {
516 cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
517 }
518
519 /// Performs layout on this element in the available space, then prepaints it at the given absolute origin.
520 pub fn prepaint_as_root(
521 &mut self,
522 origin: Point<Pixels>,
523 available_space: Size<AvailableSpace>,
524 cx: &mut WindowContext,
525 ) {
526 self.layout_as_root(available_space, cx);
527 cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
528 }
529}
530
531impl Element for AnyElement {
532 type RequestLayoutState = ();
533 type PrepaintState = ();
534
535 fn id(&self) -> Option<ElementId> {
536 None
537 }
538
539 fn request_layout(
540 &mut self,
541 _: Option<&GlobalElementId>,
542 cx: &mut WindowContext,
543 ) -> (LayoutId, Self::RequestLayoutState) {
544 let layout_id = self.request_layout(cx);
545 (layout_id, ())
546 }
547
548 fn prepaint(
549 &mut self,
550 _: Option<&GlobalElementId>,
551 _: Bounds<Pixels>,
552 _: &mut Self::RequestLayoutState,
553 cx: &mut WindowContext,
554 ) {
555 self.prepaint(cx)
556 }
557
558 fn paint(
559 &mut self,
560 _: Option<&GlobalElementId>,
561 _: Bounds<Pixels>,
562 _: &mut Self::RequestLayoutState,
563 _: &mut Self::PrepaintState,
564 cx: &mut WindowContext,
565 ) {
566 self.paint(cx)
567 }
568}
569
570impl IntoElement for AnyElement {
571 type Element = Self;
572
573 fn into_element(self) -> Self::Element {
574 self
575 }
576
577 fn into_any_element(self) -> AnyElement {
578 self
579 }
580}
581
582/// The empty element, which renders nothing.
583pub struct Empty;
584
585impl IntoElement for Empty {
586 type Element = Self;
587
588 fn into_element(self) -> Self::Element {
589 self
590 }
591}
592
593impl Element for Empty {
594 type RequestLayoutState = ();
595 type PrepaintState = ();
596
597 fn id(&self) -> Option<ElementId> {
598 None
599 }
600
601 fn request_layout(
602 &mut self,
603 _id: Option<&GlobalElementId>,
604 cx: &mut WindowContext,
605 ) -> (LayoutId, Self::RequestLayoutState) {
606 (cx.request_layout(Style::default(), None), ())
607 }
608
609 fn prepaint(
610 &mut self,
611 _id: Option<&GlobalElementId>,
612 _bounds: Bounds<Pixels>,
613 _state: &mut Self::RequestLayoutState,
614 _cx: &mut WindowContext,
615 ) {
616 }
617
618 fn paint(
619 &mut self,
620 _id: Option<&GlobalElementId>,
621 _bounds: Bounds<Pixels>,
622 _request_layout: &mut Self::RequestLayoutState,
623 _prepaint: &mut Self::PrepaintState,
624 _cx: &mut WindowContext,
625 ) {
626 }
627}