use crate::{
CapacityConfig, Context, EffectId, EntitiesSlot, EntityId, FlatDfsSequenceVec, MichiuSoA,
ParentsSecondary, ReadSignal, SignalId, TopologyStore, WriteSignal, define_secondary,
define_slotmap, define_sparse_secondary, define_vec, execute_effect,
};
use rustc_hash::FxHashMap;
use slotmap::{SecondaryMap, SlotMap, SparseSecondaryMap};
use smallvec::SmallVec;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EffectCategory {
None,
Style,
Text,
Input,
Image,
Movie,
ExternalTexture,
WebView2,
Contents,
Accessibility,
ActiveState,
SelectState,
DisableState,
FocusState,
FocusableState,
}
pub struct Effects(pub Box<dyn FnMut(&mut Context)>);
impl std::fmt::Debug for Effects {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Effects(<closure>)")
}
}
define_slotmap!(pub struct SignalsSlot(SignalId, Box<dyn std::any::Any>));
define_slotmap!(pub struct EffectsSlot(EffectId, Effects));
define_secondary!(pub struct SubscribersSecondary(SignalId, SmallVec<[EffectId; 8]>));
define_secondary!(pub struct ElementEffectsSecondary(SmallVec<[(EffectCategory, EffectId); 8]>));
define_secondary!(pub struct EffectToElementSecondary(EffectId, EntityId));
define_sparse_secondary!(pub struct ProvidersSparseSecondary(FxHashMap<std::any::TypeId, SignalId>));
define_vec!(pub struct PendingElementEffectsVec(EffectId));
pub struct ReactiveStore {
pub(crate) react_signals: SignalsSlot,
pub(crate) react_effects: EffectsSlot,
pub(crate) react_subscribers: SubscribersSecondary,
pub(crate) react_element_effects: ElementEffectsSecondary,
pub(crate) react_effect_to_element: EffectToElementSecondary,
pub(crate) react_pending_element_effects: PendingElementEffectsVec,
pub(crate) react_providers: ProvidersSparseSecondary,
}
impl Default for ReactiveStore {
fn default() -> Self {
Self::new()
}
}
impl ReactiveStore {
#[inline]
#[must_use]
pub(crate) fn new() -> Self {
Self {
react_signals: SignalsSlot(SlotMap::with_key()),
react_effects: EffectsSlot(SlotMap::with_key()),
react_subscribers: SubscribersSecondary(SecondaryMap::new()),
react_element_effects: ElementEffectsSecondary(SecondaryMap::new()),
react_effect_to_element: EffectToElementSecondary(SecondaryMap::new()),
react_pending_element_effects: PendingElementEffectsVec(Vec::new()),
react_providers: ProvidersSparseSecondary(SparseSecondaryMap::new()),
}
}
#[inline]
#[must_use]
pub(crate) fn with_capacity(c: &CapacityConfig) -> Self {
Self {
react_signals: SignalsSlot(SlotMap::with_capacity_and_key(c.react_signals)),
react_effects: EffectsSlot(SlotMap::with_capacity_and_key(c.react_effects)),
react_subscribers: SubscribersSecondary(SecondaryMap::with_capacity(
c.react_subscribers,
)),
react_element_effects: ElementEffectsSecondary(SecondaryMap::with_capacity(
c.react_element_effects,
)),
react_effect_to_element: EffectToElementSecondary(SecondaryMap::with_capacity(
c.react_effect_to_element,
)),
react_pending_element_effects: PendingElementEffectsVec(Vec::with_capacity(
c.react_pending_element_effects,
)),
react_providers: ProvidersSparseSecondary(SparseSecondaryMap::with_capacity(
c.react_providers,
)),
}
}
#[inline]
pub(crate) fn clear(&mut self) {
self.react_signals.clear();
self.react_effects.clear();
self.react_subscribers.clear();
self.react_element_effects.clear();
self.react_effect_to_element.clear();
self.react_pending_element_effects.clear();
self.react_providers.clear();
}
#[inline]
pub(crate) fn despawn(&mut self, id: EntityId) {
if let Some(react_effects) = self.react_element_effects.remove(id) {
for (_, effect_id) in react_effects {
self.react_effects.remove(effect_id);
self.react_effect_to_element.remove(effect_id);
self.react_pending_element_effects
.retain(|&x| x != effect_id);
}
}
self.react_providers.remove(id);
}
}
impl ReactiveStore {
#[track_caller]
#[inline]
pub(crate) fn use_provided_from<T: Clone + 'static>(
id: EntityId,
react_providers: &ProvidersSparseSecondary,
topo_parents: &ParentsSecondary,
) -> Option<ReadSignal<T>> {
let type_id = std::any::TypeId::of::<T>();
std::iter::successors(Some(id), |&curr_id| *topo_parents.at(curr_id)).find_map(|curr_id| {
react_providers
.find(curr_id)
.and_then(|map| map.get(&type_id))
.map(|&signal_id| ReadSignal::new(signal_id))
})
}
pub(crate) fn resolve_element_effect(
react_effect_to_element: &EffectToElementSecondary,
) -> Option<EntityId> {
if let Some(effect_id) = crate::ACTIVE_EFFECT.with(std::cell::Cell::get) {
return react_effect_to_element.get(effect_id).copied();
}
if let Some(element_id) = crate::ACTIVE_ELEMENT.with(std::cell::Cell::get) {
return Some(element_id);
}
None
}
#[track_caller]
#[inline]
pub(crate) fn use_provided_setter_from<T: Send + 'static>(
id: EntityId,
react_providers: &ProvidersSparseSecondary,
topo_parents: &ParentsSecondary,
) -> Option<WriteSignal<T>> {
let type_id = std::any::TypeId::of::<T>();
std::iter::successors(Some(id), |&curr_id| *topo_parents.at(curr_id)).find_map(|curr_id| {
react_providers
.find(curr_id)
.and_then(|map| map.get(&type_id))
.map(|&signal_id| WriteSignal::new(signal_id))
})
}
#[inline]
pub(crate) fn register_element_effect(
element_id: EntityId,
category: EffectCategory,
effect_id: EffectId,
react_effects: &mut EffectsSlot,
react_element_effects: &mut ElementEffectsSecondary,
react_effect_to_element: &mut EffectToElementSecondary,
react_pending_element_effects: &mut PendingElementEffectsVec,
) {
if let Some(e) = react_element_effects.find_mut(element_id) {
if let Some(pos) = e.iter().position(|(cat, _)| *cat == category) {
let (_, old_id) = e.remove(pos);
react_effects.remove(old_id); react_effect_to_element.remove(old_id); react_pending_element_effects.retain(|&x| x != old_id); }
e.push((category, effect_id));
return;
}
react_element_effects.insert(element_id, smallvec::smallvec![(category, effect_id)]);
}
pub(crate) fn create_element_effect<F>(
element_id: EntityId,
category: EffectCategory,
react_effects: &mut EffectsSlot,
react_element_effects: &mut ElementEffectsSecondary,
react_effect_to_element: &mut EffectToElementSecondary,
react_pending_element_effects: &mut PendingElementEffectsVec,
f: F,
) -> EffectId
where
F: FnMut(&mut Context) + 'static,
{
let effect_id = react_effects.insert(Effects(Box::new(f)));
react_effect_to_element.insert(effect_id, element_id);
ReactiveStore::register_element_effect(
element_id,
category,
effect_id,
react_effects,
react_element_effects,
react_effect_to_element,
react_pending_element_effects,
);
react_pending_element_effects.push(effect_id);
effect_id
}
#[inline]
pub(crate) fn provide<T: Send + 'static>(
id: Option<EntityId>,
read_signal: ReadSignal<T>,
react_providers: &mut ProvidersSparseSecondary,
topo_entities: &EntitiesSlot,
topo_parents: &ParentsSecondary,
topo_flat_dfs_sequence: &FlatDfsSequenceVec,
) {
let id = if let Some(i) = id {
i
} else {
let Some(i) = TopologyStore::find_root_entity(
topo_entities,
topo_parents,
topo_flat_dfs_sequence,
) else {
return;
};
i
};
let Some(entry) = react_providers.entry(id) else {
return;
};
let map = entry.or_default();
map.insert(std::any::TypeId::of::<T>(), read_signal.id);
}
#[inline]
pub(crate) fn create_signal<T: Send + 'static>(
initial_value: T,
react_signals: &mut SignalsSlot,
react_subscribers: &mut SubscribersSecondary,
) -> (ReadSignal<T>, WriteSignal<T>) {
let id = react_signals.insert(Box::new(initial_value));
react_subscribers.insert(id, SmallVec::new());
(ReadSignal::new(id), WriteSignal::new(id))
}
#[inline]
pub(crate) fn evaluate_pending_element_effects(
react_pending_element_effects: &mut PendingElementEffectsVec,
react_effects: &EffectsSlot,
) {
if react_pending_element_effects.is_empty() {
return;
}
let pending: Vec<EffectId> = std::mem::take(react_pending_element_effects);
for effect_id in pending
.into_iter()
.filter(|&id| react_effects.contains_key(id))
{
execute_effect(effect_id);
}
}
#[inline]
pub fn effect_to_element_mut(&mut self) -> &mut EffectToElementSecondary {
&mut self.react_effect_to_element
}
#[inline]
pub fn effects_mut(&mut self) -> &mut EffectsSlot {
&mut self.react_effects
}
#[inline]
pub fn element_effects_mut(&mut self) -> &mut ElementEffectsSecondary {
&mut self.react_element_effects
}
#[inline]
pub fn pending_element_effects_mut(&mut self) -> &mut PendingElementEffectsVec {
&mut self.react_pending_element_effects
}
#[inline]
pub fn providers_mut(&mut self) -> &mut ProvidersSparseSecondary {
&mut self.react_providers
}
#[inline]
pub fn signals_mut(&mut self) -> &mut SignalsSlot {
&mut self.react_signals
}
#[inline]
pub fn subscribers_mut(&mut self) -> &mut SubscribersSecondary {
&mut self.react_subscribers
}
}
impl Context {
#[inline]
pub(crate) fn create_element_effect<F>(
&mut self,
element_id: EntityId,
category: EffectCategory,
f: F,
) -> EffectId
where
F: FnMut(&mut Context) + 'static,
{
ReactiveStore::create_element_effect(
element_id,
category,
&mut self.reactive.react_effects,
&mut self.reactive.react_element_effects,
&mut self.reactive.react_effect_to_element,
&mut self.reactive.react_pending_element_effects,
f,
)
}
#[inline]
pub(crate) fn evaluate_pending_element_effects(&mut self) {
ReactiveStore::evaluate_pending_element_effects(
&mut self.reactive.react_pending_element_effects,
&self.reactive.react_effects,
);
}
}
#[cfg(test)]
mod tests;