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`], type.
18//! If an element returns an [`ElementId`] from [`IntoElement::element_id()`], and that element id
19//! appears in the same place relative to other views and ElementIds in the frame, then the previous
20//! frame's state will be passed to the element's layout and paint methods.
21//!
22//! # Implementing your own elements
23//!
24//! Elements are intended to be the low level, imperative API to GPUI. They are responsible for upholding,
25//! or breaking, GPUI's features as they deem necessary. As an example, most GPUI elements are expected
26//! to stay in the bounds that their parent element gives them. But with [`WindowContext::break_content_mask`],
27//! you can ignore this restriction and paint anywhere inside of the window's bounds. This is useful for overlays
28//! and popups and anything else that shows up 'on top' of other elements.
29//! With great power, comes great responsibility.
30//!
31//! However, most of the time, you won't need to implement your own elements. GPUI provides a number of
32//! elements that should cover most common use cases out of the box and it's recommended that you use those
33//! to construct `components`, using the `RenderOnce` trait and the `#[derive(IntoElement)]` macro. Only implement
34//! elements when you need to take manual control of the layout and painting process, such as when using
35//! your own custom layout algorithm or rendering a code editor.
36
37use crate::{
38 util::FluentBuilder, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId,
39 Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
40};
41use derive_more::{Deref, DerefMut};
42pub(crate) use smallvec::SmallVec;
43use std::{any::Any, fmt::Debug};
44
45/// Implemented by types that participate in laying out and painting the contents of a window.
46/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
47/// You can create custom elements by implementing this trait, see the module-level documentation
48/// for more details.
49pub trait Element: 'static + IntoElement {
50 /// The type of state to store for this element between frames. See the module-level documentation
51 /// for details.
52 type State: 'static;
53
54 /// Before an element can be painted, we need to know where it's going to be and how big it is.
55 /// Use this method to request a layout from Taffy and initialize the element's state.
56 fn request_layout(
57 &mut self,
58 state: Option<Self::State>,
59 cx: &mut WindowContext,
60 ) -> (LayoutId, Self::State);
61
62 /// Once layout has been completed, this method will be called to paint the element to the screen.
63 /// The state argument is the same state that was returned from [`Element::request_layout()`].
64 fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
65
66 /// Convert this element into a dynamically-typed [`AnyElement`].
67 fn into_any(self) -> AnyElement {
68 AnyElement::new(self)
69 }
70}
71
72/// Implemented by any type that can be converted into an element.
73pub trait IntoElement: Sized {
74 /// The specific type of element into which the implementing type is converted.
75 /// Useful for converting other types into elements automatically, like Strings
76 type Element: Element;
77
78 /// The [`ElementId`] of self once converted into an [`Element`].
79 /// If present, the resulting element's state will be carried across frames.
80 fn element_id(&self) -> Option<ElementId>;
81
82 /// Convert self into a type that implements [`Element`].
83 fn into_element(self) -> Self::Element;
84
85 /// Convert self into a dynamically-typed [`AnyElement`].
86 fn into_any_element(self) -> AnyElement {
87 self.into_element().into_any()
88 }
89
90 /// Convert into an element, then draw in the current window at the given origin.
91 /// The available space argument is provided to the layout engine to determine the size of the
92 // root element. Once the element is drawn, its associated element state is yielded to the
93 // given callback.
94 fn draw_and_update_state<T, R>(
95 self,
96 origin: Point<Pixels>,
97 available_space: Size<T>,
98 cx: &mut WindowContext,
99 f: impl FnOnce(&mut <Self::Element as Element>::State, &mut WindowContext) -> R,
100 ) -> R
101 where
102 T: Clone + Default + Debug + Into<AvailableSpace>,
103 {
104 let element = self.into_element();
105 let element_id = element.element_id();
106 let element = DrawableElement {
107 element: Some(element),
108 phase: ElementDrawPhase::Start,
109 };
110
111 let frame_state =
112 DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
113
114 if let Some(mut frame_state) = frame_state {
115 f(&mut frame_state, cx)
116 } else {
117 cx.with_element_state(element_id.unwrap(), |element_state, cx| {
118 let mut element_state = element_state.unwrap();
119 let result = f(&mut element_state, cx);
120 (result, element_state)
121 })
122 }
123 }
124}
125
126impl<T: IntoElement> FluentBuilder for T {}
127
128/// An object that can be drawn to the screen. This is the trait that distinguishes `Views` from
129/// models. Views are drawn to the screen and care about the current window's state, models are not and do not.
130pub trait Render: 'static + Sized {
131 /// Render this view into an element tree.
132 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
133}
134
135impl Render for () {
136 fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
137 ()
138 }
139}
140
141/// You can derive [`IntoElement`] on any type that implements this trait.
142/// It is used to construct reusable `components` out of plain data. Think of
143/// components as a recipe for a certain pattern of elements. RenderOnce allows
144/// you to invoke this pattern, without breaking the fluent builder pattern of
145/// the element APIs.
146pub trait RenderOnce: 'static {
147 /// Render this component into an element tree. Note that this method
148 /// takes ownership of self, as compared to [`Render::render()`] method
149 /// which takes a mutable reference.
150 fn render(self, cx: &mut WindowContext) -> impl IntoElement;
151}
152
153/// This is a helper trait to provide a uniform interface for constructing elements that
154/// can accept any number of any kind of child elements
155pub trait ParentElement {
156 /// Extend this element's children with the given child elements.
157 fn extend(&mut self, elements: impl Iterator<Item = AnyElement>);
158
159 /// Add a single child element to this element.
160 fn child(mut self, child: impl IntoElement) -> Self
161 where
162 Self: Sized,
163 {
164 self.extend(std::iter::once(child.into_element().into_any()));
165 self
166 }
167
168 /// Add multiple child elements to this element.
169 fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
170 where
171 Self: Sized,
172 {
173 self.extend(children.into_iter().map(|child| child.into_any_element()));
174 self
175 }
176}
177
178/// An element for rendering components. An implementation detail of the [`IntoElement`] derive macro
179/// for [`RenderOnce`]
180#[doc(hidden)]
181pub struct Component<C: RenderOnce>(Option<C>);
182
183impl<C: RenderOnce> Component<C> {
184 /// Create a new component from the given RenderOnce type.
185 pub fn new(component: C) -> Self {
186 Component(Some(component))
187 }
188}
189
190impl<C: RenderOnce> Element for Component<C> {
191 type State = AnyElement;
192
193 fn request_layout(
194 &mut self,
195 _: Option<Self::State>,
196 cx: &mut WindowContext,
197 ) -> (LayoutId, Self::State) {
198 let mut element = self.0.take().unwrap().render(cx).into_any_element();
199 let layout_id = element.request_layout(cx);
200 (layout_id, element)
201 }
202
203 fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
204 element.paint(cx)
205 }
206}
207
208impl<C: RenderOnce> IntoElement for Component<C> {
209 type Element = Self;
210
211 fn element_id(&self) -> Option<ElementId> {
212 None
213 }
214
215 fn into_element(self) -> Self::Element {
216 self
217 }
218}
219
220/// A globally unique identifier for an element, used to track state across frames.
221#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
222pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
223
224trait ElementObject {
225 fn element_id(&self) -> Option<ElementId>;
226
227 fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
228
229 fn paint(&mut self, cx: &mut WindowContext);
230
231 fn measure(
232 &mut self,
233 available_space: Size<AvailableSpace>,
234 cx: &mut WindowContext,
235 ) -> Size<Pixels>;
236
237 fn draw(
238 &mut self,
239 origin: Point<Pixels>,
240 available_space: Size<AvailableSpace>,
241 cx: &mut WindowContext,
242 );
243}
244
245/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
246pub(crate) struct DrawableElement<E: Element> {
247 element: Option<E>,
248 phase: ElementDrawPhase<E::State>,
249}
250
251#[derive(Default)]
252enum ElementDrawPhase<S> {
253 #[default]
254 Start,
255 LayoutRequested {
256 layout_id: LayoutId,
257 frame_state: Option<S>,
258 },
259 LayoutComputed {
260 layout_id: LayoutId,
261 available_space: Size<AvailableSpace>,
262 frame_state: Option<S>,
263 },
264}
265
266/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
267impl<E: Element> DrawableElement<E> {
268 fn new(element: E) -> Self {
269 DrawableElement {
270 element: Some(element),
271 phase: ElementDrawPhase::Start,
272 }
273 }
274
275 fn element_id(&self) -> Option<ElementId> {
276 self.element.as_ref()?.element_id()
277 }
278
279 fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
280 let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
281 {
282 let layout_id = cx.with_element_state(id, |element_state, cx| {
283 self.element
284 .as_mut()
285 .unwrap()
286 .request_layout(element_state, cx)
287 });
288 (layout_id, None)
289 } else {
290 let (layout_id, frame_state) = self.element.as_mut().unwrap().request_layout(None, cx);
291 (layout_id, Some(frame_state))
292 };
293
294 self.phase = ElementDrawPhase::LayoutRequested {
295 layout_id,
296 frame_state,
297 };
298 layout_id
299 }
300
301 fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> {
302 match self.phase {
303 ElementDrawPhase::LayoutRequested {
304 layout_id,
305 frame_state,
306 }
307 | ElementDrawPhase::LayoutComputed {
308 layout_id,
309 frame_state,
310 ..
311 } => {
312 let bounds = cx.layout_bounds(layout_id);
313
314 if let Some(mut frame_state) = frame_state {
315 self.element
316 .take()
317 .unwrap()
318 .paint(bounds, &mut frame_state, cx);
319 Some(frame_state)
320 } else {
321 let element_id = self
322 .element
323 .as_ref()
324 .unwrap()
325 .element_id()
326 .expect("if we don't have frame state, we should have element state");
327 cx.with_element_state(element_id, |element_state, cx| {
328 let mut element_state = element_state.unwrap();
329 self.element
330 .take()
331 .unwrap()
332 .paint(bounds, &mut element_state, cx);
333 ((), element_state)
334 });
335 None
336 }
337 }
338
339 _ => panic!("must call layout before paint"),
340 }
341 }
342
343 fn measure(
344 &mut self,
345 available_space: Size<AvailableSpace>,
346 cx: &mut WindowContext,
347 ) -> Size<Pixels> {
348 if matches!(&self.phase, ElementDrawPhase::Start) {
349 self.request_layout(cx);
350 }
351
352 let layout_id = match &mut self.phase {
353 ElementDrawPhase::LayoutRequested {
354 layout_id,
355 frame_state,
356 } => {
357 cx.compute_layout(*layout_id, available_space);
358 let layout_id = *layout_id;
359 self.phase = ElementDrawPhase::LayoutComputed {
360 layout_id,
361 available_space,
362 frame_state: frame_state.take(),
363 };
364 layout_id
365 }
366 ElementDrawPhase::LayoutComputed {
367 layout_id,
368 available_space: prev_available_space,
369 ..
370 } => {
371 if available_space != *prev_available_space {
372 cx.compute_layout(*layout_id, available_space);
373 *prev_available_space = available_space;
374 }
375 *layout_id
376 }
377 _ => panic!("cannot measure after painting"),
378 };
379
380 cx.layout_bounds(layout_id).size
381 }
382
383 fn draw(
384 mut self,
385 origin: Point<Pixels>,
386 available_space: Size<AvailableSpace>,
387 cx: &mut WindowContext,
388 ) -> Option<E::State> {
389 self.measure(available_space, cx);
390 cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
391 }
392}
393
394impl<E> ElementObject for Option<DrawableElement<E>>
395where
396 E: Element,
397 E::State: 'static,
398{
399 fn element_id(&self) -> Option<ElementId> {
400 self.as_ref().unwrap().element_id()
401 }
402
403 fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
404 DrawableElement::request_layout(self.as_mut().unwrap(), cx)
405 }
406
407 fn paint(&mut self, cx: &mut WindowContext) {
408 DrawableElement::paint(self.take().unwrap(), cx);
409 }
410
411 fn measure(
412 &mut self,
413 available_space: Size<AvailableSpace>,
414 cx: &mut WindowContext,
415 ) -> Size<Pixels> {
416 DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
417 }
418
419 fn draw(
420 &mut self,
421 origin: Point<Pixels>,
422 available_space: Size<AvailableSpace>,
423 cx: &mut WindowContext,
424 ) {
425 DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
426 }
427}
428
429/// A dynamically typed element that can be used to store any element type.
430pub struct AnyElement(ArenaBox<dyn ElementObject>);
431
432impl AnyElement {
433 pub(crate) fn new<E>(element: E) -> Self
434 where
435 E: 'static + Element,
436 E::State: Any,
437 {
438 let element = ELEMENT_ARENA
439 .with_borrow_mut(|arena| arena.alloc(|| Some(DrawableElement::new(element))))
440 .map(|element| element as &mut dyn ElementObject);
441 AnyElement(element)
442 }
443
444 /// Request the layout ID of the element stored in this `AnyElement`.
445 /// Used for laying out child elements in a parent element.
446 pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
447 self.0.request_layout(cx)
448 }
449
450 /// Paints the element stored in this `AnyElement`.
451 pub fn paint(&mut self, cx: &mut WindowContext) {
452 self.0.paint(cx)
453 }
454
455 /// Initializes this element and performs layout within the given available space to determine its size.
456 pub fn measure(
457 &mut self,
458 available_space: Size<AvailableSpace>,
459 cx: &mut WindowContext,
460 ) -> Size<Pixels> {
461 self.0.measure(available_space, cx)
462 }
463
464 /// Initializes this element and performs layout in the available space, then paints it at the given origin.
465 pub fn draw(
466 &mut self,
467 origin: Point<Pixels>,
468 available_space: Size<AvailableSpace>,
469 cx: &mut WindowContext,
470 ) {
471 self.0.draw(origin, available_space, cx)
472 }
473
474 /// Returns the element ID of the element stored in this `AnyElement`, if any.
475 pub fn inner_id(&self) -> Option<ElementId> {
476 self.0.element_id()
477 }
478}
479
480impl Element for AnyElement {
481 type State = ();
482
483 fn request_layout(
484 &mut self,
485 _: Option<Self::State>,
486 cx: &mut WindowContext,
487 ) -> (LayoutId, Self::State) {
488 let layout_id = self.request_layout(cx);
489 (layout_id, ())
490 }
491
492 fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
493 self.paint(cx)
494 }
495}
496
497impl IntoElement for AnyElement {
498 type Element = Self;
499
500 fn element_id(&self) -> Option<ElementId> {
501 None
502 }
503
504 fn into_element(self) -> Self::Element {
505 self
506 }
507
508 fn into_any_element(self) -> AnyElement {
509 self
510 }
511}
512
513/// The empty element, which renders nothing.
514pub type Empty = ();
515
516impl IntoElement for () {
517 type Element = Self;
518
519 fn element_id(&self) -> Option<ElementId> {
520 None
521 }
522
523 fn into_element(self) -> Self::Element {
524 self
525 }
526}
527
528impl Element for () {
529 type State = ();
530
531 fn request_layout(
532 &mut self,
533 _state: Option<Self::State>,
534 cx: &mut WindowContext,
535 ) -> (LayoutId, Self::State) {
536 (cx.request_layout(&crate::Style::default(), None), ())
537 }
538
539 fn paint(
540 &mut self,
541 _bounds: Bounds<Pixels>,
542 _state: &mut Self::State,
543 _cx: &mut WindowContext,
544 ) {
545 }
546}