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,
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, FocusHandle,
36 LayoutId, 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) -> Option<FocusHandle> {
497 let focus_assigned = cx.window.next_frame.focus.is_some();
498
499 self.0.prepaint(cx);
500
501 if !focus_assigned {
502 if let Some(focus_id) = cx.window.next_frame.focus {
503 return FocusHandle::for_id(focus_id, &cx.focus_handles);
504 }
505 }
506
507 None
508 }
509
510 /// Paints the element stored in this `AnyElement`.
511 pub fn paint(&mut self, cx: &mut WindowContext) {
512 self.0.paint(cx);
513 }
514
515 /// Performs layout for this element within the given available space and returns its size.
516 pub fn layout_as_root(
517 &mut self,
518 available_space: Size<AvailableSpace>,
519 cx: &mut WindowContext,
520 ) -> Size<Pixels> {
521 self.0.layout_as_root(available_space, cx)
522 }
523
524 /// Prepaints this element at the given absolute origin.
525 /// If any element in the subtree beneath this element is focused, its FocusHandle is returned.
526 pub fn prepaint_at(
527 &mut self,
528 origin: Point<Pixels>,
529 cx: &mut WindowContext,
530 ) -> Option<FocusHandle> {
531 cx.with_absolute_element_offset(origin, |cx| self.prepaint(cx))
532 }
533
534 /// Performs layout on this element in the available space, then prepaints it at the given absolute origin.
535 /// If any element in the subtree beneath this element is focused, its FocusHandle is returned.
536 pub fn prepaint_as_root(
537 &mut self,
538 origin: Point<Pixels>,
539 available_space: Size<AvailableSpace>,
540 cx: &mut WindowContext,
541 ) -> Option<FocusHandle> {
542 self.layout_as_root(available_space, cx);
543 cx.with_absolute_element_offset(origin, |cx| self.prepaint(cx))
544 }
545}
546
547impl Element for AnyElement {
548 type RequestLayoutState = ();
549 type PrepaintState = ();
550
551 fn id(&self) -> Option<ElementId> {
552 None
553 }
554
555 fn request_layout(
556 &mut self,
557 _: Option<&GlobalElementId>,
558 cx: &mut WindowContext,
559 ) -> (LayoutId, Self::RequestLayoutState) {
560 let layout_id = self.request_layout(cx);
561 (layout_id, ())
562 }
563
564 fn prepaint(
565 &mut self,
566 _: Option<&GlobalElementId>,
567 _: Bounds<Pixels>,
568 _: &mut Self::RequestLayoutState,
569 cx: &mut WindowContext,
570 ) {
571 self.prepaint(cx);
572 }
573
574 fn paint(
575 &mut self,
576 _: Option<&GlobalElementId>,
577 _: Bounds<Pixels>,
578 _: &mut Self::RequestLayoutState,
579 _: &mut Self::PrepaintState,
580 cx: &mut WindowContext,
581 ) {
582 self.paint(cx);
583 }
584}
585
586impl IntoElement for AnyElement {
587 type Element = Self;
588
589 fn into_element(self) -> Self::Element {
590 self
591 }
592
593 fn into_any_element(self) -> AnyElement {
594 self
595 }
596}
597
598/// The empty element, which renders nothing.
599pub struct Empty;
600
601impl IntoElement for Empty {
602 type Element = Self;
603
604 fn into_element(self) -> Self::Element {
605 self
606 }
607}
608
609impl Element for Empty {
610 type RequestLayoutState = ();
611 type PrepaintState = ();
612
613 fn id(&self) -> Option<ElementId> {
614 None
615 }
616
617 fn request_layout(
618 &mut self,
619 _id: Option<&GlobalElementId>,
620 cx: &mut WindowContext,
621 ) -> (LayoutId, Self::RequestLayoutState) {
622 (cx.request_layout(Style::default(), None), ())
623 }
624
625 fn prepaint(
626 &mut self,
627 _id: Option<&GlobalElementId>,
628 _bounds: Bounds<Pixels>,
629 _state: &mut Self::RequestLayoutState,
630 _cx: &mut WindowContext,
631 ) {
632 }
633
634 fn paint(
635 &mut self,
636 _id: Option<&GlobalElementId>,
637 _bounds: Bounds<Pixels>,
638 _request_layout: &mut Self::RequestLayoutState,
639 _prepaint: &mut Self::PrepaintState,
640 _cx: &mut WindowContext,
641 ) {
642 }
643}