1use pathfinder_geometry::{rect::RectF, vector::Vector2F};
2
3use crate::{
4 AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
5 ViewContext,
6};
7
8use super::Empty;
9
10pub trait GeneralComponent {
11 fn render<V: View>(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
12 fn element<V: View>(self) -> ComponentAdapter<V, Self>
13 where
14 Self: Sized,
15 {
16 ComponentAdapter::new(self)
17 }
18}
19
20pub trait StyleableComponent {
21 type Style: Clone;
22 type Output: GeneralComponent;
23
24 fn with_style(self, style: Self::Style) -> Self::Output;
25}
26
27impl GeneralComponent for () {
28 fn render<V: View>(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
29 Empty::new().into_any()
30 }
31}
32
33impl StyleableComponent for () {
34 type Style = ();
35 type Output = ();
36
37 fn with_style(self, _: Self::Style) -> Self::Output {
38 ()
39 }
40}
41
42pub trait Component<V: View> {
43 fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
44
45 fn into_element(self) -> ComponentAdapter<V, Self>
46 where
47 Self: Sized,
48 {
49 ComponentAdapter::new(self)
50 }
51}
52
53impl<V: View, C: GeneralComponent> Component<V> for C {
54 fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
55 self.render(v, cx)
56 }
57}
58
59// StylableComponent -> GeneralComponent
60pub struct StylableComponentAdapter<C: Component<V>, V: View> {
61 component: C,
62 phantom: std::marker::PhantomData<V>,
63}
64
65impl<C: Component<V>, V: View> StylableComponentAdapter<C, V> {
66 pub fn new(component: C) -> Self {
67 Self {
68 component,
69 phantom: std::marker::PhantomData,
70 }
71 }
72}
73
74impl<C: GeneralComponent, V: View> StyleableComponent for StylableComponentAdapter<C, V> {
75 type Style = ();
76
77 type Output = C;
78
79 fn with_style(self, _: Self::Style) -> Self::Output {
80 self.component
81 }
82}
83
84// Element -> Component
85pub struct ElementAdapter<V: View> {
86 element: AnyElement<V>,
87 _phantom: std::marker::PhantomData<V>,
88}
89
90impl<V: View> ElementAdapter<V> {
91 pub fn new(element: AnyElement<V>) -> Self {
92 Self {
93 element,
94 _phantom: std::marker::PhantomData,
95 }
96 }
97}
98
99impl<V: View> Component<V> for ElementAdapter<V> {
100 fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
101 self.element
102 }
103}
104
105// Component -> Element
106pub struct ComponentAdapter<V: View, E> {
107 component: Option<E>,
108 element: Option<AnyElement<V>>,
109 #[cfg(debug_assertions)]
110 _component_name: &'static str,
111}
112
113impl<E, V: View> ComponentAdapter<V, E> {
114 pub fn new(e: E) -> Self {
115 Self {
116 component: Some(e),
117 element: None,
118 #[cfg(debug_assertions)]
119 _component_name: std::any::type_name::<E>(),
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 component = self.component.take().unwrap();
137 self.element = Some(component.render(view, cx.view_context()));
138 }
139 let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
140 (constraint, ())
141 }
142
143 fn paint(
144 &mut self,
145 scene: &mut SceneBuilder,
146 bounds: RectF,
147 visible_bounds: RectF,
148 _: &mut Self::LayoutState,
149 view: &mut V,
150 cx: &mut PaintContext<V>,
151 ) -> Self::PaintState {
152 self.element
153 .as_mut()
154 .unwrap()
155 .paint(scene, bounds.origin(), visible_bounds, view, cx)
156 }
157
158 fn rect_for_text_range(
159 &self,
160 range_utf16: std::ops::Range<usize>,
161 _: RectF,
162 _: RectF,
163 _: &Self::LayoutState,
164 _: &Self::PaintState,
165 view: &V,
166 cx: &ViewContext<V>,
167 ) -> Option<RectF> {
168 self.element
169 .as_ref()
170 .unwrap()
171 .rect_for_text_range(range_utf16, view, cx)
172 }
173
174 fn debug(
175 &self,
176 _: RectF,
177 _: &Self::LayoutState,
178 _: &Self::PaintState,
179 view: &V,
180 cx: &ViewContext<V>,
181 ) -> serde_json::Value {
182 #[cfg(debug_assertions)]
183 let component_name = self._component_name;
184
185 #[cfg(not(debug_assertions))]
186 let component_name = "Unknown";
187
188 serde_json::json!({
189 "type": "ComponentAdapter",
190 "child": self.element.as_ref().unwrap().debug(view, cx),
191 "component_name": component_name
192 })
193 }
194}