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