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, _: &mut PaintingCtx) {}
fn only_sized_by_parent(&self) -> bool { false }
fn hit_test(&self, ctx: &HitTestCtx, pos: Point) -> HitTest { ctx.box_hit_test(pos) }
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(Box<dyn FnOnce() -> WidgetId + 'w>),
Tree { parent: Box<dyn FnOnce() -> WidgetId + 'w>, children: Vec<Widget<'w>> },
}
struct LazyNode<'w>(Box<dyn FnOnce() -> Widget<'static> + 'w>);
impl<'w> LazyNode<'w> {
fn new(f: impl FnOnce() -> Widget<'w> + 'w) -> Self {
let f: Box<dyn FnOnce() -> Widget<'w> + 'w> = Box::new(f);
let f: Box<dyn FnOnce() -> Widget<'static> + 'w> = unsafe { std::mem::transmute(f) };
Self(f)
}
fn consume(self) -> Widget<'w> { (self.0)() }
}
#[derive(Clone)]
pub struct GenWidget(InnerGenWidget);
type InnerGenWidget = Sc<RefCell<Box<dyn FnMut() -> Widget<'static>>>>;
pub struct FnWidget<'w>(Box<dyn FnOnce() -> 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() -> Widget<'static> + 'static) -> Self {
Self(Sc::new(RefCell::new(Box::new(f))))
}
pub fn gen_widget(&self) -> Widget<'static> { self.0.borrow_mut()() }
}
impl<'w> FnWidget<'w> {
pub fn new(f: impl FnOnce() -> Widget<'w> + 'w) -> Self { Self(Box::new(f)) }
}
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> { Widget::from_render(Box::new(PureRender(self))) }
}
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() -> Widget<'w> + 'w,
{
fn into_widget_strict(self) -> Widget<'w> {
let lazy = LazyNode::new(self);
Widget(InnerWidget::Lazy(lazy))
}
}
impl<'w> IntoWidgetStrict<'w, FN> for FnWidget<'w> {
#[inline]
fn into_widget_strict(self) -> Widget<'w> { self.0.into_widget_strict() }
}
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) + 'w) -> Self {
let lazy = move || {
let node = self.into_node().on_build(f);
Widget(InnerWidget::Node(node))
};
Widget(InnerWidget::Lazy(LazyNode::new(lazy)))
}
pub(crate) fn from_render(r: Box<dyn RenderQueryable>) -> Widget<'static> {
Widget(InnerWidget::Node(Node::Leaf(Box::new(|| BuildCtx::get_mut().alloc(r)))))
}
pub fn attach_anonymous_data(self, data: impl Any) -> Self {
self.on_build(|id| id.attach_anonymous_data(data, BuildCtx::get_mut().tree_mut()))
}
pub fn attach_data(self, data: Box<dyn Query>) -> Self {
self.on_build(|id| id.attach_data(data, BuildCtx::get_mut().tree_mut()))
}
pub 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) -> WidgetId {
let mut subtrees = vec![];
let root = self.into_node().build(&mut subtrees);
let ctx = BuildCtx::get_mut();
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().build(&mut subtrees);
p.append(c, ctx.tree_mut());
}
root
}
pub(crate) fn directly_compose_children(self, children: Vec<Widget<'w>>) -> Widget<'w> {
let mut list: SmallVec<[_; 1]> = SmallVec::default();
let mut node = Some(self);
while let Some(n) = node.take() {
match n.into_node() {
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()
}
pub(crate) fn from_id(id: WidgetId) -> Widget<'static> {
let node = Node::Leaf(Box::new(move || id));
Widget(InnerWidget::Node(node))
}
fn into_node(self) -> Node<'w> {
let mut w = self;
loop {
match w.0 {
InnerWidget::Node(node) => break node,
InnerWidget::Lazy(l) => w = l.consume(),
}
}
}
}
impl<'w> Node<'w> {
fn build(self, subtrees: &mut Vec<(WidgetId, Widget<'w>)>) -> WidgetId {
match self {
Node::Leaf(r) => r(),
Node::Tree { parent, children } => {
let p = parent();
for c in children.into_iter().rev() {
subtrees.push((p, c))
}
p
}
}
}
fn into_widget(self) -> Widget<'w> { Widget(InnerWidget::Node(self)) }
fn on_build(self, f: impl FnOnce(WidgetId) + 'w) -> Self {
let on_build_node = |node: Box<dyn FnOnce() -> WidgetId + 'w>| {
let new_node = move || {
let id = node();
f(id);
id
};
Box::new(new_node)
};
match self {
Node::Leaf(n) => Node::Leaf(on_build_node(n)),
Node::Tree { parent, children } => Node::Tree { parent: on_build_node(parent), children },
}
}
}
impl<F: FnMut() -> Widget<'static> + 'static> From<F> for GenWidget {
#[inline]
fn from(f: F) -> Self { Self::new(f) }
}