use std::{any::Any, marker::PhantomData}; use pathfinder_geometry::{rect::RectF, vector::Vector2F}; use crate::{ AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext, }; use super::Empty; pub trait GeneralComponent { fn render(self, v: &mut V, cx: &mut ViewContext) -> AnyElement; fn element(self) -> ComponentAdapter where Self: Sized, { ComponentAdapter::new(self) } fn stylable(self) -> GeneralStylableComponentAdapter where Self: Sized, { GeneralStylableComponentAdapter::new(self) } } pub struct GeneralStylableComponentAdapter { component: C, } impl GeneralStylableComponentAdapter { pub fn new(component: C) -> Self { Self { component } } } impl GeneralStyleableComponent for GeneralStylableComponentAdapter { type Style = (); type Output = C; fn with_style(self, _: Self::Style) -> Self::Output { self.component } } pub trait GeneralStyleableComponent { type Style: Clone; type Output: GeneralComponent; fn with_style(self, style: Self::Style) -> Self::Output; } impl GeneralComponent for () { fn render(self, _: &mut V, _: &mut ViewContext) -> AnyElement { Empty::new().into_any() } } impl GeneralStyleableComponent for () { type Style = (); type Output = (); fn with_style(self, _: Self::Style) -> Self::Output { () } } pub trait StyleableComponent { type Style: Clone; type Output: Component; fn c_with_style(self, style: Self::Style) -> Self::Output; } impl StyleableComponent for C { type Style = C::Style; type Output = C::Output; fn c_with_style(self, style: Self::Style) -> Self::Output { self.with_style(style) } } pub trait Component { fn render(self, v: &mut V, cx: &mut ViewContext) -> AnyElement; fn c_element(self) -> ComponentAdapter where Self: Sized, { ComponentAdapter::new(self) } fn c_styleable(self) -> StylableComponentAdapter where Self: Sized, { StylableComponentAdapter::new(self) } } impl Component for C { fn render(self, v: &mut V, cx: &mut ViewContext) -> AnyElement { self.render(v, cx) } } // StylableComponent -> Component pub struct StylableComponentAdapter, V: View> { component: C, phantom: std::marker::PhantomData, } impl, V: View> StylableComponentAdapter { pub fn new(component: C) -> Self { Self { component, phantom: std::marker::PhantomData, } } } impl, V: View> StyleableComponent for StylableComponentAdapter { type Style = (); type Output = C; fn c_with_style(self, _: Self::Style) -> Self::Output { self.component } } // Element -> GeneralComponent pub struct DynamicElementAdapter { element: Box, } impl DynamicElementAdapter { pub fn new(element: AnyElement) -> Self { DynamicElementAdapter { element: Box::new(element) as Box, } } } impl GeneralComponent for DynamicElementAdapter { fn render(self, _: &mut V, _: &mut ViewContext) -> AnyElement { let element = self .element .downcast::>() .expect("Don't move elements out of their view :("); *element } } // Element -> Component pub struct ElementAdapter { element: AnyElement, _phantom: std::marker::PhantomData, } impl ElementAdapter { pub fn new(element: AnyElement) -> Self { Self { element, _phantom: std::marker::PhantomData, } } } impl Component for ElementAdapter { fn render(self, _: &mut V, _: &mut ViewContext) -> AnyElement { self.element } } // Component -> Element pub struct ComponentAdapter { component: Option, element: Option>, phantom: PhantomData, } impl ComponentAdapter { pub fn new(e: E) -> Self { Self { component: Some(e), element: None, phantom: PhantomData, } } } impl + 'static> Element for ComponentAdapter { type LayoutState = (); type PaintState = (); fn layout( &mut self, constraint: SizeConstraint, view: &mut V, cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { if self.element.is_none() { let element = self .component .take() .expect("Component can only be rendered once") .render(view, cx.view_context()); self.element = Some(element); } let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx); (constraint, ()) } fn paint( &mut self, scene: &mut SceneBuilder, bounds: RectF, visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, cx: &mut PaintContext, ) -> Self::PaintState { self.element .as_mut() .expect("Layout should always be called before paint") .paint(scene, bounds.origin(), visible_bounds, view, cx) } fn rect_for_text_range( &self, range_utf16: std::ops::Range, _: RectF, _: RectF, _: &Self::LayoutState, _: &Self::PaintState, view: &V, cx: &ViewContext, ) -> Option { self.element .as_ref() .and_then(|el| el.rect_for_text_range(range_utf16, view, cx)) } fn debug( &self, _: RectF, _: &Self::LayoutState, _: &Self::PaintState, view: &V, cx: &ViewContext, ) -> serde_json::Value { serde_json::json!({ "type": "ComponentAdapter", "child": self.element.as_ref().map(|el| el.debug(view, cx)), }) } }