1use crate::{
2 util::FluentBuilder, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId,
3 Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
4};
5use derive_more::{Deref, DerefMut};
6pub(crate) use smallvec::SmallVec;
7use std::{any::Any, fmt::Debug};
8
9/// Implemented by types that participate in laying out and painting the contents of a window.
10/// Elements form a tree and are laid out according to web-based layout rules.
11/// Rather than calling methods on implementers of this trait directly, you'll usually call `into_any` to convert them into an AnyElement, which manages state internally.
12/// You can create custom elements by implementing this trait.
13pub trait Element: 'static + IntoElement {
14 type State: 'static;
15
16 fn request_layout(
17 &mut self,
18 state: Option<Self::State>,
19 cx: &mut WindowContext,
20 ) -> (LayoutId, Self::State);
21
22 fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
23
24 fn into_any(self) -> AnyElement {
25 AnyElement::new(self)
26 }
27}
28
29/// Implemented by any type that can be converted into an element.
30pub trait IntoElement: Sized {
31 /// The specific type of element into which the implementing type is converted.
32 type Element: Element;
33
34 /// The [`ElementId`] of self once converted into an [`Element`].
35 /// If present, the resulting element's state will be carried across frames.
36 fn element_id(&self) -> Option<ElementId>;
37
38 /// Convert self into a type that implements [`Element`].
39 fn into_element(self) -> Self::Element;
40
41 /// Convert self into a dynamically-typed [`AnyElement`].
42 fn into_any_element(self) -> AnyElement {
43 self.into_element().into_any()
44 }
45
46 /// Convert into an element, then draw in the current window at the given origin.
47 /// The available space argument is provided to the layout engine to determine the size of the
48 // root element. Once the element is drawn, its associated element state is yielded to the
49 // given callback.
50 fn draw_and_update_state<T, R>(
51 self,
52 origin: Point<Pixels>,
53 available_space: Size<T>,
54 cx: &mut WindowContext,
55 f: impl FnOnce(&mut <Self::Element as Element>::State, &mut WindowContext) -> R,
56 ) -> R
57 where
58 T: Clone + Default + Debug + Into<AvailableSpace>,
59 {
60 let element = self.into_element();
61 let element_id = element.element_id();
62 let element = DrawableElement {
63 element: Some(element),
64 phase: ElementDrawPhase::Start,
65 };
66
67 let frame_state =
68 DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
69
70 if let Some(mut frame_state) = frame_state {
71 f(&mut frame_state, cx)
72 } else {
73 cx.with_element_state(element_id.unwrap(), |element_state, cx| {
74 let mut element_state = element_state.unwrap();
75 let result = f(&mut element_state, cx);
76 (result, element_state)
77 })
78 }
79 }
80}
81
82impl<T: IntoElement> FluentBuilder for T {}
83
84pub trait Render: 'static + Sized {
85 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
86}
87
88impl Render for () {
89 fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
90 ()
91 }
92}
93
94/// You can derive [`IntoElement`] on any type that implements this trait.
95/// It is used to allow views to be expressed in terms of abstract data.
96pub trait RenderOnce: 'static {
97 fn render(self, cx: &mut WindowContext) -> impl IntoElement;
98}
99
100pub trait ParentElement {
101 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
102
103 fn child(mut self, child: impl IntoElement) -> Self
104 where
105 Self: Sized,
106 {
107 self.children_mut().push(child.into_element().into_any());
108 self
109 }
110
111 fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
112 where
113 Self: Sized,
114 {
115 self.children_mut()
116 .extend(children.into_iter().map(|child| child.into_any_element()));
117 self
118 }
119}
120
121pub struct Component<C: RenderOnce>(Option<C>);
122
123impl<C: RenderOnce> Component<C> {
124 pub fn new(component: C) -> Self {
125 Component(Some(component))
126 }
127}
128
129impl<C: RenderOnce> Element for Component<C> {
130 type State = AnyElement;
131
132 fn request_layout(
133 &mut self,
134 _: Option<Self::State>,
135 cx: &mut WindowContext,
136 ) -> (LayoutId, Self::State) {
137 let mut element = self.0.take().unwrap().render(cx).into_any_element();
138 let layout_id = element.request_layout(cx);
139 (layout_id, element)
140 }
141
142 fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
143 element.paint(cx)
144 }
145}
146
147impl<C: RenderOnce> IntoElement for Component<C> {
148 type Element = Self;
149
150 fn element_id(&self) -> Option<ElementId> {
151 None
152 }
153
154 fn into_element(self) -> Self::Element {
155 self
156 }
157}
158
159#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
160pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
161
162trait ElementObject {
163 fn element_id(&self) -> Option<ElementId>;
164
165 fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
166
167 fn paint(&mut self, cx: &mut WindowContext);
168
169 fn measure(
170 &mut self,
171 available_space: Size<AvailableSpace>,
172 cx: &mut WindowContext,
173 ) -> Size<Pixels>;
174
175 fn draw(
176 &mut self,
177 origin: Point<Pixels>,
178 available_space: Size<AvailableSpace>,
179 cx: &mut WindowContext,
180 );
181}
182
183pub struct DrawableElement<E: Element> {
184 element: Option<E>,
185 phase: ElementDrawPhase<E::State>,
186}
187
188#[derive(Default)]
189enum ElementDrawPhase<S> {
190 #[default]
191 Start,
192 LayoutRequested {
193 layout_id: LayoutId,
194 frame_state: Option<S>,
195 },
196 LayoutComputed {
197 layout_id: LayoutId,
198 available_space: Size<AvailableSpace>,
199 frame_state: Option<S>,
200 },
201}
202
203/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
204impl<E: Element> DrawableElement<E> {
205 fn new(element: E) -> Self {
206 DrawableElement {
207 element: Some(element),
208 phase: ElementDrawPhase::Start,
209 }
210 }
211
212 fn element_id(&self) -> Option<ElementId> {
213 self.element.as_ref()?.element_id()
214 }
215
216 fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
217 let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
218 {
219 let layout_id = cx.with_element_state(id, |element_state, cx| {
220 self.element
221 .as_mut()
222 .unwrap()
223 .request_layout(element_state, cx)
224 });
225 (layout_id, None)
226 } else {
227 let (layout_id, frame_state) = self.element.as_mut().unwrap().request_layout(None, cx);
228 (layout_id, Some(frame_state))
229 };
230
231 self.phase = ElementDrawPhase::LayoutRequested {
232 layout_id,
233 frame_state,
234 };
235 layout_id
236 }
237
238 fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> {
239 match self.phase {
240 ElementDrawPhase::LayoutRequested {
241 layout_id,
242 frame_state,
243 }
244 | ElementDrawPhase::LayoutComputed {
245 layout_id,
246 frame_state,
247 ..
248 } => {
249 let bounds = cx.layout_bounds(layout_id);
250
251 if let Some(mut frame_state) = frame_state {
252 self.element
253 .take()
254 .unwrap()
255 .paint(bounds, &mut frame_state, cx);
256 Some(frame_state)
257 } else {
258 let element_id = self
259 .element
260 .as_ref()
261 .unwrap()
262 .element_id()
263 .expect("if we don't have frame state, we should have element state");
264 cx.with_element_state(element_id, |element_state, cx| {
265 let mut element_state = element_state.unwrap();
266 self.element
267 .take()
268 .unwrap()
269 .paint(bounds, &mut element_state, cx);
270 ((), element_state)
271 });
272 None
273 }
274 }
275
276 _ => panic!("must call layout before paint"),
277 }
278 }
279
280 fn measure(
281 &mut self,
282 available_space: Size<AvailableSpace>,
283 cx: &mut WindowContext,
284 ) -> Size<Pixels> {
285 if matches!(&self.phase, ElementDrawPhase::Start) {
286 self.request_layout(cx);
287 }
288
289 let layout_id = match &mut self.phase {
290 ElementDrawPhase::LayoutRequested {
291 layout_id,
292 frame_state,
293 } => {
294 cx.compute_layout(*layout_id, available_space);
295 let layout_id = *layout_id;
296 self.phase = ElementDrawPhase::LayoutComputed {
297 layout_id,
298 available_space,
299 frame_state: frame_state.take(),
300 };
301 layout_id
302 }
303 ElementDrawPhase::LayoutComputed {
304 layout_id,
305 available_space: prev_available_space,
306 ..
307 } => {
308 if available_space != *prev_available_space {
309 cx.compute_layout(*layout_id, available_space);
310 *prev_available_space = available_space;
311 }
312 *layout_id
313 }
314 _ => panic!("cannot measure after painting"),
315 };
316
317 cx.layout_bounds(layout_id).size
318 }
319
320 fn draw(
321 mut self,
322 origin: Point<Pixels>,
323 available_space: Size<AvailableSpace>,
324 cx: &mut WindowContext,
325 ) -> Option<E::State> {
326 self.measure(available_space, cx);
327 cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
328 }
329}
330
331impl<E> ElementObject for Option<DrawableElement<E>>
332where
333 E: Element,
334 E::State: 'static,
335{
336 fn element_id(&self) -> Option<ElementId> {
337 self.as_ref().unwrap().element_id()
338 }
339
340 fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
341 DrawableElement::request_layout(self.as_mut().unwrap(), cx)
342 }
343
344 fn paint(&mut self, cx: &mut WindowContext) {
345 DrawableElement::paint(self.take().unwrap(), cx);
346 }
347
348 fn measure(
349 &mut self,
350 available_space: Size<AvailableSpace>,
351 cx: &mut WindowContext,
352 ) -> Size<Pixels> {
353 DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
354 }
355
356 fn draw(
357 &mut self,
358 origin: Point<Pixels>,
359 available_space: Size<AvailableSpace>,
360 cx: &mut WindowContext,
361 ) {
362 DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
363 }
364}
365
366pub struct AnyElement(ArenaBox<dyn ElementObject>);
367
368impl AnyElement {
369 pub fn new<E>(element: E) -> Self
370 where
371 E: 'static + Element,
372 E::State: Any,
373 {
374 let element = ELEMENT_ARENA
375 .with_borrow_mut(|arena| arena.alloc(|| Some(DrawableElement::new(element))))
376 .map(|element| element as &mut dyn ElementObject);
377 AnyElement(element)
378 }
379
380 pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
381 self.0.request_layout(cx)
382 }
383
384 pub fn paint(&mut self, cx: &mut WindowContext) {
385 self.0.paint(cx)
386 }
387
388 /// Initializes this element and performs layout within the given available space to determine its size.
389 pub fn measure(
390 &mut self,
391 available_space: Size<AvailableSpace>,
392 cx: &mut WindowContext,
393 ) -> Size<Pixels> {
394 self.0.measure(available_space, cx)
395 }
396
397 /// Initializes this element and performs layout in the available space, then paints it at the given origin.
398 pub fn draw(
399 &mut self,
400 origin: Point<Pixels>,
401 available_space: Size<AvailableSpace>,
402 cx: &mut WindowContext,
403 ) {
404 self.0.draw(origin, available_space, cx)
405 }
406
407 pub fn inner_id(&self) -> Option<ElementId> {
408 self.0.element_id()
409 }
410}
411
412impl Element for AnyElement {
413 type State = ();
414
415 fn request_layout(
416 &mut self,
417 _: Option<Self::State>,
418 cx: &mut WindowContext,
419 ) -> (LayoutId, Self::State) {
420 let layout_id = self.request_layout(cx);
421 (layout_id, ())
422 }
423
424 fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
425 self.paint(cx)
426 }
427}
428
429impl IntoElement for AnyElement {
430 type Element = Self;
431
432 fn element_id(&self) -> Option<ElementId> {
433 None
434 }
435
436 fn into_element(self) -> Self::Element {
437 self
438 }
439
440 fn into_any_element(self) -> AnyElement {
441 self
442 }
443}
444
445/// The empty element, which renders nothing.
446pub type Empty = ();
447
448impl IntoElement for () {
449 type Element = Self;
450
451 fn element_id(&self) -> Option<ElementId> {
452 None
453 }
454
455 fn into_element(self) -> Self::Element {
456 self
457 }
458}
459
460impl Element for () {
461 type State = ();
462
463 fn request_layout(
464 &mut self,
465 _state: Option<Self::State>,
466 cx: &mut WindowContext,
467 ) -> (LayoutId, Self::State) {
468 (cx.request_layout(&crate::Style::default(), None), ())
469 }
470
471 fn paint(
472 &mut self,
473 _bounds: Bounds<Pixels>,
474 _state: &mut Self::State,
475 _cx: &mut WindowContext,
476 ) {
477 }
478}