component.rs

  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}