1use std::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 fn element<V: View>(self) -> ComponentAdapter<V, Self>
15 where
16 Self: Sized,
17 {
18 ComponentAdapter::new(self)
19 }
20}
21
22pub trait StyleableComponent {
23 type Style: Clone;
24 type Output: GeneralComponent;
25
26 fn with_style(self, style: Self::Style) -> Self::Output;
27}
28
29impl GeneralComponent for () {
30 fn render<V: View>(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
31 Empty::new().into_any()
32 }
33}
34
35impl StyleableComponent for () {
36 type Style = ();
37 type Output = ();
38
39 fn with_style(self, _: Self::Style) -> Self::Output {
40 ()
41 }
42}
43
44pub trait Component<V: View> {
45 fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
46
47 fn element(self) -> ComponentAdapter<V, Self>
48 where
49 Self: Sized,
50 {
51 ComponentAdapter::new(self)
52 }
53}
54
55impl<V: View, C: GeneralComponent> Component<V> for C {
56 fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
57 self.render(v, cx)
58 }
59}
60
61// StylableComponent -> GeneralComponent
62pub struct StylableComponentAdapter<C: Component<V>, V: View> {
63 component: C,
64 phantom: std::marker::PhantomData<V>,
65}
66
67impl<C: Component<V>, V: View> StylableComponentAdapter<C, V> {
68 pub fn new(component: C) -> Self {
69 Self {
70 component,
71 phantom: std::marker::PhantomData,
72 }
73 }
74}
75
76impl<C: GeneralComponent, V: View> StyleableComponent for StylableComponentAdapter<C, V> {
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// Element -> Component
87pub struct ElementAdapter<V: View> {
88 element: AnyElement<V>,
89 _phantom: std::marker::PhantomData<V>,
90}
91
92impl<V: View> ElementAdapter<V> {
93 pub fn new(element: AnyElement<V>) -> Self {
94 Self {
95 element,
96 _phantom: std::marker::PhantomData,
97 }
98 }
99}
100
101impl<V: View> Component<V> for ElementAdapter<V> {
102 fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
103 self.element
104 }
105}
106
107// Component -> Element
108pub struct ComponentAdapter<V: View, E> {
109 component: Option<E>,
110 element: Option<AnyElement<V>>,
111 phantom: PhantomData<V>,
112}
113
114impl<E, V: View> ComponentAdapter<V, E> {
115 pub fn new(e: E) -> Self {
116 Self {
117 component: Some(e),
118 element: None,
119 phantom: PhantomData,
120 }
121 }
122}
123
124impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
125 type LayoutState = ();
126
127 type PaintState = ();
128
129 fn layout(
130 &mut self,
131 constraint: SizeConstraint,
132 view: &mut V,
133 cx: &mut LayoutContext<V>,
134 ) -> (Vector2F, Self::LayoutState) {
135 if self.element.is_none() {
136 let element = self
137 .component
138 .take()
139 .expect("Component can only be rendered once")
140 .render(view, cx.view_context());
141 self.element = Some(element);
142 }
143 let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
144 (constraint, ())
145 }
146
147 fn paint(
148 &mut self,
149 scene: &mut SceneBuilder,
150 bounds: RectF,
151 visible_bounds: RectF,
152 _: &mut Self::LayoutState,
153 view: &mut V,
154 cx: &mut PaintContext<V>,
155 ) -> Self::PaintState {
156 self.element
157 .as_mut()
158 .expect("Layout should always be called before paint")
159 .paint(scene, bounds.origin(), visible_bounds, view, cx)
160 }
161
162 fn rect_for_text_range(
163 &self,
164 range_utf16: std::ops::Range<usize>,
165 _: RectF,
166 _: RectF,
167 _: &Self::LayoutState,
168 _: &Self::PaintState,
169 view: &V,
170 cx: &ViewContext<V>,
171 ) -> Option<RectF> {
172 self.element
173 .as_ref()
174 .and_then(|el| el.rect_for_text_range(range_utf16, view, cx))
175 }
176
177 fn debug(
178 &self,
179 _: RectF,
180 _: &Self::LayoutState,
181 _: &Self::PaintState,
182 view: &V,
183 cx: &ViewContext<V>,
184 ) -> serde_json::Value {
185 serde_json::json!({
186 "type": "ComponentAdapter",
187 "child": self.element.as_ref().map(|el| el.debug(view, cx)),
188 })
189 }
190}