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