#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(
html_logo_url = "https://avatars.githubusercontent.com/u/161107368",
html_favicon_url = "https://avatars.githubusercontent.com/u/161107368"
)]
extern crate alloc;
use ahash::AHasher;
use alloc::rc::Rc;
use core::{
any::{Any, TypeId},
cell::{Cell, RefCell, UnsafeCell},
fmt,
future::Future,
hash::{BuildHasherDefault, Hash, Hasher},
marker::PhantomData,
mem,
ops::Deref,
pin::Pin,
ptr::NonNull,
};
use slotmap::DefaultKey;
use thiserror::Error;
#[cfg(not(feature = "std"))]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
pub mod prelude {
pub use crate::{
compose::{self, catch, dyn_compose, memo, Compose, DynCompose, Error, Memo},
data::{data, Data},
use_callback, use_context, use_drop, use_local_task, use_memo, use_mut, use_provider,
use_ref, Cow, Generational, Map, RefMap, Scope, ScopeState, Signal, SignalMut,
};
#[cfg(feature = "animation")]
#[cfg_attr(docsrs, doc(cfg(feature = "animation")))]
pub use crate::animation::{use_animated, UseAnimated};
#[cfg(feature = "ecs")]
#[cfg_attr(docsrs, doc(cfg(feature = "ecs")))]
pub use crate::ecs::{
spawn, use_bundle, use_commands, use_world, use_world_once, ActuatePlugin, Composition,
Modifier, Modify, Spawn, UseCommands,
};
#[cfg(feature = "executor")]
#[cfg_attr(docsrs, doc(cfg(feature = "executor")))]
pub use crate::use_task;
#[cfg(feature = "ui")]
#[cfg_attr(docsrs, doc(cfg(feature = "ui")))]
pub use crate::ui::{scroll_view, ScrollView};
#[cfg(feature = "material")]
#[cfg_attr(docsrs, doc(cfg(feature = "material")))]
pub use crate::ui::material::{
button, container, material_ui, radio_button, text, Button, MaterialUi, RadioButton, Theme,
TypographyKind, TypographyStyleKind,
};
}
#[cfg(feature = "animation")]
#[cfg_attr(docsrs, doc(cfg(feature = "animation")))]
pub mod animation;
pub mod compose;
use self::compose::{AnyCompose, Compose};
pub mod composer;
use self::composer::Runtime;
pub mod data;
use crate::data::Data;
#[cfg(feature = "ecs")]
#[cfg_attr(docsrs, doc(cfg(feature = "ecs")))]
pub mod ecs;
#[cfg(feature = "executor")]
#[cfg_attr(docsrs, doc(cfg(feature = "executor")))]
pub mod executor;
#[cfg(feature = "ui")]
#[cfg_attr(docsrs, doc(cfg(feature = "ui")))]
pub mod ui;
#[derive(Debug)]
pub enum Cow<'a, T> {
Borrowed(RefMap<'a, T>),
Owned(T),
}
impl<T> Cow<'_, T> {
pub fn to_owned(&self) -> T
where
T: Clone,
{
self.clone().into_owned()
}
pub fn into_owned(self) -> T
where
T: Clone,
{
match self {
Cow::Borrowed(value) => (*value).clone(),
Cow::Owned(value) => value,
}
}
}
impl<T> Clone for Cow<'_, T>
where
T: Clone,
{
fn clone(&self) -> Self {
match self {
Cow::Borrowed(value) => Cow::Borrowed(*value),
Cow::Owned(value) => Cow::Owned(value.clone()),
}
}
}
impl<T> Deref for Cow<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Cow::Borrowed(ref_map) => ref_map,
Cow::Owned(value) => value,
}
}
}
impl<'a, T> From<RefMap<'a, T>> for Cow<'a, T> {
fn from(value: RefMap<'a, T>) -> Self {
Cow::Borrowed(value)
}
}
impl<'a, T> From<Signal<'a, T>> for Cow<'a, T> {
fn from(value: Signal<'a, T>) -> Self {
RefMap::from(value).into()
}
}
impl<'a, T> From<Map<'a, T>> for Cow<'a, T> {
fn from(value: Map<'a, T>) -> Self {
RefMap::from(value).into()
}
}
impl<T: fmt::Display> fmt::Display for Cow<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Cow::Borrowed(value) => value.fmt(f),
Cow::Owned(value) => value.fmt(f),
}
}
}
unsafe impl<T: Data> Data for Cow<'_, T> {}
#[derive(Debug)]
pub enum RefMap<'a, T> {
Ref(&'a T),
Signal(Signal<'a, T>),
Map(Map<'a, T>),
}
impl<T> Clone for RefMap<'_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for RefMap<'_, T> {}
impl<T> Deref for RefMap<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
RefMap::Ref(r) => r,
RefMap::Signal(s) => s,
RefMap::Map(map) => map,
}
}
}
impl<T: Hash> Hash for RefMap<'_, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<'a, T> From<Signal<'a, T>> for RefMap<'a, T> {
fn from(value: Signal<'a, T>) -> Self {
RefMap::Signal(value)
}
}
impl<'a, T> From<Map<'a, T>> for RefMap<'a, T> {
fn from(value: Map<'a, T>) -> Self {
RefMap::Map(value)
}
}
impl<T: fmt::Display> fmt::Display for RefMap<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
unsafe impl<T: Data> Data for RefMap<'_, T> {}
pub struct Map<'a, T> {
ptr: *const (),
map_fn: *const (),
deref_fn: fn(*const (), *const ()) -> &'a T,
generation: *const Cell<u64>,
}
impl<T> Deref for Map<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
(self.deref_fn)(self.ptr, self.map_fn)
}
}
pub struct MapUnchecked<'a, T> {
map: Map<'a, T>,
}
unsafe impl<T> Data for MapUnchecked<'_, T> {}
impl<C: Compose> Compose for MapUnchecked<'_, C> {
fn compose(cx: Scope<Self>) -> impl Compose {
unsafe { (*cx.me().map).any_compose(cx.state) }
}
fn name() -> Option<std::borrow::Cow<'static, str>> {
C::name()
}
}
impl<T> Hash for Map<'_, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.ptr.hash(state);
self.generation.hash(state);
}
}
pub struct Signal<'a, T> {
value: &'a T,
generation: *const Cell<u64>,
}
impl<'a, T> Signal<'a, T> {
pub fn map<U>(me: Self, f: fn(&T) -> &U) -> Map<'a, U> {
Map {
ptr: me.value as *const _ as _,
map_fn: f as _,
deref_fn: |ptr, g| {
unsafe {
let g: fn(&T) -> &U = mem::transmute(g);
g(&*(ptr as *const T))
}
},
generation: me.generation,
}
}
pub unsafe fn map_unchecked<U>(me: Self, f: fn(&T) -> &U) -> MapUnchecked<'a, U> {
MapUnchecked {
map: Signal::map(me, f),
}
}
}
impl<T> Deref for Signal<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<T> Hash for Signal<'_, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.value as *const T).hash(state);
self.generation.hash(state);
}
}
#[derive(Clone, Copy)]
struct UnsafeWrap<T: ?Sized>(T);
unsafe impl<T: ?Sized> Send for UnsafeWrap<T> {}
unsafe impl<T: ?Sized> Sync for UnsafeWrap<T> {}
pub struct SignalMut<'a, T> {
ptr: NonNull<T>,
scope_key: DefaultKey,
generation: *const Cell<u64>,
_marker: PhantomData<&'a ()>,
}
impl<'a, T: 'static> SignalMut<'a, T> {
pub fn update(me: Self, f: impl FnOnce(&mut T) + Send + 'static) {
let scope_key = me.scope_key;
Self::with(me, move |value| {
let rt = Runtime::current();
rt.queue(scope_key);
f(value)
})
}
pub fn set(me: Self, value: T)
where
T: Send,
{
SignalMut::update(me, |x| *x = value)
}
pub fn set_if_neq(me: Self, value: T)
where
T: PartialEq + Send,
{
if *me != value {
SignalMut::set(me, value);
}
}
pub fn with(me: Self, f: impl FnOnce(&mut T) + Send + 'static) {
let cell = UnsafeWrap(Some(f));
let ptr = UnsafeWrap(me.ptr);
let generation_ptr = UnsafeWrap(me.generation);
Runtime::current().update(move || {
let mut cell = cell;
let mut ptr = ptr;
let generation_ptr = generation_ptr;
let value = unsafe { ptr.0.as_mut() };
cell.0.take().unwrap()(value);
let generation = unsafe { &*generation_ptr.0 };
generation.set(generation.get() + 1)
});
}
pub fn as_ref(me: Self) -> Signal<'a, T> {
Signal {
value: unsafe { me.ptr.as_ref() },
generation: me.generation,
}
}
}
impl<T> Deref for SignalMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.ptr.as_ref() }
}
}
macro_rules! impl_pointer {
($($t:ident),*) => {
$(
impl<T> Clone for $t<'_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for $t<'_, T> {}
impl<T: fmt::Debug> fmt::Debug for $t<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct(stringify!($t))
.field("value", &**self)
.field("generation", &unsafe { &*self.generation }.get())
.finish()
}
}
impl<T: fmt::Display> fmt::Display for $t<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(&**self).fmt(f)
}
}
unsafe impl<T: Send + Sync> Send for $t<'_, T> {}
unsafe impl<T: Sync + Sync> Sync for $t<'_, T> {}
impl<'a, T: 'a> IntoIterator for $t<'a, T>
where
&'a T: IntoIterator,
{
type Item = <&'a T as IntoIterator>::Item;
type IntoIter = <&'a T as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
let value: &T = &self;
let value: &T = unsafe { mem::transmute(value) };
value.into_iter()
}
}
unsafe impl<T: Data> Data for $t<'_, T> {}
)*
};
}
impl_pointer!(Signal, Map, SignalMut);
#[derive(Clone, Default)]
struct Contexts {
values: HashMap<TypeId, Rc<dyn Any>, BuildHasherDefault<AHasher>>,
}
pub type ScopeState<'a> = &'a ScopeData<'a>;
#[derive(Default)]
pub struct ScopeData<'a> {
hooks: UnsafeCell<Vec<Box<dyn Any>>>,
hook_idx: Cell<usize>,
contexts: RefCell<Contexts>,
child_contexts: RefCell<Contexts>,
drops: RefCell<Vec<usize>>,
generation: Cell<u64>,
_marker: PhantomData<&'a fn(ScopeData<'a>) -> ScopeData<'a>>,
}
impl Drop for ScopeData<'_> {
fn drop(&mut self) {
for idx in &*self.drops.borrow() {
let hooks = unsafe { &mut *self.hooks.get() };
let any = hooks.get_mut(*idx).unwrap();
(**any).downcast_mut::<Box<dyn FnMut()>>().unwrap()();
}
}
}
pub struct Scope<'a, C: ?Sized> {
me: &'a C,
state: ScopeState<'a>,
}
impl<'a, C> Scope<'a, C> {
pub fn me(self) -> Signal<'a, C> {
Signal {
value: self.me,
generation: &self.state.generation,
}
}
pub fn state(self) -> ScopeState<'a> {
self.state
}
}
impl<C> Clone for Scope<'_, C> {
fn clone(&self) -> Self {
*self
}
}
impl<C> Copy for Scope<'_, C> {}
impl<'a, C> Deref for Scope<'a, C> {
type Target = ScopeState<'a>;
fn deref(&self) -> &Self::Target {
&self.state
}
}
pub fn use_ref<T: 'static>(cx: ScopeState<'_>, make_value: impl FnOnce() -> T) -> &T {
let hooks = unsafe { &mut *cx.hooks.get() };
let idx = cx.hook_idx.get();
cx.hook_idx.set(idx + 1);
let any = if idx >= hooks.len() {
hooks.push(Box::new(make_value()));
hooks.last().unwrap()
} else {
hooks.get(idx).unwrap()
};
(**any).downcast_ref().unwrap()
}
struct MutState<T> {
value: T,
generation: Cell<u64>,
}
pub fn use_mut<T: 'static>(cx: ScopeState<'_>, make_value: impl FnOnce() -> T) -> SignalMut<'_, T> {
let hooks = unsafe { &mut *cx.hooks.get() };
let idx = cx.hook_idx.get();
cx.hook_idx.set(idx + 1);
let any = if idx >= hooks.len() {
let state = MutState {
value: make_value(),
generation: Cell::new(0),
};
hooks.push(Box::new(state));
hooks.last_mut().unwrap()
} else {
hooks.get_mut(idx).unwrap()
};
let state: &mut MutState<T> = any.downcast_mut().unwrap();
SignalMut {
ptr: unsafe { NonNull::new_unchecked(&mut state.value as *mut _) },
scope_key: Runtime::current().current_key.get(),
generation: &state.generation,
_marker: PhantomData,
}
}
pub fn use_callback<'a, T, R>(
cx: ScopeState<'a>,
f: impl FnMut(T) -> R + 'a,
) -> &'a Rc<dyn Fn(T) -> R + 'a>
where
T: 'static,
R: 'static,
{
let f_cell: Option<Box<dyn FnMut(T) -> R + 'a>> = Some(Box::new(f));
let mut f_cell: Option<Box<dyn FnMut(T) -> R>> = unsafe { mem::transmute(f_cell) };
let callback = use_ref(cx, || Rc::new(RefCell::new(f_cell.take().unwrap()))).clone();
if let Some(f) = f_cell {
*callback.borrow_mut() = f;
}
use_ref(cx, move || {
let f = callback.clone();
Rc::new(move |input| f.borrow_mut()(input)) as Rc<dyn Fn(T) -> R>
})
}
#[derive(Error)]
pub struct ContextError<T> {
_marker: PhantomData<T>,
}
impl<T> Clone for ContextError<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for ContextError<T> {}
impl<T> fmt::Debug for ContextError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("ContextError")
.field(&core::any::type_name::<T>())
.finish()
}
}
impl<T> fmt::Display for ContextError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&format!(
"Context value not found for type: {}",
core::any::type_name::<T>()
))
}
}
pub fn use_context<T: 'static>(cx: ScopeState<'_>) -> Result<&Rc<T>, ContextError<T>> {
let result = use_ref(cx, || {
let Some(any) = cx.contexts.borrow().values.get(&TypeId::of::<T>()).cloned() else {
return Err(ContextError {
_marker: PhantomData,
});
};
let value: Rc<T> = Rc::downcast(any).unwrap();
Ok(value)
});
result.as_ref().map_err(|e| *e)
}
pub fn use_provider<T: 'static>(cx: ScopeState<'_>, make_value: impl FnOnce() -> T) -> &Rc<T> {
use_ref(cx, || {
let value = Rc::new(make_value());
cx.child_contexts
.borrow_mut()
.values
.insert(TypeId::of::<T>(), value.clone());
value
})
}
pub trait Generational {
fn generation(self) -> u64;
}
impl<T> Generational for Signal<'_, T> {
fn generation(self) -> u64 {
unsafe { &*self.generation }.get()
}
}
impl<T> Generational for Map<'_, T> {
fn generation(self) -> u64 {
unsafe { &*self.generation }.get()
}
}
impl<T> Generational for SignalMut<'_, T> {
fn generation(self) -> u64 {
unsafe { &*self.generation }.get()
}
}
pub fn use_effect<D, T>(cx: ScopeState<'_>, dependency: D, effect: impl FnOnce(&D))
where
D: PartialEq + Send + 'static,
{
let mut dependency_cell = Some(dependency);
let last_mut = use_mut(cx, || dependency_cell.take().unwrap());
if let Some(dependency) = dependency_cell.take() {
if dependency != *last_mut {
effect(&dependency);
SignalMut::set(last_mut, dependency);
}
} else {
effect(&last_mut);
}
}
pub fn use_memo<D, T>(
cx: ScopeState<'_>,
dependency: D,
make_value: impl FnOnce() -> T,
) -> Signal<'_, T>
where
D: PartialEq + Send + 'static,
T: Send + 'static,
{
let mut dependency_cell = Some(dependency);
let mut make_value_cell = Some(make_value);
let value_mut = use_mut(cx, || make_value_cell.take().unwrap()());
let last_mut = use_mut(cx, || dependency_cell.take().unwrap());
if let Some(make_value) = make_value_cell {
if let Some(dependency) = dependency_cell.take() {
if dependency != *last_mut {
let value = make_value();
SignalMut::with(value_mut, move |update| *update = value);
SignalMut::with(last_mut, move |dst| *dst = dependency);
}
}
}
SignalMut::as_ref(value_mut)
}
pub fn use_drop<'a>(cx: ScopeState<'a>, f: impl FnOnce() + 'a) {
let mut f_cell = Some(f);
let cell = use_ref(cx, || {
let f: Box<dyn FnOnce()> = Box::new(f_cell.take().unwrap());
let f: Box<dyn FnOnce()> = unsafe { mem::transmute(f) };
RefCell::new(Some(f))
});
let idx = cx.hook_idx.get();
use_ref(cx, || {
cx.drops.borrow_mut().push(idx);
let f: Box<dyn FnMut()> = Box::new(move || {
cell.borrow_mut().take().unwrap()();
});
let f: Box<dyn FnMut()> = unsafe { mem::transmute(f) };
f
});
if let Some(f) = f_cell {
let f: Box<dyn FnOnce()> = Box::new(f);
let f: Box<dyn FnOnce()> = unsafe { mem::transmute(f) };
*cell.borrow_mut() = Some(f);
}
}
pub fn use_local_task<'a, F>(cx: ScopeState<'a>, make_task: impl FnOnce() -> F)
where
F: Future<Output = ()> + 'a,
{
let key = *use_ref(cx, || {
let task: Pin<Box<dyn Future<Output = ()>>> = Box::pin(make_task());
let task: Pin<Box<dyn Future<Output = ()>>> = unsafe { mem::transmute(task) };
let rt = Runtime::current();
let key = rt.tasks.borrow_mut().insert(task);
rt.task_queue.push(key);
key
});
use_drop(cx, move || {
Runtime::current().tasks.borrow_mut().remove(key);
})
}
#[cfg(feature = "executor")]
type BoxedFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
#[cfg(feature = "executor")]
struct TaskFuture {
task: alloc::sync::Arc<std::sync::Mutex<Option<BoxedFuture>>>,
rt: Runtime,
}
#[cfg(feature = "executor")]
impl Future for TaskFuture {
type Output = ();
fn poll(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context,
) -> std::task::Poll<Self::Output> {
let me = &mut *self;
let mut guard = me.task.lock().unwrap();
if let Some(task) = &mut *guard {
me.rt.enter();
let _guard = Box::pin(me.rt.lock.read()).as_mut().poll(cx);
task.as_mut().poll(cx)
} else {
std::task::Poll::Ready(())
}
}
}
#[cfg(feature = "executor")]
unsafe impl Send for TaskFuture {}
#[cfg(feature = "executor")]
#[cfg_attr(docsrs, doc(cfg(feature = "executor")))]
pub fn use_task<'a, F>(cx: ScopeState<'a>, make_task: impl FnOnce() -> F)
where
F: Future<Output = ()> + Send + 'a,
{
let runtime_cx = use_context::<executor::ExecutorContext>(cx).unwrap();
let task_lock = use_ref(cx, || {
let task: Pin<Box<dyn Future<Output = ()> + Send>> = Box::pin(make_task());
let task: Pin<Box<dyn Future<Output = ()> + Send>> = unsafe { mem::transmute(task) };
let task_lock = std::sync::Arc::new(std::sync::Mutex::new(Some(task)));
runtime_cx.executor.spawn(Box::pin(TaskFuture {
task: task_lock.clone(),
rt: Runtime::current(),
}));
task_lock
});
use_drop(cx, || {
*task_lock.lock().unwrap() = None;
});
}