1use std::{any::Any, marker::PhantomData};
2
3use pathfinder_geometry::{rect::RectF, vector::Vector2F};
4
5use crate::{AnyElement, Element, SizeConstraint, ViewContext};
6
7use super::Empty;
8
9/// The core stateless component trait, simply rendering an element tree
10pub trait Component {
11 fn render<V: 'static>(self, cx: &mut ViewContext<V>) -> AnyElement<V>;
12
13 fn element<V: 'static>(self) -> ComponentAdapter<V, Self>
14 where
15 Self: Sized,
16 {
17 ComponentAdapter::new(self)
18 }
19
20 fn stylable(self) -> StylableAdapter<Self>
21 where
22 Self: Sized,
23 {
24 StylableAdapter::new(self)
25 }
26
27 fn stateful<V: 'static>(self) -> StatefulAdapter<Self, V>
28 where
29 Self: Sized,
30 {
31 StatefulAdapter::new(self)
32 }
33}
34
35/// Allows a a component's styles to be rebound in a simple way.
36pub trait Stylable: Component {
37 type Style: Clone;
38
39 fn with_style(self, style: Self::Style) -> Self;
40}
41
42/// This trait models the typestate pattern for a component's style,
43/// enforcing at compile time that a component is only usable after
44/// it has been styled while still allowing for late binding of the
45/// styling information
46pub trait SafeStylable {
47 type Style: Clone;
48 type Output: Component;
49
50 fn with_style(self, style: Self::Style) -> Self::Output;
51}
52
53/// All stylable components can trivially implement SafeStylable
54impl<C: Stylable> SafeStylable for C {
55 type Style = C::Style;
56
57 type Output = C;
58
59 fn with_style(self, style: Self::Style) -> Self::Output {
60 self.with_style(style)
61 }
62}
63
64/// Allows converting an unstylable component into a stylable one
65/// by using `()` as the style type
66pub struct StylableAdapter<C: Component> {
67 component: C,
68}
69
70impl<C: Component> StylableAdapter<C> {
71 pub fn new(component: C) -> Self {
72 Self { component }
73 }
74}
75
76impl<C: Component> SafeStylable for StylableAdapter<C> {
77 type Style = ();
78
79 type Output = C;
80
81 fn with_style(self, _: Self::Style) -> Self::Output {
82 self.component
83 }
84}
85
86/// This is a secondary trait for components that can be styled
87/// which rely on their view's state. This is useful for components that, for example,
88/// want to take click handler callbacks Unfortunately, the generic bound on the
89/// Component trait makes it incompatible with the stateless components above.
90// So let's just replicate them for now
91pub trait StatefulComponent<V: 'static> {
92 fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
93
94 fn element(self) -> ComponentAdapter<V, Self>
95 where
96 Self: Sized,
97 {
98 ComponentAdapter::new(self)
99 }
100
101 fn styleable(self) -> StatefulStylableAdapter<Self, V>
102 where
103 Self: Sized,
104 {
105 StatefulStylableAdapter::new(self)
106 }
107
108 fn stateless(self) -> StatelessElementAdapter
109 where
110 Self: Sized + 'static,
111 {
112 StatelessElementAdapter::new(self.element().into_any())
113 }
114}
115
116/// It is trivial to convert stateless components to stateful components, so lets
117/// do so en masse. Note that the reverse is impossible without a helper.
118impl<V: 'static, C: Component> StatefulComponent<V> for C {
119 fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
120 self.render(cx)
121 }
122}
123
124/// Same as stylable, but generic over a view type
125pub trait StatefulStylable<V: 'static>: StatefulComponent<V> {
126 type Style: Clone;
127
128 fn with_style(self, style: Self::Style) -> Self;
129}
130
131/// Same as SafeStylable, but generic over a view type
132pub trait StatefulSafeStylable<V: 'static> {
133 type Style: Clone;
134 type Output: StatefulComponent<V>;
135
136 fn with_style(self, style: Self::Style) -> Self::Output;
137}
138
139/// Converting from stateless to stateful
140impl<V: 'static, C: SafeStylable> StatefulSafeStylable<V> for C {
141 type Style = C::Style;
142
143 type Output = C::Output;
144
145 fn with_style(self, style: Self::Style) -> Self::Output {
146 self.with_style(style)
147 }
148}
149
150// A helper for converting stateless components into stateful ones
151pub struct StatefulAdapter<C, V> {
152 component: C,
153 phantom: std::marker::PhantomData<V>,
154}
155
156impl<C: Component, V: 'static> StatefulAdapter<C, V> {
157 pub fn new(component: C) -> Self {
158 Self {
159 component,
160 phantom: std::marker::PhantomData,
161 }
162 }
163}
164
165impl<C: Component, V: 'static> StatefulComponent<V> for StatefulAdapter<C, V> {
166 fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
167 self.component.render(cx)
168 }
169}
170
171// A helper for converting stateful but style-less components into stylable ones
172// by using `()` as the style type
173pub struct StatefulStylableAdapter<C: StatefulComponent<V>, V: 'static> {
174 component: C,
175 phantom: std::marker::PhantomData<V>,
176}
177
178impl<C: StatefulComponent<V>, V: 'static> StatefulStylableAdapter<C, V> {
179 pub fn new(component: C) -> Self {
180 Self {
181 component,
182 phantom: std::marker::PhantomData,
183 }
184 }
185}
186
187impl<C: StatefulComponent<V>, V: 'static> StatefulSafeStylable<V>
188 for StatefulStylableAdapter<C, V>
189{
190 type Style = ();
191
192 type Output = C;
193
194 fn with_style(self, _: Self::Style) -> Self::Output {
195 self.component
196 }
197}
198
199/// A way of erasing the view generic from an element, useful
200/// for wrapping up an explicit element tree into stateless
201/// components
202pub struct StatelessElementAdapter {
203 element: Box<dyn Any>,
204}
205
206impl StatelessElementAdapter {
207 pub fn new<V: 'static>(element: AnyElement<V>) -> Self {
208 StatelessElementAdapter {
209 element: Box::new(element) as Box<dyn Any>,
210 }
211 }
212}
213
214impl Component for StatelessElementAdapter {
215 fn render<V: 'static>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
216 *self
217 .element
218 .downcast::<AnyElement<V>>()
219 .expect("Don't move elements out of their view :(")
220 }
221}
222
223// For converting elements into stateful components
224pub struct StatefulElementAdapter<V: 'static> {
225 element: AnyElement<V>,
226 _phantom: std::marker::PhantomData<V>,
227}
228
229impl<V: 'static> StatefulElementAdapter<V> {
230 pub fn new(element: AnyElement<V>) -> Self {
231 Self {
232 element,
233 _phantom: std::marker::PhantomData,
234 }
235 }
236}
237
238impl<V: 'static> StatefulComponent<V> for StatefulElementAdapter<V> {
239 fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
240 self.element
241 }
242}
243
244/// A convenient shorthand for creating an empty component.
245impl Component for () {
246 fn render<V: 'static>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
247 Empty::new().into_any()
248 }
249}
250
251impl Stylable for () {
252 type Style = ();
253
254 fn with_style(self, _: Self::Style) -> Self {
255 ()
256 }
257}
258
259// For converting components back into Elements
260pub struct ComponentAdapter<V: 'static, E> {
261 component: Option<E>,
262 element: Option<AnyElement<V>>,
263 phantom: PhantomData<V>,
264}
265
266impl<E, V: 'static> ComponentAdapter<V, E> {
267 pub fn new(e: E) -> Self {
268 Self {
269 component: Some(e),
270 element: None,
271 phantom: PhantomData,
272 }
273 }
274}
275
276impl<V: 'static, C: StatefulComponent<V> + 'static> Element<V> for ComponentAdapter<V, C> {
277 type LayoutState = ();
278
279 type PaintState = ();
280
281 fn layout(
282 &mut self,
283 constraint: SizeConstraint,
284 view: &mut V,
285 cx: &mut ViewContext<V>,
286 ) -> (Vector2F, Self::LayoutState) {
287 if self.element.is_none() {
288 let element = self
289 .component
290 .take()
291 .expect("Component can only be rendered once")
292 .render(view, cx);
293 self.element = Some(element);
294 }
295 let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
296 (constraint, ())
297 }
298
299 fn paint(
300 &mut self,
301 bounds: RectF,
302 visible_bounds: RectF,
303 _: &mut Self::LayoutState,
304 view: &mut V,
305 cx: &mut ViewContext<V>,
306 ) -> Self::PaintState {
307 self.element
308 .as_mut()
309 .expect("Layout should always be called before paint")
310 .paint(bounds.origin(), visible_bounds, view, cx)
311 }
312
313 fn rect_for_text_range(
314 &self,
315 range_utf16: std::ops::Range<usize>,
316 _: RectF,
317 _: RectF,
318 _: &Self::LayoutState,
319 _: &Self::PaintState,
320 view: &V,
321 cx: &ViewContext<V>,
322 ) -> Option<RectF> {
323 self.element
324 .as_ref()
325 .and_then(|el| el.rect_for_text_range(range_utf16, view, cx))
326 }
327
328 fn debug(
329 &self,
330 _: RectF,
331 _: &Self::LayoutState,
332 _: &Self::PaintState,
333 view: &V,
334 cx: &ViewContext<V>,
335 ) -> serde_json::Value {
336 serde_json::json!({
337 "type": "ComponentAdapter",
338 "component": std::any::type_name::<C>(),
339 "child": self.element.as_ref().map(|el| el.debug(view, cx)),
340 })
341 }
342}