1pub use crate::ViewContext;
2use anyhow::Result;
3use gpui::geometry::vector::Vector2F;
4pub use gpui::{Layout, LayoutId};
5use smallvec::SmallVec;
6
7pub trait Element<V: 'static>: 'static + IntoElement<V> {
8 type PaintState;
9
10 fn layout(
11 &mut self,
12 view: &mut V,
13 cx: &mut ViewContext<V>,
14 ) -> Result<(LayoutId, Self::PaintState)>
15 where
16 Self: Sized;
17
18 fn paint(
19 &mut self,
20 view: &mut V,
21 parent_origin: Vector2F,
22 layout: &Layout,
23 state: &mut Self::PaintState,
24 cx: &mut ViewContext<V>,
25 ) where
26 Self: Sized;
27
28 fn into_any(self) -> AnyElement<V>
29 where
30 Self: 'static + Sized,
31 {
32 AnyElement(Box::new(StatefulElement {
33 element: self,
34 phase: ElementPhase::Init,
35 }))
36 }
37
38 /// Applies a given function `then` to the current element if `condition` is true.
39 /// This function is used to conditionally modify the element based on a given condition.
40 /// If `condition` is false, it just returns the current element as it is.
41 ///
42 /// # Parameters
43 /// - `self`: The current element
44 /// - `condition`: The boolean condition based on which `then` is applied to the element.
45 /// - `then`: A function that takes in the current element and returns a possibly modified element.
46 ///
47 /// # Return
48 /// It returns the potentially modified element.
49 fn when(mut self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
50 where
51 Self: Sized,
52 {
53 if condition {
54 self = then(self);
55 }
56 self
57 }
58}
59
60/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
61trait AnyStatefulElement<V> {
62 fn layout(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> Result<LayoutId>;
63 fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>);
64}
65
66/// A wrapper around an element that stores its layout state.
67struct StatefulElement<V: 'static, E: Element<V>> {
68 element: E,
69 phase: ElementPhase<V, E>,
70}
71
72enum ElementPhase<V: 'static, E: Element<V>> {
73 Init,
74 PostLayout {
75 layout_id: LayoutId,
76 paint_state: E::PaintState,
77 },
78 #[allow(dead_code)]
79 PostPaint {
80 layout: Layout,
81 paint_state: E::PaintState,
82 },
83 Error(String),
84}
85
86impl<V: 'static, E: Element<V>> std::fmt::Debug for ElementPhase<V, E> {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match self {
89 ElementPhase::Init => write!(f, "Init"),
90 ElementPhase::PostLayout { layout_id, .. } => {
91 write!(f, "PostLayout with layout id: {:?}", layout_id)
92 }
93 ElementPhase::PostPaint { layout, .. } => {
94 write!(f, "PostPaint with layout: {:?}", layout)
95 }
96 ElementPhase::Error(err) => write!(f, "Error: {}", err),
97 }
98 }
99}
100
101impl<V: 'static, E: Element<V>> Default for ElementPhase<V, E> {
102 fn default() -> Self {
103 Self::Init
104 }
105}
106
107/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
108impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
109 fn layout(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> Result<LayoutId> {
110 let result;
111 self.phase = match self.element.layout(view, cx) {
112 Ok((layout_id, paint_state)) => {
113 result = Ok(layout_id);
114 ElementPhase::PostLayout {
115 layout_id,
116 paint_state,
117 }
118 }
119 Err(error) => {
120 let message = error.to_string();
121 result = Err(error);
122 ElementPhase::Error(message)
123 }
124 };
125 result
126 }
127
128 fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>) {
129 self.phase = match std::mem::take(&mut self.phase) {
130 ElementPhase::PostLayout {
131 layout_id,
132 mut paint_state,
133 } => match cx.computed_layout(layout_id) {
134 Ok(layout) => {
135 self.element
136 .paint(view, parent_origin, &layout, &mut paint_state, cx);
137 ElementPhase::PostPaint {
138 layout,
139 paint_state,
140 }
141 }
142 Err(error) => ElementPhase::Error(error.to_string()),
143 },
144 ElementPhase::PostPaint {
145 layout,
146 mut paint_state,
147 } => {
148 self.element
149 .paint(view, parent_origin, &layout, &mut paint_state, cx);
150 ElementPhase::PostPaint {
151 layout,
152 paint_state,
153 }
154 }
155 phase @ ElementPhase::Error(_) => phase,
156
157 phase @ _ => {
158 panic!("invalid element phase to call paint: {:?}", phase);
159 }
160 };
161 }
162}
163
164/// A dynamic element.
165pub struct AnyElement<V>(Box<dyn AnyStatefulElement<V>>);
166
167impl<V> AnyElement<V> {
168 pub fn layout(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> Result<LayoutId> {
169 self.0.layout(view, cx)
170 }
171
172 pub fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>) {
173 self.0.paint(view, parent_origin, cx)
174 }
175}
176
177pub trait ParentElement<V: 'static> {
178 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
179
180 fn child(mut self, child: impl IntoElement<V>) -> Self
181 where
182 Self: Sized,
183 {
184 self.children_mut().push(child.into_element().into_any());
185 self
186 }
187
188 fn children<I, E>(mut self, children: I) -> Self
189 where
190 I: IntoIterator<Item = E>,
191 E: IntoElement<V>,
192 Self: Sized,
193 {
194 self.children_mut().extend(
195 children
196 .into_iter()
197 .map(|child| child.into_element().into_any()),
198 );
199 self
200 }
201}
202
203pub trait IntoElement<V: 'static> {
204 type Element: Element<V>;
205
206 fn into_element(self) -> Self::Element;
207}