1use super::{Layout, LayoutId, Pixels, Point, Result, ViewContext};
2pub(crate) use smallvec::SmallVec;
3
4pub trait Element: 'static {
5 type State;
6 type FrameState;
7
8 fn layout(
9 &mut self,
10 state: &mut Self::State,
11 cx: &mut ViewContext<Self::State>,
12 ) -> Result<(LayoutId, Self::FrameState)>;
13
14 fn paint(
15 &mut self,
16 layout: Layout,
17 state: &mut Self::State,
18 frame_state: &mut Self::FrameState,
19 cx: &mut ViewContext<Self::State>,
20 ) -> Result<()>;
21}
22
23pub trait ParentElement<S> {
24 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<S>; 2]>;
25
26 fn child(mut self, child: impl IntoAnyElement<S>) -> Self
27 where
28 Self: Sized,
29 {
30 self.children_mut().push(child.into_any());
31 self
32 }
33
34 fn children(mut self, iter: impl IntoIterator<Item = impl IntoAnyElement<S>>) -> Self
35 where
36 Self: Sized,
37 {
38 self.children_mut()
39 .extend(iter.into_iter().map(|item| item.into_any()));
40 self
41 }
42
43 // HACK: This is a temporary hack to get children working for the purposes
44 // of building UI on top of the current version of gpui2.
45 //
46 // We'll (hopefully) be moving away from this in the future.
47 fn children_any<I>(mut self, children: I) -> Self
48 where
49 I: IntoIterator<Item = AnyElement<S>>,
50 Self: Sized,
51 {
52 self.children_mut().extend(children.into_iter());
53 self
54 }
55
56 // HACK: This is a temporary hack to get children working for the purposes
57 // of building UI on top of the current version of gpui2.
58 //
59 // We'll (hopefully) be moving away from this in the future.
60 fn child_any(mut self, children: AnyElement<S>) -> Self
61 where
62 Self: Sized,
63 {
64 self.children_mut().push(children);
65 self
66 }
67}
68
69trait ElementObject<S> {
70 fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId>;
71 fn paint(
72 &mut self,
73 state: &mut S,
74 offset: Option<Point<Pixels>>,
75 cx: &mut ViewContext<S>,
76 ) -> Result<()>;
77}
78
79struct RenderedElement<E: Element> {
80 element: E,
81 phase: ElementRenderPhase<E::FrameState>,
82}
83
84#[derive(Default)]
85enum ElementRenderPhase<S> {
86 #[default]
87 Rendered,
88 LayoutRequested {
89 layout_id: LayoutId,
90 frame_state: S,
91 },
92 Painted {
93 layout: Layout,
94 frame_state: S,
95 },
96}
97
98/// Internal struct that wraps an element to store Layout and FrameState after the element is rendered.
99/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
100/// improved usability.
101impl<E: Element> RenderedElement<E> {
102 fn new(element: E) -> Self {
103 RenderedElement {
104 element,
105 phase: ElementRenderPhase::Rendered,
106 }
107 }
108}
109
110impl<E: Element> ElementObject<E::State> for RenderedElement<E> {
111 fn layout(&mut self, state: &mut E::State, cx: &mut ViewContext<E::State>) -> Result<LayoutId> {
112 let (layout_id, frame_state) = self.element.layout(state, cx)?;
113 self.phase = ElementRenderPhase::LayoutRequested {
114 layout_id,
115 frame_state,
116 };
117 Ok(layout_id)
118 }
119
120 fn paint(
121 &mut self,
122 state: &mut E::State,
123 offset: Option<Point<Pixels>>,
124 cx: &mut ViewContext<E::State>,
125 ) -> Result<()> {
126 self.phase = match std::mem::take(&mut self.phase) {
127 ElementRenderPhase::Rendered => panic!("must call layout before paint"),
128
129 ElementRenderPhase::LayoutRequested {
130 layout_id,
131 mut frame_state,
132 } => {
133 let mut layout = cx.layout(layout_id)?.clone();
134 offset.map(|offset| layout.bounds.origin += offset);
135 self.element
136 .paint(layout.clone(), state, &mut frame_state, cx)?;
137 ElementRenderPhase::Painted {
138 layout,
139 frame_state,
140 }
141 }
142
143 ElementRenderPhase::Painted {
144 layout,
145 mut frame_state,
146 } => {
147 self.element
148 .paint(layout.clone(), state, &mut frame_state, cx)?;
149 ElementRenderPhase::Painted {
150 layout,
151 frame_state,
152 }
153 }
154 };
155
156 Ok(())
157 }
158}
159
160pub struct AnyElement<S>(Box<dyn ElementObject<S>>);
161
162impl<S> AnyElement<S> {
163 pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId> {
164 self.0.layout(state, cx)
165 }
166
167 pub fn paint(
168 &mut self,
169 state: &mut S,
170 offset: Option<Point<Pixels>>,
171 cx: &mut ViewContext<S>,
172 ) -> Result<()> {
173 self.0.paint(state, offset, cx)
174 }
175}
176
177pub trait IntoAnyElement<S> {
178 fn into_any(self) -> AnyElement<S>;
179}
180
181impl<E: Element> IntoAnyElement<E::State> for E {
182 fn into_any(self) -> AnyElement<E::State> {
183 AnyElement(Box::new(RenderedElement::new(self)))
184 }
185}
186
187impl<S> IntoAnyElement<S> for AnyElement<S> {
188 fn into_any(self) -> AnyElement<S> {
189 self
190 }
191}