#[doc(hidden)]
pub use std::{
any::{Any, TypeId},
marker::PhantomData,
ops::Deref,
};
use std::{cell::RefCell, convert::Infallible};
use ops::box_it::CloneableBoxOp;
use ribir_algo::Sc;
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: &mut HitTestCtx, pos: Point) -> HitTest {
let hit = ctx.box_hit_test(pos);
HitTest { hit, can_hit_child: hit || !self.only_sized_by_parent() }
}
fn dirty_phase(&self) -> DirtyPhase { DirtyPhase::Layout }
fn get_transform(&self) -> Option<Transform> { None }
}
pub struct Widget<'w>(InnerWidget<'w>);
pub struct InnerWidget<'w>(Box<dyn FnOnce(&mut BuildCtx) -> WidgetId + 'w>);
#[derive(Clone, ChildOfCompose)]
pub struct GenWidget(InnerGenWidget);
type InnerGenWidget = Sc<RefCell<Box<dyn FnMut() -> Widget<'static>>>>;
#[derive(ChildOfCompose)]
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)) }
pub fn call(self) -> Widget<'w> { (self.0)() }
}
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> { Widget::from_fn(move |ctx| self().call(ctx)) }
}
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 {
Widget::from_fn(move |ctx| {
let id = self.call(ctx);
f(id);
id
})
}
pub fn dirty_on(
self, upstream: CloneableBoxOp<'static, ModifyScope, Infallible>, dirty: DirtyPhase,
) -> Self {
let track = TrackWidgetId::default();
let id = track.track_id();
let tree = BuildCtx::get_mut().tree_mut();
let marker = tree.dirty_marker();
let h = upstream
.filter(|b| b.contains(ModifyScope::FRAMEWORK))
.subscribe(move |_| {
if let Some(id) = id.get() {
marker.mark(id, dirty);
}
})
.unsubscribe_when_dropped();
track
.with_child(self)
.into_widget()
.attach_anonymous_data(h)
}
pub(crate) fn from_render(r: Box<dyn RenderQueryable>) -> Widget<'static> {
Widget::from_fn(|_| BuildCtx::get_mut().tree_mut().alloc_node(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 from_id(id: WidgetId) -> Widget<'static> { Widget::from_fn(move |_| id) }
pub(crate) fn new(parent: Widget<'w>, children: Vec<Widget<'w>>) -> Widget<'w> {
Widget::from_fn(move |ctx| ctx.build_parent(parent, children))
}
pub(crate) fn from_fn(f: impl FnOnce(&mut BuildCtx) -> WidgetId + 'w) -> Widget<'w> {
Widget(InnerWidget(Box::new(f)))
}
pub(crate) fn call(self, ctx: &mut BuildCtx) -> WidgetId { (self.0.0)(ctx) }
}
impl<F: FnMut() -> Widget<'static> + 'static> From<F> for GenWidget {
#[inline]
fn from(f: F) -> Self { Self::new(f) }
}