use crate::context::{LayoutCtx, PaintCtx, PrepaintCtx};
use crate::types::{AccessibilityInfo, Bounds, ElementId, LayoutId};
pub trait Element: 'static {
type LayoutState: 'static;
type PaintState: 'static;
fn request_layout(&mut self, cx: &mut LayoutCtx) -> (LayoutId, Self::LayoutState);
fn prepaint(
&mut self,
bounds: Bounds,
layout_state: &mut Self::LayoutState,
cx: &mut PrepaintCtx,
) -> Self::PaintState;
fn paint(
&mut self,
bounds: Bounds,
layout_state: &mut Self::LayoutState,
paint_state: &mut Self::PaintState,
cx: &mut PaintCtx,
);
fn accessibility(&self) -> Option<AccessibilityInfo> {
None
}
fn id(&self) -> Option<ElementId> {
None
}
fn paint_input_hash(&self, _bounds: Bounds) -> u64 {
0
}
}
mod sealed {
pub trait Sealed {}
}
pub trait IntoElement: sealed::Sealed {
type Element: Element;
fn into_element(self) -> Self::Element;
}
pub struct AnyElement {
element: Box<dyn AnyElementDynamic>,
}
impl AnyElement {
pub fn new<E: IntoElement>(element: E) -> Self {
let element = element.into_element();
Self {
element: Box::new(ElementState {
element,
layout_state: None,
paint_state: None,
}),
}
}
pub fn request_layout(&mut self, cx: &mut LayoutCtx) -> LayoutId {
self.element.request_layout(cx)
}
pub fn prepaint(&mut self, bounds: Bounds, cx: &mut PrepaintCtx) {
self.element.prepaint(bounds, cx);
}
pub fn paint(&mut self, bounds: Bounds, cx: &mut PaintCtx) {
self.element.paint(bounds, cx);
}
pub fn accessibility(&self) -> Option<AccessibilityInfo> {
self.element.accessibility()
}
pub fn id(&self) -> Option<ElementId> {
self.element.id()
}
pub fn paint_input_hash(&self, bounds: Bounds) -> u64 {
self.element.paint_input_hash(bounds)
}
}
trait AnyElementDynamic: 'static {
fn request_layout(&mut self, cx: &mut LayoutCtx) -> LayoutId;
fn prepaint(&mut self, bounds: Bounds, cx: &mut PrepaintCtx);
fn paint(&mut self, bounds: Bounds, cx: &mut PaintCtx);
fn accessibility(&self) -> Option<AccessibilityInfo>;
fn id(&self) -> Option<ElementId>;
fn paint_input_hash(&self, bounds: Bounds) -> u64;
}
struct ElementState<E: Element> {
element: E,
layout_state: Option<E::LayoutState>,
paint_state: Option<E::PaintState>,
}
impl<E: Element> AnyElementDynamic for ElementState<E> {
fn request_layout(&mut self, cx: &mut LayoutCtx) -> LayoutId {
let (layout_id, layout_state) = self.element.request_layout(cx);
self.layout_state = Some(layout_state);
layout_id
}
fn prepaint(&mut self, bounds: Bounds, cx: &mut PrepaintCtx) {
let layout_state = self
.layout_state
.as_mut()
.expect("prepaint called before request_layout");
let paint_state = self.element.prepaint(bounds, layout_state, cx);
self.paint_state = Some(paint_state);
}
fn paint(&mut self, bounds: Bounds, cx: &mut PaintCtx) {
let layout_state = self
.layout_state
.as_mut()
.expect("paint called before request_layout");
let paint_state = self
.paint_state
.as_mut()
.expect("paint called before prepaint");
self.element.paint(bounds, layout_state, paint_state, cx);
}
fn accessibility(&self) -> Option<AccessibilityInfo> {
self.element.accessibility()
}
fn id(&self) -> Option<ElementId> {
self.element.id()
}
fn paint_input_hash(&self, bounds: Bounds) -> u64 {
self.element.paint_input_hash(bounds)
}
}
pub(crate) use sealed::Sealed;
#[cfg(test)]
mod tests {
use super::*;
struct TestElement;
impl sealed::Sealed for TestElement {}
impl Element for TestElement {
type LayoutState = ();
type PaintState = ();
fn request_layout(&mut self, cx: &mut LayoutCtx) -> (LayoutId, Self::LayoutState) {
let node = cx
.taffy
.new_leaf(taffy::Style::default())
.expect("failed to create node");
(LayoutId(node), ())
}
fn prepaint(
&mut self,
_bounds: Bounds,
_layout_state: &mut Self::LayoutState,
_cx: &mut PrepaintCtx,
) -> Self::PaintState {
}
fn paint(
&mut self,
_bounds: Bounds,
_layout_state: &mut Self::LayoutState,
_paint_state: &mut Self::PaintState,
_cx: &mut PaintCtx,
) {
}
}
impl IntoElement for TestElement {
type Element = Self;
fn into_element(self) -> Self {
self
}
}
#[test]
fn any_element_creation() {
let _any = AnyElement::new(TestElement);
}
}