pub struct StateStore { /* private fields */ }Expand description
Type-erased retained signal storage keyed by tree, element, and slot index.
The store has no automatic capacity limit; reconciliation must remove
unmounted element/tree slots. Stored values and returned signals are
Rc<RefCell<_>> based, making this a single-UI-thread facility. The first
concrete type used for a mounted key remains mandatory until removal.
§Examples
use ailloli_ui_runtime::app::StateStore;
let _store = StateStore::default();Implementations§
Source§impl StateStore
Provides the operations defined for StateStore.
impl StateStore
Provides the operations defined for StateStore.
Sourcepub fn signal<T: 'static>(
&mut self,
element_id: ElementId,
slot_index: usize,
initial: T,
invalidate: Rc<dyn Fn()>,
) -> Signal<T>
pub fn signal<T: 'static>( &mut self, element_id: ElementId, slot_index: usize, initial: T, invalidate: Rc<dyn Fn()>, ) -> Signal<T>
Returns a signal stored in the legacy root-tree namespace (tree 0).
Retained runtimes use Self::signal_scoped so equal ElementId
values from independent element trees cannot alias one another.
initial is evaluated by the caller before this method; it is discarded
if the slot already exists. Returned handles share value/source and keep
the invalidator installed when the slot was first created.
§Panics
Panics if this mounted tree-0/element/index key was first created with a
different concrete T.
§Examples
use std::rc::Rc;
use ailloli_ui_core::ElementId;
use ailloli_ui_runtime::app::StateStore;
let mut store = StateStore::default();
let first = store.signal(ElementId(1), 0, 10, Rc::new(|| {}));
first.set(11);
let reused = store.signal(ElementId(1), 0, 99, Rc::new(|| {}));
assert_eq!((reused.read(), reused.revision()), (11, 1));Sourcepub fn signal_scoped<T: 'static>(
&mut self,
element_tree_id: ElementTreeId,
element_id: ElementId,
slot_index: usize,
initial: T,
invalidate: Rc<dyn Fn()>,
) -> Signal<T>
pub fn signal_scoped<T: 'static>( &mut self, element_tree_id: ElementTreeId, element_id: ElementId, slot_index: usize, initial: T, invalidate: Rc<dyn Fn()>, ) -> Signal<T>
Returns a signal isolated to one retained element tree.
Equal element IDs and slot indices in different element_tree_id
namespaces do not alias. As with Self::signal, initial is eagerly
constructed before the method sees whether the slot exists.
§Panics
Panics when the existing scoped key contains a different concrete type.
§Examples
use std::rc::Rc;
use ailloli_ui_core::ElementId;
use ailloli_ui_runtime::{app::StateStore, popup::ElementTreeId};
let mut store = StateStore::default();
let a = store.signal_scoped(ElementTreeId::new(1), ElementId(1), 0, 10, Rc::new(|| {}));
let b = store.signal_scoped(ElementTreeId::new(2), ElementId(1), 0, 20, Rc::new(|| {}));
a.set(11);
assert_eq!((a.read(), b.read()), (11, 20));Sourcepub fn signal_scoped_with<T: 'static>(
&mut self,
element_tree_id: ElementTreeId,
element_id: ElementId,
slot_index: usize,
initial: impl FnOnce() -> T,
invalidate: Rc<dyn Fn()>,
) -> Signal<T>
pub fn signal_scoped_with<T: 'static>( &mut self, element_tree_id: ElementTreeId, element_id: ElementId, slot_index: usize, initial: impl FnOnce() -> T, invalidate: Rc<dyn Fn()>, ) -> Signal<T>
Lazily initializes a retained signal slot. The factory is never evaluated again while the same typed slot remains mounted.
Every returned handle shares the slot value, source revision, first
historical invalidator, and dynamic retained subscribers. The store
retains its Rcs until removal; outstanding handles keep old state alive
after removal, while a later lookup creates a fresh independent slot.
§Panics
Panics when an existing scoped key contains a different concrete T.
A panic from initial propagates and leaves the entry vacant.
§Examples
use std::{cell::Cell, rc::Rc};
use ailloli_ui_core::ElementId;
use ailloli_ui_runtime::{app::StateStore, popup::ElementTreeId};
let mut store = StateStore::default();
let calls = Rc::new(Cell::new(0));
let counted = calls.clone();
let first = store.signal_scoped_with(ElementTreeId::new(1), ElementId(1), 0, move || { counted.set(counted.get() + 1); 7 }, Rc::new(|| {}));
let second = store.signal_scoped_with(ElementTreeId::new(1), ElementId(1), 0, || 99, Rc::new(|| {}));
assert_eq!((first.read(), second.read(), calls.get()), (7, 7, 1));Sourcepub fn remove_element(&mut self, element_id: ElementId)
pub fn remove_element(&mut self, element_id: ElementId)
Removes an element from the legacy root-tree namespace (tree 0).
Every slot index and concrete type for the element is removed. Missing
elements are a no-op. Existing Signal handles continue to own and mutate
their detached values; the next lookup creates fresh state.
§Examples
use std::rc::Rc;
use ailloli_ui_core::ElementId;
use ailloli_ui_runtime::app::StateStore;
let mut store = StateStore::default();
let old = store.signal(ElementId(1), 0, 4, Rc::new(|| {}));
store.remove_element(ElementId(1));
let fresh = store.signal(ElementId(1), 0, 9, Rc::new(|| {}));
assert_eq!((old.read(), fresh.read()), (4, 9));Sourcepub fn remove_element_scoped(
&mut self,
element_tree_id: ElementTreeId,
element_id: ElementId,
)
pub fn remove_element_scoped( &mut self, element_tree_id: ElementTreeId, element_id: ElementId, )
Removes all state slots belonging to element_id in one tree only.
This scans the full sparse store in O(total slots). Other trees (even with the same element ID) remain intact.
§Examples
use std::rc::Rc;
use ailloli_ui_core::ElementId;
use ailloli_ui_runtime::{app::StateStore, popup::ElementTreeId};
let mut store = StateStore::default();
let other = store.signal_scoped(ElementTreeId::new(2), ElementId(1), 0, 20, Rc::new(|| {}));
let _ = store.signal_scoped(ElementTreeId::new(1), ElementId(1), 0, 10, Rc::new(|| {}));
store.remove_element_scoped(ElementTreeId::new(1), ElementId(1));
assert_eq!(other.read(), 20);