use std::marker::PhantomData;
use bevy::ecs::{
prelude::*,
system::{SystemParam, SystemState},
};
use crate::{
childable::Childable,
dom::ControlBundle,
insertable::Insertable,
lens::ComponentLens,
observer::{ComponentExistsObserver, Observer, OptComponentObserver, UninitObserver},
runtime::{UfMarker, UiScratchSpace, UpdateFunc},
};
pub struct Ctx<'a> {
pub(crate) world: &'a mut World,
pub(crate) current_entity: Entity,
}
pub struct McCtx<'a> {
pub(crate) world: &'a mut World,
pub(crate) get_new_child: &'a mut dyn FnMut(&mut World) -> Entity,
}
impl McCtx<'_> {
pub fn c(&mut self, f: impl FnOnce(Ctx) -> Ctx) -> &mut Self {
let new_child = (self.get_new_child)(self.world);
f(Ctx {
current_entity: new_child,
world: self.world,
});
self
}
pub fn dyn_group<M>(&mut self, children: impl Childable<M>) -> &mut Self {
self.c(|ctx: Ctx| ctx.with_bundle(ControlBundle::default()).children(children))
}
}
impl Ctx<'_> {
pub fn with<T: Component, M>(mut self, item: impl Insertable<T, M>) -> Self {
item.insert_ui_val(&mut self);
self
}
pub fn inherit(self, widget: impl FnOnce(Ctx) -> Ctx) -> Self {
widget(self)
}
pub fn state<P: SystemParam>(&mut self) -> SystemState<P> {
SystemState::new(self.world)
}
pub fn with_modified<T, O, F>(self, initial: T, observer: O, mutator: F) -> Self
where
T: Component,
O: UninitObserver,
for<'a> O::Observer: Observer<'a>,
F: for<'a> Fn(<O::Observer as Observer<'a>>::Return, T) -> T,
F: Send + Sync + 'static,
{
self.world.entity_mut(self.current_entity).insert(initial);
let entity = self.current_entity;
let uf = observer.register_self(self.world, |mut observer, world| {
let mut first = true;
let (uf, marker) = UpdateFunc::new::<T, _>(move |world| {
world.resource_scope(|world, mut ctx: Mut<UiScratchSpace>| {
let t = world.entity_mut(entity).remove::<T>().unwrap();
let (val, changed) = observer.get(world);
if !changed && !first {
drop(val);
let mut e = world.entity_mut(entity);
e.insert(t);
return;
}
first = false;
let t = mutator(val, t);
let mut e = world.entity_mut(entity);
e.get_mut::<UfMarker<T>>().unwrap().trigger(&mut ctx);
e.insert(t);
})
});
world.entity_mut(entity).insert(marker);
uf
});
uf.run(self.world);
self
}
pub fn with_bundle(self, bundle: impl Bundle) -> Self {
self.world
.entity_mut(self.current_entity)
.insert_bundle(bundle);
self
}
pub fn child(self, f: impl FnOnce(Ctx) -> Ctx) -> Self {
self.children(|ctx: &mut McCtx| {
ctx.c(f);
})
}
pub fn children<M>(mut self, children: impl Childable<M>) -> Self {
children.insert(&mut self);
self
}
pub fn component<T: Component>(&self) -> ComponentLens<T> {
ComponentLens(self.current_entity, PhantomData)
}
pub fn opt_component<T: Component>(&self) -> OptComponentObserver<T> {
OptComponentObserver(self.current_entity, PhantomData)
}
pub fn has_component<T: Send + Sync + 'static>(&self) -> ComponentExistsObserver<T> {
ComponentExistsObserver(self.current_entity, PhantomData)
}
pub fn current_entity(&self) -> Entity {
self.current_entity
}
}
pub trait WidgetBuilderExtWith<T, M, I>
where
T: Component,
I: Insertable<T, M>,
{
type WithOut: FnOnce(Ctx) -> Ctx;
fn with(self, item: I) -> Self::WithOut;
}
pub trait WidgetBuilderExtWithModified<T, O, F>
where
T: Component,
O: UninitObserver,
for<'a> O::Observer: Observer<'a>,
F: for<'a> Fn(<O::Observer as Observer<'a>>::Return, T) -> T,
F: Send + Sync + 'static,
{
type WithModifiedOut: FnOnce(Ctx) -> Ctx;
fn with_modified(self, initial: T, observer: O, mutator: F) -> Self::WithModifiedOut;
}
#[cfg(feature = "nightly")]
mod nightly_impls {
use super::*;
impl<W: 'static, T, M, I> WidgetBuilderExtWith<T, M, I> for W
where
W: FnOnce(Ctx) -> Ctx,
T: Component,
I: Insertable<T, M>,
{
type WithOut = impl FnOnce(Ctx) -> Ctx;
fn with(self, item: I) -> Self::WithOut {
|ctx| (self)(ctx).with(item)
}
}
impl<W: 'static, T, O, F> WidgetBuilderExtWithModified<T, O, F> for W
where
W: FnOnce(Ctx) -> Ctx,
T: Component,
O: UninitObserver,
for<'a> O::Observer: Observer<'a>,
F: for<'a> Fn(<O::Observer as Observer<'a>>::Return, T) -> T,
F: Send + Sync + 'static,
{
type WithModifiedOut = impl FnOnce(Ctx) -> Ctx;
fn with_modified(self, initial: T, observer: O, mutator: F) -> Self::WithModifiedOut {
|ctx| (self)(ctx).with_modified(initial, observer, mutator)
}
}
}
#[cfg(not(feature = "nightly"))]
mod stable_impls {
use super::*;
impl<W: 'static, T, M, I> WidgetBuilderExtWith<T, M, I> for W
where
W: FnOnce(Ctx) -> Ctx,
T: Component,
I: Insertable<T, M>,
{
type WithOut = Box<dyn FnOnce(Ctx) -> Ctx>;
fn with(self, item: I) -> Self::WithOut {
Box::new(|ctx| (self)(ctx).with(item))
}
}
impl<W: 'static, T, O, F> WidgetBuilderExtWithModified<T, O, F> for W
where
W: FnOnce(Ctx) -> Ctx,
T: Component,
O: UninitObserver,
for<'a> O::Observer: Observer<'a>,
F: for<'a> Fn(<O::Observer as Observer<'a>>::Return, T) -> T,
F: Send + Sync + 'static,
{
type WithModifiedOut = Box<dyn FnOnce(Ctx) -> Ctx>;
fn with_modified(self, initial: T, observer: O, mutator: F) -> Self::WithModifiedOut {
Box::new(|ctx| (self)(ctx).with_modified(initial, observer, mutator))
}
}
}