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 fn styleable(self) -> StylableComponentAdapter<Self, V>
55 where
56 Self: Sized,
57 {
58 StylableComponentAdapter::new(self)
59 }
60}
61
62impl<V: View, C: GeneralComponent> Component<V> for C {
63 fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
64 self.render(v, cx)
65 }
66}
67
68// StylableComponent -> GeneralComponent
69pub struct StylableComponentAdapter<C: Component<V>, V: View> {
70 component: C,
71 phantom: std::marker::PhantomData<V>,
72}
73
74impl<C: Component<V>, V: View> StylableComponentAdapter<C, V> {
75 pub fn new(component: C) -> Self {
76 Self {
77 component,
78 phantom: std::marker::PhantomData,
79 }
80 }
81}
82
83impl<C: GeneralComponent, V: View> StyleableComponent for StylableComponentAdapter<C, V> {
84 type Style = ();
85
86 type Output = C;
87
88 fn with_style(self, _: Self::Style) -> Self::Output {
89 self.component
90 }
91}
92
93// Element -> Component
94pub struct ElementAdapter<V: View> {
95 element: AnyElement<V>,
96 _phantom: std::marker::PhantomData<V>,
97}
98
99impl<V: View> ElementAdapter<V> {
100 pub fn new(element: AnyElement<V>) -> Self {
101 Self {
102 element,
103 _phantom: std::marker::PhantomData,
104 }
105 }
106}
107
108impl<V: View> Component<V> for ElementAdapter<V> {
109 fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
110 self.element
111 }
112}
113
114// Component -> Element
115pub struct ComponentAdapter<V: View, E> {
116 component: Option<E>,
117 element: Option<AnyElement<V>>,
118 phantom: PhantomData<V>,
119}
120
121impl<E, V: View> ComponentAdapter<V, E> {
122 pub fn new(e: E) -> Self {
123 Self {
124 component: Some(e),
125 element: None,
126 phantom: PhantomData,
127 }
128 }
129}
130
131impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
132 type LayoutState = ();
133
134 type PaintState = ();
135
136 fn layout(
137 &mut self,
138 constraint: SizeConstraint,
139 view: &mut V,
140 cx: &mut LayoutContext<V>,
141 ) -> (Vector2F, Self::LayoutState) {
142 if self.element.is_none() {
143 let element = self
144 .component
145 .take()
146 .expect("Component can only be rendered once")
147 .render(view, cx.view_context());
148 self.element = Some(element);
149 }
150 let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
151 (constraint, ())
152 }
153
154 fn paint(
155 &mut self,
156 scene: &mut SceneBuilder,
157 bounds: RectF,
158 visible_bounds: RectF,
159 _: &mut Self::LayoutState,
160 view: &mut V,
161 cx: &mut PaintContext<V>,
162 ) -> Self::PaintState {
163 self.element
164 .as_mut()
165 .expect("Layout should always be called before paint")
166 .paint(scene, bounds.origin(), visible_bounds, view, cx)
167 }
168
169 fn rect_for_text_range(
170 &self,
171 range_utf16: std::ops::Range<usize>,
172 _: RectF,
173 _: RectF,
174 _: &Self::LayoutState,
175 _: &Self::PaintState,
176 view: &V,
177 cx: &ViewContext<V>,
178 ) -> Option<RectF> {
179 self.element
180 .as_ref()
181 .and_then(|el| el.rect_for_text_range(range_utf16, view, cx))
182 }
183
184 fn debug(
185 &self,
186 _: RectF,
187 _: &Self::LayoutState,
188 _: &Self::PaintState,
189 view: &V,
190 cx: &ViewContext<V>,
191 ) -> serde_json::Value {
192 serde_json::json!({
193 "type": "ComponentAdapter",
194 "child": self.element.as_ref().map(|el| el.debug(view, cx)),
195 })
196 }
197}