component.rs

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