use std::convert::Infallible;
#[doc(hidden)]
pub use std::{
any::{Any, TypeId},
marker::PhantomData,
ops::Deref,
};
use rxrust::ops::box_it::CloneableBoxOp;
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>) -> impl WidgetBuilder;
}
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 {
id: WidgetId,
handle: BuildCtxHandle,
}
pub type BoxedWidget = Box<dyn for<'a, 'b> FnOnce(&'a BuildCtx<'b>) -> Widget>;
pub struct GenWidget(Box<dyn for<'a, 'b> FnMut(&'a BuildCtx<'b>) -> Widget>);
pub trait WidgetBuilder {
fn build(self, ctx: &BuildCtx) -> Widget;
fn box_it(self) -> BoxedWidget
where
Self: Sized + 'static,
{
Box::new(move |ctx| self.build(ctx))
}
}
pub trait ComposeBuilder {
fn build(self, ctx: &BuildCtx) -> Widget;
}
pub trait RenderBuilder {
fn build(self, ctx: &BuildCtx) -> Widget;
}
pub trait ComposeChildBuilder {
fn build(self, ctx: &BuildCtx) -> Widget;
}
pub trait SelfBuilder {
fn build(self, ctx: &BuildCtx) -> Widget;
}
impl Widget {
pub(crate) fn consume(self) -> WidgetId {
let id = self.id;
std::mem::forget(self);
id
}
pub(crate) fn dirty_subscribe(
self, upstream: CloneableBoxOp<'static, ModifyScope, Infallible>, ctx: &BuildCtx,
) -> Self {
let dirty_set = ctx.tree.borrow().dirty_set.clone();
let id = self.id();
let h = upstream
.filter(|b| b.contains(ModifyScope::FRAMEWORK))
.subscribe(move |_| {
dirty_set.borrow_mut().insert(id);
})
.unsubscribe_when_dropped();
self.attach_anonymous_data(h, ctx)
}
pub(crate) fn id(&self) -> WidgetId { self.id }
pub(crate) fn new(w: Box<dyn RenderQueryable>, ctx: &BuildCtx) -> Self {
Self::from_id(ctx.alloc_widget(w), ctx)
}
pub(crate) fn from_id(id: WidgetId, ctx: &BuildCtx) -> Self { Self { id, handle: ctx.handle() } }
}
impl SelfBuilder for Widget {
#[inline(always)]
fn build(self, _: &BuildCtx) -> Widget { self }
}
impl<F> WidgetBuilder for F
where
F: FnOnce(&BuildCtx) -> Widget,
{
#[inline]
fn build(self, ctx: &BuildCtx) -> Widget { self(ctx) }
}
impl WidgetBuilder for GenWidget {
#[inline]
fn build(mut self, ctx: &BuildCtx) -> Widget { self.gen_widget(ctx) }
}
impl GenWidget {
#[inline]
pub fn new(f: impl FnMut(&BuildCtx) -> Widget + 'static) -> Self { Self(Box::new(f)) }
#[inline]
pub fn gen_widget(&mut self, ctx: &BuildCtx) -> Widget { (self.0)(ctx) }
}
impl<F: FnMut(&BuildCtx) -> Widget + 'static> From<F> for GenWidget {
#[inline]
fn from(f: F) -> Self { Self::new(f) }
}
impl<C: Compose + 'static> ComposeBuilder for C {
#[inline]
fn build(self, ctx: &BuildCtx) -> Widget { Compose::compose(State::value(self)).build(ctx) }
}
impl<R: Render + 'static> RenderBuilder for R {
fn build(self, ctx: &BuildCtx) -> Widget { Widget::new(Box::new(PureRender(self)), ctx) }
}
impl<W: ComposeChild<Child = Option<C>> + 'static, C> ComposeChildBuilder for W {
#[inline]
fn build(self, ctx: &BuildCtx) -> Widget {
ComposeChild::compose_child(State::value(self), None).build(ctx)
}
}
pub(crate) fn hit_test_impl(ctx: &HitTestCtx, pos: Point) -> bool {
ctx
.box_rect()
.map_or(false, |rect| rect.contains(pos))
}
macro_rules! _replace {
(@replace($n: path) [$($e:tt)*] {#} $($rest:tt)*) => {
$crate::widget::_replace!(@replace($n) [$($e)* $n] $($rest)*);
};
(@replace($n: path) [$($e:tt)*] $first: tt $($rest:tt)*) => {
$crate::widget::_replace!(@replace($n) [$($e)* $first] $($rest)*);
};
(@replace($i: path) [$($e:tt)*]) => { $($e)* };
(@replace($n: path) $first: tt $($rest:tt)*) => {
$crate::widget::_replace!(@replace($n) [$first] $($rest)*);
};
}
macro_rules! multi_build_replace_impl {
($($rest:tt)*) => {
$crate::widget::repeat_and_replace!([
$crate::widget::ComposeBuilder,
$crate::widget::RenderBuilder,
$crate::widget::ComposeChildBuilder,
$crate::widget::WidgetBuilder
] $($rest)*);
};
}
macro_rules! multi_build_replace_impl_include_self {
($($rest:tt)*) => {
$crate::widget::multi_build_replace_impl!($($rest)*);
$crate::widget::_replace!(@replace($crate::widget::SelfBuilder) $($rest)*);
};
({} $($rest:tt)*) => {}
}
macro_rules! repeat_and_replace {
([$first: path $(,$n: path)*] $($rest:tt)*) => {
$crate::widget::_replace!(@replace($first) $($rest)*);
$crate::widget::repeat_and_replace!([$($n),*] $($rest)*);
};
([] $($rest:tt)*) => {
};
}
pub(crate) use _replace;
pub(crate) use multi_build_replace_impl;
pub(crate) use multi_build_replace_impl_include_self;
pub(crate) use repeat_and_replace;
impl Drop for Widget {
fn drop(&mut self) {
log::warn!("widget allocated but never used: {:?}", self.id);
self
.handle
.with_ctx(|ctx| ctx.tree.borrow_mut().remove_subtree(self.id));
}
}