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