1use crate::{
2 AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size, WindowContext,
3};
4use derive_more::{Deref, DerefMut};
5pub(crate) use smallvec::SmallVec;
6use std::{any::Any, fmt::Debug, mem};
7
8pub trait Element {
9 type ElementState: 'static;
10
11 fn element_id(&self) -> Option<ElementId>;
12
13 fn layout(
14 &mut self,
15 element_state: Option<Self::ElementState>,
16 cx: &mut WindowContext,
17 ) -> (LayoutId, Self::ElementState);
18
19 fn paint(
20 &mut self,
21 bounds: Bounds<Pixels>,
22 element_state: &mut Self::ElementState,
23 cx: &mut WindowContext,
24 );
25
26 fn draw<T, R>(
27 self,
28 origin: Point<Pixels>,
29 available_space: Size<T>,
30 cx: &mut WindowContext,
31 f: impl FnOnce(&Self::ElementState, &mut WindowContext) -> R,
32 ) -> R
33 where
34 Self: Sized,
35 T: Clone + Default + Debug + Into<AvailableSpace>,
36 {
37 let mut element = RenderedElement {
38 element: self,
39 phase: ElementRenderPhase::Start,
40 };
41 element.draw(origin, available_space.map(Into::into), cx);
42 if let ElementRenderPhase::Painted { frame_state } = &element.phase {
43 if let Some(frame_state) = frame_state.as_ref() {
44 f(&frame_state, cx)
45 } else {
46 let element_id = element
47 .element
48 .element_id()
49 .expect("we either have some frame_state or some element_id");
50 cx.with_element_state(element_id, |element_state, cx| {
51 let element_state = element_state.unwrap();
52 let result = f(&element_state, cx);
53 (result, element_state)
54 })
55 }
56 } else {
57 unreachable!()
58 }
59 }
60}
61
62#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
63pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
64
65pub trait ParentComponent {
66 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
67
68 fn child(mut self, child: impl Component) -> Self
69 where
70 Self: Sized,
71 {
72 self.children_mut().push(child.render());
73 self
74 }
75
76 fn children(mut self, iter: impl IntoIterator<Item = impl Component>) -> Self
77 where
78 Self: Sized,
79 {
80 self.children_mut()
81 .extend(iter.into_iter().map(|item| item.render()));
82 self
83 }
84}
85
86trait ElementObject {
87 fn element_id(&self) -> Option<ElementId>;
88 fn layout(&mut self, cx: &mut WindowContext) -> LayoutId;
89 fn paint(&mut self, cx: &mut WindowContext);
90 fn measure(
91 &mut self,
92 available_space: Size<AvailableSpace>,
93 cx: &mut WindowContext,
94 ) -> Size<Pixels>;
95 fn draw(
96 &mut self,
97 origin: Point<Pixels>,
98 available_space: Size<AvailableSpace>,
99 cx: &mut WindowContext,
100 );
101}
102
103struct RenderedElement<E: Element> {
104 element: E,
105 phase: ElementRenderPhase<E::ElementState>,
106}
107
108#[derive(Default)]
109enum ElementRenderPhase<V> {
110 #[default]
111 Start,
112 LayoutRequested {
113 layout_id: LayoutId,
114 frame_state: Option<V>,
115 },
116 LayoutComputed {
117 layout_id: LayoutId,
118 available_space: Size<AvailableSpace>,
119 frame_state: Option<V>,
120 },
121 Painted {
122 frame_state: Option<V>,
123 },
124}
125
126/// Internal struct that wraps an element to store Layout and ElementState after the element is rendered.
127/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
128/// improved usability.
129impl<E: Element> RenderedElement<E> {
130 fn new(element: E) -> Self {
131 RenderedElement {
132 element,
133 phase: ElementRenderPhase::Start,
134 }
135 }
136}
137
138impl<E> ElementObject for RenderedElement<E>
139where
140 E: Element,
141 E::ElementState: 'static,
142{
143 fn element_id(&self) -> Option<ElementId> {
144 self.element.element_id()
145 }
146
147 fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
148 let (layout_id, frame_state) = match mem::take(&mut self.phase) {
149 ElementRenderPhase::Start => {
150 if let Some(id) = self.element.element_id() {
151 let layout_id = cx.with_element_state(id, |element_state, cx| {
152 self.element.layout(element_state, cx)
153 });
154 (layout_id, None)
155 } else {
156 let (layout_id, frame_state) = self.element.layout(None, cx);
157 (layout_id, Some(frame_state))
158 }
159 }
160 ElementRenderPhase::LayoutRequested { .. }
161 | ElementRenderPhase::LayoutComputed { .. }
162 | ElementRenderPhase::Painted { .. } => {
163 panic!("element rendered twice")
164 }
165 };
166
167 self.phase = ElementRenderPhase::LayoutRequested {
168 layout_id,
169 frame_state,
170 };
171 layout_id
172 }
173
174 fn paint(&mut self, cx: &mut WindowContext) {
175 self.phase = match mem::take(&mut self.phase) {
176 ElementRenderPhase::LayoutRequested {
177 layout_id,
178 mut frame_state,
179 }
180 | ElementRenderPhase::LayoutComputed {
181 layout_id,
182 mut frame_state,
183 ..
184 } => {
185 let bounds = cx.layout_bounds(layout_id);
186 if let Some(id) = self.element.element_id() {
187 cx.with_element_state(id, |element_state, cx| {
188 let mut element_state = element_state.unwrap();
189 self.element.paint(bounds, &mut element_state, cx);
190 ((), element_state)
191 });
192 } else {
193 self.element
194 .paint(bounds, frame_state.as_mut().unwrap(), cx);
195 }
196 ElementRenderPhase::Painted { frame_state }
197 }
198
199 _ => panic!("must call layout before paint"),
200 };
201 }
202
203 fn measure(
204 &mut self,
205 available_space: Size<AvailableSpace>,
206 cx: &mut WindowContext,
207 ) -> Size<Pixels> {
208 if matches!(&self.phase, ElementRenderPhase::Start) {
209 self.layout(cx);
210 }
211
212 let layout_id = match &mut self.phase {
213 ElementRenderPhase::LayoutRequested {
214 layout_id,
215 frame_state,
216 } => {
217 cx.compute_layout(*layout_id, available_space);
218 let layout_id = *layout_id;
219 self.phase = ElementRenderPhase::LayoutComputed {
220 layout_id,
221 available_space,
222 frame_state: frame_state.take(),
223 };
224 layout_id
225 }
226 ElementRenderPhase::LayoutComputed {
227 layout_id,
228 available_space: prev_available_space,
229 ..
230 } => {
231 if available_space != *prev_available_space {
232 cx.compute_layout(*layout_id, available_space);
233 *prev_available_space = available_space;
234 }
235 *layout_id
236 }
237 _ => panic!("cannot measure after painting"),
238 };
239
240 cx.layout_bounds(layout_id).size
241 }
242
243 fn draw(
244 &mut self,
245 origin: Point<Pixels>,
246 available_space: Size<AvailableSpace>,
247 cx: &mut WindowContext,
248 ) {
249 self.measure(available_space, cx);
250 cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
251 }
252}
253
254pub struct AnyElement(Box<dyn ElementObject>);
255
256impl AnyElement {
257 pub fn new<E>(element: E) -> Self
258 where
259 E: 'static + Element,
260 E::ElementState: Any,
261 {
262 AnyElement(Box::new(RenderedElement::new(element)))
263 }
264
265 pub fn element_id(&self) -> Option<ElementId> {
266 self.0.element_id()
267 }
268
269 pub fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
270 self.0.layout(cx)
271 }
272
273 pub fn paint(&mut self, cx: &mut WindowContext) {
274 self.0.paint(cx)
275 }
276
277 /// Initializes this element and performs layout within the given available space to determine its size.
278 pub fn measure(
279 &mut self,
280 available_space: Size<AvailableSpace>,
281 cx: &mut WindowContext,
282 ) -> Size<Pixels> {
283 self.0.measure(available_space, cx)
284 }
285
286 /// Initializes this element and performs layout in the available space, then paints it at the given origin.
287 pub fn draw(
288 &mut self,
289 origin: Point<Pixels>,
290 available_space: Size<AvailableSpace>,
291 cx: &mut WindowContext,
292 ) {
293 self.0.draw(origin, available_space, cx)
294 }
295}
296
297pub trait Component {
298 fn render(self) -> AnyElement;
299
300 fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
301 where
302 Self: Sized,
303 U: Component,
304 {
305 f(self)
306 }
307
308 fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
309 where
310 Self: Sized,
311 {
312 self.map(|this| if condition { then(this) } else { this })
313 }
314
315 fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
316 where
317 Self: Sized,
318 {
319 self.map(|this| {
320 if let Some(value) = option {
321 then(this, value)
322 } else {
323 this
324 }
325 })
326 }
327}
328
329impl Component for AnyElement {
330 fn render(self) -> AnyElement {
331 self
332 }
333}
334
335impl<E, F> Element for Option<F>
336where
337 E: 'static + Component,
338 F: FnOnce(&mut WindowContext) -> E + 'static,
339{
340 type ElementState = AnyElement;
341
342 fn element_id(&self) -> Option<ElementId> {
343 None
344 }
345
346 fn layout(
347 &mut self,
348 _: Option<Self::ElementState>,
349 cx: &mut WindowContext,
350 ) -> (LayoutId, Self::ElementState) {
351 let render = self.take().unwrap();
352 let mut rendered_element = (render)(cx).render();
353 let layout_id = rendered_element.layout(cx);
354 (layout_id, rendered_element)
355 }
356
357 fn paint(
358 &mut self,
359 _bounds: Bounds<Pixels>,
360 rendered_element: &mut Self::ElementState,
361 cx: &mut WindowContext,
362 ) {
363 rendered_element.paint(cx)
364 }
365}
366
367impl<E, F> Component for Option<F>
368where
369 E: 'static + Component,
370 F: FnOnce(&mut WindowContext) -> E + 'static,
371{
372 fn render(self) -> AnyElement {
373 AnyElement::new(self)
374 }
375}
376
377impl<E, F> Component for F
378where
379 E: 'static + Component,
380 F: FnOnce(&mut WindowContext) -> E + 'static,
381{
382 fn render(self) -> AnyElement {
383 AnyElement::new(Some(self))
384 }
385}