use std::cell::RefCell;
#[doc(hidden)]
pub use std::{
any::{Any, TypeId},
marker::PhantomData,
ops::Deref,
};
use ribir_algo::Sc;
use smallvec::SmallVec;
use widget_id::RenderQueryable;
pub(crate) use crate::widget_tree::*;
use crate::{context::*, prelude::*, render_helper::PureRender};
pub trait Compose: Sized {
fn compose(this: impl StateWriter<Value = Self>) -> Widget<'static>;
}
pub struct HitTest {
pub hit: bool,
pub can_hit_child: bool,
}
pub trait Render: 'static {
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size;
fn paint(&self, ctx: &mut PaintingCtx);
fn only_sized_by_parent(&self) -> bool { false }
fn hit_test(&self, ctx: &HitTestCtx, pos: Point) -> HitTest {
let is_hit = hit_test_impl(ctx, pos);
HitTest { hit: is_hit, can_hit_child: is_hit }
}
fn get_transform(&self) -> Option<Transform> { None }
}
pub struct Widget<'w>(InnerWidget<'w>);
enum InnerWidget<'w> {
Node(Node<'w>),
Lazy(LazyNode<'w>),
}
enum Node<'w> {
Leaf(PureNode<'w>),
Tree { parent: PureNode<'w>, children: Vec<Widget<'w>> },
}
enum PureNode<'w> {
Render(Box<dyn RenderQueryable>),
LazyBuild(Box<dyn FnOnce(&mut BuildCtx) -> WidgetId + 'w>),
}
struct LazyNode<'w>(Box<dyn FnOnce(&mut BuildCtx) -> Widget<'static> + 'w>);
impl<'w> LazyNode<'w> {
fn new(f: impl FnOnce(&mut BuildCtx) -> Widget<'w> + 'w) -> Self {
let f: Box<dyn FnOnce(&mut BuildCtx) -> Widget<'w> + 'w> = Box::new(f);
let f: Box<dyn FnOnce(&mut BuildCtx) -> Widget<'static> + 'w> =
unsafe { std::mem::transmute(f) };
Self(f)
}
fn consume(self, ctx: &mut BuildCtx) -> Widget<'w> { (self.0)(ctx) }
}
#[derive(Clone)]
pub struct GenWidget(InnerGenWidget);
type InnerGenWidget = Sc<RefCell<Box<dyn FnMut(&mut BuildCtx) -> Widget<'static>>>>;
pub type FnWidget<'w> = Box<dyn FnOnce(&mut BuildCtx) -> Widget<'w> + 'w>;
pub const COMPOSE: usize = 1;
pub const RENDER: usize = 2;
pub const FN: usize = 3;
pub trait IntoWidget<'w, const M: usize>: 'w {
fn into_widget(self) -> Widget<'w>;
}
pub(crate) trait IntoWidgetStrict<'w, const M: usize>: 'w {
fn into_widget_strict(self) -> Widget<'w>;
}
impl GenWidget {
pub fn new(f: impl FnMut(&mut BuildCtx) -> Widget<'static> + 'static) -> Self {
Self(Sc::new(RefCell::new(Box::new(f))))
}
pub fn gen_widget(&self) -> Widget<'static> {
let f = self.0.clone();
fn_widget! { f.borrow_mut()(ctx!()) }.into_widget()
}
}
impl<'w> IntoWidget<'w, FN> for Widget<'w> {
#[inline(always)]
fn into_widget(self) -> Widget<'w> { self }
}
impl<'w, const M: usize, T: IntoWidgetStrict<'w, M>> IntoWidget<'w, M> for T {
#[inline(always)]
fn into_widget(self) -> Widget<'w> { self.into_widget_strict() }
}
impl<C: Compose + 'static> IntoWidgetStrict<'static, COMPOSE> for C {
#[inline]
fn into_widget_strict(self) -> Widget<'static> {
Compose::compose(State::value(self)).into_widget()
}
}
impl<R: Render + 'static> IntoWidgetStrict<'static, RENDER> for R {
fn into_widget_strict(self) -> Widget<'static> {
let n = PureNode::Render(Box::new(PureRender(self)));
let node = Node::Leaf(n);
Widget(InnerWidget::Node(node))
}
}
impl<W: ComposeChild<'static, Child = Option<C>>, C> Compose for W {
fn compose(this: impl StateWriter<Value = Self>) -> Widget<'static> {
ComposeChild::compose_child(this, None)
}
}
impl<'w, F> IntoWidgetStrict<'w, FN> for F
where
F: FnOnce(&mut BuildCtx) -> Widget<'w> + 'w,
{
fn into_widget_strict(self) -> Widget<'w> {
let lazy = LazyNode::new(self);
Widget(InnerWidget::Lazy(lazy))
}
}
impl IntoWidgetStrict<'static, FN> for GenWidget {
#[inline]
fn into_widget_strict(self) -> Widget<'static> { self.gen_widget() }
}
impl<'w> Widget<'w> {
pub fn on_build(self, f: impl FnOnce(WidgetId, &mut BuildCtx) + 'w) -> Self {
self.wrap_root(move |n: PureNode<'_>| {
let lazy = move |ctx: &mut BuildCtx| {
let id = n.alloc(ctx);
f(id, ctx);
id
};
PureNode::LazyBuild(Box::new(lazy))
})
}
pub(crate) fn consume_root(self, ctx: &mut BuildCtx) -> Self {
let node = self.into_node(ctx).wrap_root(|n| {
let id = n.alloc(ctx);
PureNode::LazyBuild(Box::new(move |_| id))
});
Widget(InnerWidget::Node(node))
}
pub fn attach_anonymous_data(self, data: impl Any) -> Self {
self.on_build(|id, ctx| id.attach_anonymous_data(data, ctx.tree_mut()))
}
pub(crate) fn attach_data(self, data: Box<dyn Query>) -> Self {
self.on_build(|id, ctx| id.attach_data(data, ctx.tree_mut()))
}
pub(crate) fn try_unwrap_state_and_attach<D: Any>(
self, data: impl StateWriter<Value = D> + 'static,
) -> Self {
let data: Box<dyn Query> = match data.try_into_value() {
Ok(data) => Box::new(Queryable(data)),
Err(data) => Box::new(data),
};
self.attach_data(data)
}
pub(crate) fn build(self, ctx: &mut BuildCtx) -> WidgetId {
let mut subtrees = vec![];
let root = self.into_node(ctx).build(&mut subtrees, ctx);
while let Some((p, child)) = subtrees.pop() {
if ctx.providers.last() == Some(&p) && subtrees.last().map(|(p, _)| p) != Some(&p) {
ctx.providers.pop();
} else if ctx.providers.last() != Some(&p) && p.queryable(ctx.tree()) {
ctx.providers.push(p);
}
let c = child.into_node(ctx).build(&mut subtrees, ctx);
p.append(c, ctx.tree_mut());
}
root
}
pub(crate) fn directly_compose_children(
self, children: Vec<Widget<'w>>, ctx: &mut BuildCtx,
) -> Widget<'w> {
let mut list: SmallVec<[PureNode<'w>; 1]> = SmallVec::default();
let mut node = Some(self);
while let Some(n) = node.take() {
match n.into_node(ctx) {
Node::Leaf(r) => list.push(r),
Node::Tree { parent, mut children } => {
list.push(parent);
if let Some(p) = children.pop() {
node = Some(p)
}
assert!(children.is_empty(), "As a parent widget, it should have exactly one child.")
}
}
}
let mut node = Node::Tree { parent: list.pop().unwrap(), children };
while let Some(n) = list.pop() {
node = Node::Tree { parent: n, children: vec![node.into_widget()] };
}
node.into_widget()
}
fn into_node(self, ctx: &mut BuildCtx) -> Node<'w> {
let mut w = self;
loop {
match w.0 {
InnerWidget::Node(node) => break node,
InnerWidget::Lazy(l) => w = l.consume(ctx),
}
}
}
fn wrap_root(self, f: impl FnOnce(PureNode<'w>) -> PureNode<'w> + 'w) -> Self {
let lazy = move |ctx: &mut BuildCtx| {
let node = self.into_node(ctx).wrap_root(f);
Widget(InnerWidget::Node(node))
};
Widget(InnerWidget::Lazy(LazyNode::new(lazy)))
}
}
impl<'w> Node<'w> {
fn build(self, subtrees: &mut Vec<(WidgetId, Widget<'w>)>, ctx: &mut BuildCtx) -> WidgetId {
match self {
Node::Leaf(r) => r.alloc(ctx),
Node::Tree { parent, children } => {
let p = parent.alloc(ctx);
for c in children.into_iter().rev() {
subtrees.push((p, c))
}
p
}
}
}
fn into_widget(self) -> Widget<'w> { Widget(InnerWidget::Node(self)) }
fn wrap_root(self, f: impl FnOnce(PureNode<'w>) -> PureNode<'w>) -> Self {
match self {
Node::Leaf(n) => Node::Leaf(f(n)),
Node::Tree { parent, children } => Node::Tree { parent: f(parent), children },
}
}
}
impl<'w> PureNode<'w> {
fn alloc(self, ctx: &mut BuildCtx) -> WidgetId {
match self {
PureNode::Render(r) => ctx.alloc(r),
PureNode::LazyBuild(l) => l(ctx),
}
}
}
impl<F: FnMut(&mut BuildCtx) -> Widget<'static> + 'static> From<F> for GenWidget {
#[inline]
fn from(f: F) -> Self { Self::new(f) }
}
pub(crate) fn hit_test_impl(ctx: &HitTestCtx, pos: Point) -> bool {
ctx
.box_rect()
.map_or(false, |rect| rect.contains(pos))
}