use std::{fmt, ops, sync::Arc};
use crate::prelude::*;
#[doc(hidden)]
pub use zng_wgt::prelude::clmv as __clmv;
type BoxedWgtFn<D> = Box<dyn Fn(D) -> BoxedUiNode + Send + Sync>;
pub struct WidgetFn<D: ?Sized>(Option<Arc<BoxedWgtFn<D>>>);
impl<D> Clone for WidgetFn<D> {
fn clone(&self) -> Self {
WidgetFn(self.0.clone())
}
}
impl<D> fmt::Debug for WidgetFn<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "WidgetFn<{}>", pretty_type_name::pretty_type_name::<D>())
}
}
impl<D> PartialEq for WidgetFn<D> {
fn eq(&self, other: &Self) -> bool {
match (&self.0, &other.0) {
(None, None) => true,
(Some(a), Some(b)) => Arc::ptr_eq(a, b),
_ => false,
}
}
}
impl<D> Default for WidgetFn<D> {
fn default() -> Self {
Self::nil()
}
}
impl<D> WidgetFn<D> {
pub fn new<U: UiNode>(func: impl Fn(D) -> U + Send + Sync + 'static) -> Self {
WidgetFn(Some(Arc::new(Box::new(move |data| func(data).boxed()))))
}
pub const fn nil() -> Self {
WidgetFn(None)
}
pub fn is_nil(&self) -> bool {
self.0.is_none()
}
pub fn call(&self, data: D) -> BoxedUiNode {
if let Some(g) = &self.0 {
g(data)
} else {
NilUiNode.boxed()
}
}
pub fn call_checked(&self, data: D) -> Option<BoxedUiNode> {
let r = self.0.as_ref()?(data);
if r.is_nil() {
None
} else {
Some(r)
}
}
pub fn singleton(widget: impl UiNode) -> Self {
let widget = ArcNode::new(widget);
Self::new(move |_| widget.take_on_init())
}
}
impl<D: 'static> ops::Deref for WidgetFn<D> {
type Target = dyn Fn(D) -> BoxedUiNode;
fn deref(&self) -> &Self::Target {
match self.0.as_ref() {
Some(f) => &**f,
None => &nil_call::<D>,
}
}
}
fn nil_call<D>(_: D) -> BoxedUiNode {
NilUiNode.boxed()
}
#[macro_export]
macro_rules! wgt_fn {
($fn:path) => {
$crate::WidgetFn::new($fn)
};
($($tt:tt)+) => {
$crate::WidgetFn::new($crate::__clmv! {
$($tt)+
})
};
() => {
$crate::WidgetFn::nil()
};
}