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, E> {
109    component: Option<E>,
110    phantom: PhantomData<V>,
111}
112
113impl<E, V> ComponentAdapter<V, E> {
114    pub fn new(e: E) -> Self {
115        Self {
116            component: Some(e),
117            phantom: PhantomData,
118        }
119    }
120}
121
122impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
123    type LayoutState = AnyElement<V>;
124
125    type PaintState = ();
126
127    fn layout(
128        &mut self,
129        constraint: SizeConstraint,
130        view: &mut V,
131        cx: &mut LayoutContext<V>,
132    ) -> (Vector2F, Self::LayoutState) {
133        let component = self.component.take().unwrap();
134        let mut element = component.render(view, cx.view_context());
135        let constraint = element.layout(constraint, view, cx);
136        (constraint, element)
137    }
138
139    fn paint(
140        &mut self,
141        scene: &mut SceneBuilder,
142        bounds: RectF,
143        visible_bounds: RectF,
144        layout: &mut Self::LayoutState,
145        view: &mut V,
146        cx: &mut PaintContext<V>,
147    ) -> Self::PaintState {
148        layout.paint(scene, bounds.origin(), visible_bounds, view, cx)
149    }
150
151    fn rect_for_text_range(
152        &self,
153        range_utf16: std::ops::Range<usize>,
154        _: RectF,
155        _: RectF,
156        element: &Self::LayoutState,
157        _: &Self::PaintState,
158        view: &V,
159        cx: &ViewContext<V>,
160    ) -> Option<RectF> {
161        element.rect_for_text_range(range_utf16, view, cx)
162    }
163
164    fn debug(
165        &self,
166        _: RectF,
167        element: &Self::LayoutState,
168        _: &Self::PaintState,
169        view: &V,
170        cx: &ViewContext<V>,
171    ) -> serde_json::Value {
172        serde_json::json!({
173            "type": "ComponentAdapter",
174            "child": element.debug(view, cx),
175        })
176    }
177}