use std::any::Any;
use std::rc::Rc;
use day_reactive::Scope;
use day_spec::{Event, PieceKind};
use crate::layout::Layout;
use crate::tree::{Flex, RNode, with_tree};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Boundary {
No,
Yes,
}
pub struct BuildCx {
parent: RNode,
}
impl BuildCx {
pub fn new(parent: RNode) -> Self {
BuildCx { parent }
}
pub fn parent(&self) -> RNode {
self.parent
}
pub fn leaf(&mut self, kind: PieceKind, props: &dyn Any, flex: Flex) -> RNode {
let n = with_tree(|t| {
t.create_node(
kind,
props,
Rc::new(crate::layout::LeafLayout),
flex,
true,
false,
Scope::current(),
)
});
with_tree(|t| t.attach(self.parent, n));
n
}
pub fn native(
&mut self,
kind: PieceKind,
props: &dyn Any,
layout: Rc<dyn Layout>,
flex: Flex,
boundary: Boundary,
) -> RNode {
let n = with_tree(|t| {
t.create_node(
kind,
props,
layout,
flex,
true,
boundary == Boundary::Yes,
Scope::current(),
)
});
with_tree(|t| t.attach(self.parent, n));
n
}
pub fn layout_only(&mut self, layout: Rc<dyn Layout>, flex: Flex, boundary: Boundary) -> RNode {
let n = with_tree(|t| {
t.create_node(
"day.layout",
&(),
layout,
flex,
false,
boundary == Boundary::Yes,
Scope::current(),
)
});
with_tree(|t| t.attach(self.parent, n));
n
}
pub fn under<R>(&mut self, node: RNode, f: impl FnOnce(&mut BuildCx) -> R) -> R {
let mut cx = BuildCx { parent: node };
f(&mut cx)
}
pub fn on(&mut self, node: RNode, h: impl Fn(&Event) + 'static) {
let scope = Scope::current();
let wrapped: Rc<dyn Fn(&Event)> = Rc::new(move |ev| {
if scope.is_alive() {
let ev = ev.clone();
scope.enter(|| h(&ev));
}
});
with_tree(|t| t.on_event(node, wrapped));
}
}
pub trait Piece: 'static {
fn build(self, cx: &mut BuildCx) -> RNode;
}
pub struct AnyPiece(Box<dyn FnOnce(&mut BuildCx) -> RNode>);
impl AnyPiece {
pub fn new<P: Piece>(p: P) -> Self {
AnyPiece(Box::new(move |cx| p.build(cx)))
}
pub fn any(self) -> AnyPiece {
self
}
}
impl Piece for AnyPiece {
fn build(self, cx: &mut BuildCx) -> RNode {
(self.0)(cx)
}
}
pub struct PieceFn<F>(F);
impl<F: FnOnce(&mut BuildCx) -> RNode + 'static> Piece for PieceFn<F> {
fn build(self, cx: &mut BuildCx) -> RNode {
(self.0)(cx)
}
}
pub fn piece_fn<F: FnOnce(&mut BuildCx) -> RNode + 'static>(f: F) -> PieceFn<F> {
PieceFn(f)
}
pub enum Either<A, B> {
Left(A),
Right(B),
}
impl<A: Piece, B: Piece> Piece for Either<A, B> {
fn build(self, cx: &mut BuildCx) -> RNode {
match self {
Either::Left(a) => a.build(cx),
Either::Right(b) => b.build(cx),
}
}
}
impl<P: Piece> Piece for Option<P> {
fn build(self, cx: &mut BuildCx) -> RNode {
match self {
Some(p) => p.build(cx),
None => cx.layout_only(
Rc::new(crate::layout::PassThrough),
Flex {
is_group: true,
..Default::default()
},
Boundary::No,
),
}
}
}
#[cfg(test)]
mod any_piece_tests {
use super::*;
use slotmap::Key as _;
fn box_addr(p: &AnyPiece) -> *const () {
(&*p.0 as *const dyn FnOnce(&mut BuildCx) -> RNode).cast::<()>()
}
#[test]
fn any_on_an_erased_piece_reuses_its_box() {
let p = AnyPiece::new(piece_fn(|_| RNode::null()));
let before = box_addr(&p);
let p = p.any();
assert_eq!(before, box_addr(&p));
}
}
pub trait PieceSeq: 'static {
fn build_each(self, cx: &mut BuildCx);
}
impl PieceSeq for () {
fn build_each(self, _cx: &mut BuildCx) {}
}
impl<P: Piece> PieceSeq for Vec<P> {
fn build_each(self, cx: &mut BuildCx) {
for p in self {
let _ = p.build(cx);
}
}
}
impl<P: Piece, const N: usize> PieceSeq for [P; N] {
fn build_each(self, cx: &mut BuildCx) {
for p in self {
let _ = p.build(cx);
}
}
}
impl<P: Piece> PieceSeq for Option<P> {
fn build_each(self, cx: &mut BuildCx) {
if let Some(p) = self {
let _ = p.build(cx);
}
}
}
macro_rules! impl_piece_seq {
($($name:ident),+) => {
impl<$($name: Piece),+> PieceSeq for ($($name,)+) {
#[allow(non_snake_case)]
fn build_each(self, cx: &mut BuildCx) {
let ($($name,)+) = self;
$(let _ = $name.build(cx);)+
}
}
};
}
impl_piece_seq!(A);
impl_piece_seq!(A, B);
impl_piece_seq!(A, B, C);
impl_piece_seq!(A, B, C, D);
impl_piece_seq!(A, B, C, D, E);
impl_piece_seq!(A, B, C, D, E, F);
impl_piece_seq!(A, B, C, D, E, F, G);
impl_piece_seq!(A, B, C, D, E, F, G, H);
impl_piece_seq!(A, B, C, D, E, F, G, H, I);
impl_piece_seq!(A, B, C, D, E, F, G, H, I, J);
impl_piece_seq!(A, B, C, D, E, F, G, H, I, J, K);
impl_piece_seq!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_piece_seq!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_piece_seq!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_piece_seq!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_piece_seq!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, Q);