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
12pub trait GeneralComponent {
13 fn render<V: View>(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
14
15 fn element<V: View>(self) -> ComponentAdapter<V, Self>
16 where
17 Self: Sized,
18 {
19 ComponentAdapter::new(self)
20 }
21
22 fn stylable(self) -> GeneralStylableComponentAdapter<Self>
23 where
24 Self: Sized,
25 {
26 GeneralStylableComponentAdapter::new(self)
27 }
28}
29
30pub struct GeneralStylableComponentAdapter<C: GeneralComponent> {
31 component: C,
32}
33
34impl<C: GeneralComponent> GeneralStylableComponentAdapter<C> {
35 pub fn new(component: C) -> Self {
36 Self { component }
37 }
38}
39
40impl<C: GeneralComponent> GeneralStyleableComponent for GeneralStylableComponentAdapter<C> {
41 type Style = ();
42
43 type Output = C;
44
45 fn with_style(self, _: Self::Style) -> Self::Output {
46 self.component
47 }
48}
49
50pub trait GeneralStyleableComponent {
51 type Style: Clone;
52 type Output: GeneralComponent;
53
54 fn with_style(self, style: Self::Style) -> Self::Output;
55}
56
57impl GeneralComponent for () {
58 fn render<V: View>(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
59 Empty::new().into_any()
60 }
61}
62
63impl GeneralStyleableComponent for () {
64 type Style = ();
65 type Output = ();
66
67 fn with_style(self, _: Self::Style) -> Self::Output {
68 ()
69 }
70}
71
72pub trait StyleableComponent<V: View> {
73 type Style: Clone;
74 type Output: Component<V>;
75
76 fn c_with_style(self, style: Self::Style) -> Self::Output;
77}
78
79impl<V: View, C: GeneralStyleableComponent> StyleableComponent<V> for C {
80 type Style = C::Style;
81
82 type Output = C::Output;
83
84 fn c_with_style(self, style: Self::Style) -> Self::Output {
85 self.with_style(style)
86 }
87}
88
89pub trait Component<V: View> {
90 fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
91
92 fn c_element(self) -> ComponentAdapter<V, Self>
93 where
94 Self: Sized,
95 {
96 ComponentAdapter::new(self)
97 }
98
99 fn c_styleable(self) -> StylableComponentAdapter<Self, V>
100 where
101 Self: Sized,
102 {
103 StylableComponentAdapter::new(self)
104 }
105}
106
107impl<V: View, C: GeneralComponent> Component<V> for C {
108 fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
109 self.render(v, cx)
110 }
111}
112
113// StylableComponent -> Component
114pub struct StylableComponentAdapter<C: Component<V>, V: View> {
115 component: C,
116 phantom: std::marker::PhantomData<V>,
117}
118
119impl<C: Component<V>, V: View> StylableComponentAdapter<C, V> {
120 pub fn new(component: C) -> Self {
121 Self {
122 component,
123 phantom: std::marker::PhantomData,
124 }
125 }
126}
127
128impl<C: Component<V>, V: View> StyleableComponent<V> for StylableComponentAdapter<C, V> {
129 type Style = ();
130
131 type Output = C;
132
133 fn c_with_style(self, _: Self::Style) -> Self::Output {
134 self.component
135 }
136}
137
138// Element -> GeneralComponent
139
140pub struct DynamicElementAdapter {
141 element: Box<dyn Any>,
142}
143
144impl DynamicElementAdapter {
145 pub fn new<V: View>(element: AnyElement<V>) -> Self {
146 DynamicElementAdapter {
147 element: Box::new(element) as Box<dyn Any>,
148 }
149 }
150}
151
152impl GeneralComponent for DynamicElementAdapter {
153 fn render<V: View>(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
154 let element = self
155 .element
156 .downcast::<AnyElement<V>>()
157 .expect("Don't move elements out of their view :(");
158 *element
159 }
160}
161
162// Element -> Component
163pub struct ElementAdapter<V: View> {
164 element: AnyElement<V>,
165 _phantom: std::marker::PhantomData<V>,
166}
167
168impl<V: View> ElementAdapter<V> {
169 pub fn new(element: AnyElement<V>) -> Self {
170 Self {
171 element,
172 _phantom: std::marker::PhantomData,
173 }
174 }
175}
176
177impl<V: View> Component<V> for ElementAdapter<V> {
178 fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
179 self.element
180 }
181}
182
183// Component -> Element
184pub struct ComponentAdapter<V: View, E> {
185 component: Option<E>,
186 element: Option<AnyElement<V>>,
187 phantom: PhantomData<V>,
188}
189
190impl<E, V: View> ComponentAdapter<V, E> {
191 pub fn new(e: E) -> Self {
192 Self {
193 component: Some(e),
194 element: None,
195 phantom: PhantomData,
196 }
197 }
198}
199
200impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
201 type LayoutState = ();
202
203 type PaintState = ();
204
205 fn layout(
206 &mut self,
207 constraint: SizeConstraint,
208 view: &mut V,
209 cx: &mut LayoutContext<V>,
210 ) -> (Vector2F, Self::LayoutState) {
211 if self.element.is_none() {
212 let element = self
213 .component
214 .take()
215 .expect("Component can only be rendered once")
216 .render(view, cx.view_context());
217 self.element = Some(element);
218 }
219 let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
220 (constraint, ())
221 }
222
223 fn paint(
224 &mut self,
225 scene: &mut SceneBuilder,
226 bounds: RectF,
227 visible_bounds: RectF,
228 _: &mut Self::LayoutState,
229 view: &mut V,
230 cx: &mut PaintContext<V>,
231 ) -> Self::PaintState {
232 self.element
233 .as_mut()
234 .expect("Layout should always be called before paint")
235 .paint(scene, bounds.origin(), visible_bounds, view, cx)
236 }
237
238 fn rect_for_text_range(
239 &self,
240 range_utf16: std::ops::Range<usize>,
241 _: RectF,
242 _: RectF,
243 _: &Self::LayoutState,
244 _: &Self::PaintState,
245 view: &V,
246 cx: &ViewContext<V>,
247 ) -> Option<RectF> {
248 self.element
249 .as_ref()
250 .and_then(|el| el.rect_for_text_range(range_utf16, view, cx))
251 }
252
253 fn debug(
254 &self,
255 _: RectF,
256 _: &Self::LayoutState,
257 _: &Self::PaintState,
258 view: &V,
259 cx: &ViewContext<V>,
260 ) -> serde_json::Value {
261 serde_json::json!({
262 "type": "ComponentAdapter",
263 "child": self.element.as_ref().map(|el| el.debug(view, cx)),
264 })
265 }
266}