use smallvec::SmallVec; use super::{Handle, Layout, LayoutId, Pixels, Point, Result, ViewContext, WindowContext}; use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc}; pub trait Element: 'static { type State; type FrameState; fn layout( &mut self, state: &mut Self::State, cx: &mut ViewContext, ) -> Result<(LayoutId, Self::FrameState)>; fn paint( &mut self, layout: Layout, state: &mut Self::State, frame_state: &mut Self::FrameState, cx: &mut ViewContext, ) -> Result<()>; } pub trait ParentElement { fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>; fn child(mut self, child: impl IntoAnyElement) -> Self where Self: Sized, { self.children_mut().push(child.into_any()); self } fn children(mut self, iter: impl IntoIterator>) -> Self where Self: Sized, { self.children_mut() .extend(iter.into_iter().map(|item| item.into_any())); self } } trait ElementObject { fn layout(&mut self, state: &mut S, cx: &mut ViewContext) -> Result; fn paint( &mut self, parent_origin: super::Point, state: &mut S, cx: &mut ViewContext, ) -> Result<()>; } struct RenderedElement { element: E, phase: ElementRenderPhase, } #[derive(Default)] enum ElementRenderPhase { #[default] Rendered, LayoutRequested { layout_id: LayoutId, frame_state: S, }, Painted { layout: Layout, frame_state: S, }, } /// Internal struct that wraps an element to store Layout and FrameState after the element is rendered. /// It's allocated as a trait object to erase the element type and wrapped in AnyElement for /// improved usability. impl RenderedElement { fn new(element: E) -> Self { RenderedElement { element, phase: ElementRenderPhase::Rendered, } } } impl ElementObject for RenderedElement { fn layout(&mut self, state: &mut E::State, cx: &mut ViewContext) -> Result { let (layout_id, frame_state) = self.element.layout(state, cx)?; self.phase = ElementRenderPhase::LayoutRequested { layout_id, frame_state, }; Ok(layout_id) } fn paint( &mut self, parent_origin: Point, state: &mut E::State, cx: &mut ViewContext, ) -> Result<()> { self.phase = match std::mem::take(&mut self.phase) { ElementRenderPhase::Rendered => panic!("must call layout before paint"), ElementRenderPhase::LayoutRequested { layout_id, mut frame_state, } => { let mut layout = cx.layout(layout_id)?; layout.bounds.origin += parent_origin; self.element .paint(layout.clone(), state, &mut frame_state, cx)?; ElementRenderPhase::Painted { layout, frame_state, } } ElementRenderPhase::Painted { layout, mut frame_state, } => { self.element .paint(layout.clone(), state, &mut frame_state, cx)?; ElementRenderPhase::Painted { layout, frame_state, } } }; Ok(()) } } pub struct AnyElement(Box>); impl AnyElement { pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext) -> Result { self.0.layout(state, cx) } pub fn paint( &mut self, parent_origin: Point, state: &mut S, cx: &mut ViewContext, ) -> Result<()> { self.0.paint(parent_origin, state, cx) } } pub trait IntoAnyElement { fn into_any(self) -> AnyElement; } impl IntoAnyElement for E { fn into_any(self) -> AnyElement { AnyElement(Box::new(RenderedElement::new(self))) } } impl IntoAnyElement for AnyElement { fn into_any(self) -> AnyElement { self } } #[derive(Clone)] pub struct View { state: Handle, render: Rc) -> AnyElement>, } pub fn view>( state: Handle, render: impl 'static + Fn(&mut S, &mut ViewContext) -> E, ) -> View { View { state, render: Rc::new(move |state, cx| render(state, cx).into_any()), } } impl View { pub fn into_any(self) -> AnyView { AnyView { view: Rc::new(RefCell::new(self)), parent_state_type: PhantomData, } } } impl Element for View { type State = (); type FrameState = AnyElement; fn layout( &mut self, _: &mut Self::State, cx: &mut ViewContext, ) -> Result<(LayoutId, Self::FrameState)> { self.state.update(cx, |state, cx| { let mut element = (self.render)(state, cx); let layout_id = element.layout(state, cx)?; Ok((layout_id, element)) }) } fn paint( &mut self, layout: Layout, _: &mut Self::State, element: &mut Self::FrameState, cx: &mut ViewContext, ) -> Result<()> { self.state.update(cx, |state, cx| { element.paint(layout.bounds.origin, state, cx) }) } } trait ViewObject { fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box)>; fn paint( &mut self, layout: Layout, element: &mut dyn Any, cx: &mut WindowContext, ) -> Result<()>; } impl ViewObject for View { fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box)> { self.state.update(cx, |state, cx| { let mut element = (self.render)(state, cx); let layout_id = element.layout(state, cx)?; let element = Box::new(element) as Box; Ok((layout_id, element)) }) } fn paint( &mut self, layout: Layout, element: &mut dyn Any, cx: &mut WindowContext, ) -> Result<()> { self.state.update(cx, |state, cx| { element .downcast_mut::>() .unwrap() .paint(layout.bounds.origin, state, cx) }) } } pub struct AnyView { view: Rc>, parent_state_type: PhantomData, } impl Element for AnyView { type State = S; type FrameState = Box; fn layout( &mut self, _: &mut Self::State, cx: &mut ViewContext, ) -> Result<(LayoutId, Self::FrameState)> { self.view.borrow_mut().layout(cx) } fn paint( &mut self, layout: Layout, _: &mut Self::State, element: &mut Self::FrameState, cx: &mut ViewContext, ) -> Result<()> { self.view.borrow_mut().paint(layout, element, cx) } } impl Clone for AnyView { fn clone(&self) -> Self { Self { view: self.view.clone(), parent_state_type: PhantomData, } } }