Skip to main content

cranpose_ui/modifier/
local.rs

1use std::{
2    any::{Any, TypeId},
3    fmt,
4    hash::{Hash, Hasher},
5    rc::Rc,
6};
7
8use cranpose_core::collections::map::{HashMap, HashSet};
9use cranpose_foundation::{
10    DelegatableNode, InvalidationKind, ModifierInvalidation, ModifierInvalidations, ModifierNode,
11    ModifierNodeChain, ModifierNodeElement, NodeCapabilities, NodeState,
12};
13
14#[derive(Clone)]
15struct ModifierLocalId(Rc<()>);
16
17impl ModifierLocalId {
18    fn new() -> Self {
19        Self(Rc::new(()))
20    }
21}
22
23impl fmt::Debug for ModifierLocalId {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        f.debug_tuple("ModifierLocalId")
26            .field(&(Rc::as_ptr(&self.0) as usize))
27            .finish()
28    }
29}
30
31impl PartialEq for ModifierLocalId {
32    fn eq(&self, other: &Self) -> bool {
33        Rc::ptr_eq(&self.0, &other.0)
34    }
35}
36
37impl Eq for ModifierLocalId {}
38
39impl Hash for ModifierLocalId {
40    fn hash<H: Hasher>(&self, state: &mut H) {
41        Rc::as_ptr(&self.0).hash(state);
42    }
43}
44
45#[derive(Clone, Debug, PartialEq, Eq, Hash)]
46pub(crate) struct ModifierLocalToken {
47    id: ModifierLocalId,
48    type_id: TypeId,
49}
50
51impl ModifierLocalToken {
52    fn new(type_id: TypeId) -> Self {
53        let id = ModifierLocalId::new();
54        Self { id, type_id }
55    }
56
57    fn id(&self) -> ModifierLocalId {
58        self.id.clone()
59    }
60}
61
62/// Type-safe key referencing a modifier local value.
63#[derive(Clone)]
64pub struct ModifierLocalKey<T: 'static> {
65    token: ModifierLocalToken,
66    default: Rc<dyn Fn() -> T>,
67}
68
69impl<T: 'static> ModifierLocalKey<T> {
70    /// Creates a key. `factory` makes the value that consumers read when no provider of this key is
71    /// above them.
72    pub fn new(factory: impl Fn() -> T + 'static) -> Self {
73        Self {
74            token: ModifierLocalToken::new(TypeId::of::<T>()),
75            default: Rc::new(factory),
76        }
77    }
78
79    pub(crate) fn token(&self) -> ModifierLocalToken {
80        self.token.clone()
81    }
82
83    pub(crate) fn default_value(&self) -> T {
84        (self.default)()
85    }
86}
87
88impl<T: 'static> PartialEq for ModifierLocalKey<T> {
89    fn eq(&self, other: &Self) -> bool {
90        self.token == other.token
91    }
92}
93
94impl<T: 'static> Eq for ModifierLocalKey<T> {}
95
96impl<T: 'static> Hash for ModifierLocalKey<T> {
97    fn hash<H: Hasher>(&self, state: &mut H) {
98        self.token.hash(state);
99    }
100}
101
102pub struct ModifierLocalProviderNode {
103    token: ModifierLocalToken,
104    value_factory: Rc<dyn Fn() -> Box<dyn Any>>,
105    value: Rc<dyn Any>,
106    version: u64,
107    state: NodeState,
108}
109
110impl ModifierLocalProviderNode {
111    fn new(token: ModifierLocalToken, factory: Rc<dyn Fn() -> Box<dyn Any>>) -> Self {
112        Self {
113            token,
114            value: Self::create_value(&factory),
115            value_factory: factory,
116            version: 0,
117            state: NodeState::new(),
118        }
119    }
120
121    fn update_value(&mut self) {
122        self.value = Self::create_value(&self.value_factory);
123        self.version = self.version.wrapping_add(1);
124    }
125
126    fn set_factory(&mut self, factory: Rc<dyn Fn() -> Box<dyn Any>>) {
127        self.value_factory = factory;
128        self.update_value();
129    }
130
131    fn token(&self) -> ModifierLocalToken {
132        self.token.clone()
133    }
134
135    fn value(&self) -> Rc<dyn Any> {
136        self.value.clone()
137    }
138
139    fn version(&self) -> u64 {
140        self.version
141    }
142
143    fn create_value(factory: &Rc<dyn Fn() -> Box<dyn Any>>) -> Rc<dyn Any> {
144        Rc::from(factory())
145    }
146}
147
148impl DelegatableNode for ModifierLocalProviderNode {
149    fn node_state(&self) -> &NodeState {
150        &self.state
151    }
152}
153
154impl ModifierNode for ModifierLocalProviderNode {}
155
156pub struct ModifierLocalConsumerNode {
157    callback: Rc<dyn for<'a> Fn(&mut ModifierLocalReadScope<'a>)>,
158    state: NodeState,
159}
160
161impl ModifierLocalConsumerNode {
162    fn new(callback: Rc<dyn for<'a> Fn(&mut ModifierLocalReadScope<'a>)>) -> Self {
163        Self {
164            callback,
165            state: NodeState::new(),
166        }
167    }
168
169    fn notify(&self, scope: &mut ModifierLocalReadScope<'_>) {
170        (self.callback)(scope);
171    }
172
173    fn id(&self) -> usize {
174        self as *const Self as usize
175    }
176}
177
178#[derive(Clone)]
179pub(crate) struct ResolvedModifierLocal {
180    value: Rc<dyn Any>,
181    version: u64,
182    source: ModifierLocalSource,
183}
184
185impl ResolvedModifierLocal {
186    fn new(value: Rc<dyn Any>, version: u64, source: ModifierLocalSource) -> Self {
187        Self {
188            value,
189            version,
190            source,
191        }
192    }
193
194    pub(crate) fn value(&self) -> Rc<dyn Any> {
195        self.value.clone()
196    }
197
198    pub(crate) fn version(&self) -> u64 {
199        self.version
200    }
201
202    pub(crate) fn with_source(mut self, source: ModifierLocalSource) -> Self {
203        self.source = source;
204        self
205    }
206}
207
208#[derive(Clone, Copy, Debug, PartialEq, Eq)]
209pub(crate) enum ModifierLocalSource {
210    Chain,
211    Ancestor,
212}
213
214pub(crate) type ModifierLocalAncestorResolver<'a> =
215    dyn FnMut(&ModifierLocalToken) -> Option<ResolvedModifierLocal> + 'a;
216
217#[derive(Clone)]
218struct ProviderRecord {
219    value: Rc<dyn Any>,
220    version: u64,
221}
222
223impl ProviderRecord {
224    fn new(value: Rc<dyn Any>, version: u64) -> Self {
225        Self { value, version }
226    }
227
228    fn version(&self) -> u64 {
229        self.version
230    }
231
232    fn value(&self) -> &Rc<dyn Any> {
233        &self.value
234    }
235}
236
237#[derive(Clone)]
238struct DependencyRecord {
239    token: ModifierLocalToken,
240    source: DependencySource,
241    version: u64,
242}
243
244impl DependencyRecord {
245    fn from_chain(token: ModifierLocalToken, version: u64) -> Self {
246        Self {
247            token,
248            source: DependencySource::Chain,
249            version,
250        }
251    }
252
253    fn from_ancestor(token: ModifierLocalToken, version: u64) -> Self {
254        Self {
255            token,
256            source: DependencySource::Ancestor,
257            version,
258        }
259    }
260
261    fn from_default(token: ModifierLocalToken) -> Self {
262        Self {
263            token,
264            source: DependencySource::Default,
265            version: 0,
266        }
267    }
268
269    fn is_dirty(
270        &self,
271        providers: &HashMap<ModifierLocalId, ProviderRecord>,
272        ancestor_lookup: &mut ModifierLocalAncestorResolver<'_>,
273    ) -> bool {
274        match self.source {
275            DependencySource::Chain => match providers.get(&self.token.id()) {
276                Some(record) => record.version() != self.version,
277                None => true,
278            },
279            DependencySource::Ancestor => {
280                if providers.contains_key(&self.token.id()) {
281                    return true;
282                }
283                ancestor_lookup(&self.token)
284                    .is_none_or(|resolved| resolved.version() != self.version)
285            }
286            DependencySource::Default => {
287                providers.contains_key(&self.token.id()) || ancestor_lookup(&self.token).is_some()
288            }
289        }
290    }
291}
292
293#[derive(Clone, Copy)]
294enum DependencySource {
295    Chain,
296    Ancestor,
297    Default,
298}
299
300struct ConsumerState {
301    dependencies: Vec<DependencyRecord>,
302}
303
304impl ConsumerState {
305    fn new(dependencies: Vec<DependencyRecord>) -> Self {
306        Self { dependencies }
307    }
308
309    fn needs_update(
310        &self,
311        providers: &HashMap<ModifierLocalId, ProviderRecord>,
312        ancestor_lookup: &mut ModifierLocalAncestorResolver<'_>,
313    ) -> bool {
314        if self.dependencies.is_empty() {
315            return true;
316        }
317        self.dependencies
318            .iter()
319            .any(|dependency| dependency.is_dirty(providers, ancestor_lookup))
320    }
321}
322
323impl DelegatableNode for ModifierLocalConsumerNode {
324    fn node_state(&self) -> &NodeState {
325        &self.state
326    }
327}
328
329impl ModifierNode for ModifierLocalConsumerNode {}
330
331#[derive(Clone)]
332pub struct ModifierLocalProviderElement {
333    token: ModifierLocalToken,
334    factory: Rc<dyn Fn() -> Box<dyn Any>>,
335}
336
337impl ModifierLocalProviderElement {
338    pub fn new<T, F>(key: ModifierLocalKey<T>, factory: F) -> Self
339    where
340        T: 'static,
341        F: Fn() -> T + 'static,
342    {
343        let erased = Rc::new(move || -> Box<dyn Any> { Box::new(factory()) });
344        Self {
345            token: key.token(),
346            factory: erased,
347        }
348    }
349}
350
351impl fmt::Debug for ModifierLocalProviderElement {
352    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353        f.debug_struct("ModifierLocalProviderElement")
354            .field("id", &self.token.id())
355            .finish()
356    }
357}
358
359impl PartialEq for ModifierLocalProviderElement {
360    fn eq(&self, other: &Self) -> bool {
361        self.token == other.token
362    }
363}
364
365impl Eq for ModifierLocalProviderElement {}
366
367impl Hash for ModifierLocalProviderElement {
368    fn hash<H: Hasher>(&self, state: &mut H) {
369        "modifier_local_provider".hash(state);
370        self.token.hash(state);
371    }
372}
373
374impl ModifierNodeElement for ModifierLocalProviderElement {
375    type Node = ModifierLocalProviderNode;
376
377    fn create(&self) -> Self::Node {
378        ModifierLocalProviderNode::new(self.token.clone(), self.factory.clone())
379    }
380
381    fn update(&self, node: &mut Self::Node) {
382        node.set_factory(self.factory.clone());
383    }
384
385    fn capabilities(&self) -> NodeCapabilities {
386        NodeCapabilities::MODIFIER_LOCALS
387    }
388
389    fn always_update(&self) -> bool {
390        true
391    }
392}
393
394#[derive(Clone)]
395pub struct ModifierLocalConsumerElement {
396    callback: Rc<dyn for<'a> Fn(&mut ModifierLocalReadScope<'a>)>,
397}
398
399impl ModifierLocalConsumerElement {
400    pub fn new<F>(callback: F) -> Self
401    where
402        F: for<'a> Fn(&mut ModifierLocalReadScope<'a>) + 'static,
403    {
404        Self {
405            callback: Rc::new(callback),
406        }
407    }
408}
409
410impl fmt::Debug for ModifierLocalConsumerElement {
411    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412        f.write_str("ModifierLocalConsumerElement")
413    }
414}
415
416impl PartialEq for ModifierLocalConsumerElement {
417    fn eq(&self, _other: &Self) -> bool {
418        true
419    }
420}
421
422impl Eq for ModifierLocalConsumerElement {}
423
424impl Hash for ModifierLocalConsumerElement {
425    fn hash<H: Hasher>(&self, state: &mut H) {
426        "modifier_local_consumer".hash(state);
427    }
428}
429
430impl ModifierNodeElement for ModifierLocalConsumerElement {
431    type Node = ModifierLocalConsumerNode;
432
433    fn create(&self) -> Self::Node {
434        ModifierLocalConsumerNode::new(self.callback.clone())
435    }
436
437    fn update(&self, node: &mut Self::Node) {
438        node.callback = self.callback.clone();
439    }
440
441    fn capabilities(&self) -> NodeCapabilities {
442        NodeCapabilities::MODIFIER_LOCALS
443    }
444
445    fn always_update(&self) -> bool {
446        true
447    }
448}
449
450/// Lightweight read scope surfaced to modifier local consumers.
451pub struct ModifierLocalReadScope<'a> {
452    providers: &'a HashMap<ModifierLocalId, ProviderRecord>,
453    ancestor_lookup: &'a mut ModifierLocalAncestorResolver<'a>,
454    dependencies: &'a mut Vec<DependencyRecord>,
455    fallbacks: HashMap<ModifierLocalId, Rc<dyn Any>>,
456}
457
458impl<'a> ModifierLocalReadScope<'a> {
459    fn new(
460        providers: &'a HashMap<ModifierLocalId, ProviderRecord>,
461        ancestor_lookup: &'a mut ModifierLocalAncestorResolver<'a>,
462        dependencies: &'a mut Vec<DependencyRecord>,
463    ) -> Self {
464        Self {
465            providers,
466            ancestor_lookup,
467            dependencies,
468            fallbacks: HashMap::default(),
469        }
470    }
471
472    /// Returns the value of the nearest provider of `key`, or the key's default value when there is
473    /// none. The consumer runs again when a value it read changes.
474    pub fn get<T: 'static>(&mut self, key: &ModifierLocalKey<T>) -> &T {
475        let token = key.token();
476        if let Some(record) = self.providers.get(&token.id()) {
477            self.dependencies
478                .push(DependencyRecord::from_chain(token, record.version()));
479            if let Some(value) = record.value().downcast_ref::<T>() {
480                return value;
481            }
482            return self.default_value_for(key);
483        }
484
485        if let Some(resolved) = (self.ancestor_lookup)(&token) {
486            self.dependencies.push(DependencyRecord::from_ancestor(
487                token.clone(),
488                resolved.version(),
489            ));
490            let resolved_value = resolved.value();
491            if resolved_value.downcast_ref::<T>().is_some() {
492                self.fallbacks.insert(token.id(), resolved_value);
493                return self.fallback_value_for(key);
494            }
495            return self.default_value_for(key);
496        }
497
498        self.dependencies
499            .push(DependencyRecord::from_default(token));
500        self.default_value_for(key)
501    }
502
503    fn fallback_value_for<T: 'static>(&mut self, key: &ModifierLocalKey<T>) -> &T {
504        let id = key.token().id();
505        if self
506            .fallbacks
507            .get(&id)
508            .and_then(|value| value.downcast_ref::<T>())
509            .is_none()
510        {
511            self.fallbacks
512                .insert(id.clone(), Rc::new(key.default_value()) as Rc<dyn Any>);
513        }
514
515        match self
516            .fallbacks
517            .get(&id)
518            .and_then(|value| value.downcast_ref::<T>())
519        {
520            Some(value) => value,
521            None => Box::leak(Box::new(key.default_value())),
522        }
523    }
524
525    fn default_value_for<T: 'static>(&mut self, key: &ModifierLocalKey<T>) -> &T {
526        self.fallback_value_for(key)
527    }
528}
529
530#[cfg(test)]
531#[path = "tests/local_tests.rs"]
532mod tests;
533
534#[derive(Default)]
535pub struct ModifierLocalManager {
536    providers: HashMap<ModifierLocalId, ProviderRecord>,
537    consumers: HashMap<usize, ConsumerState>,
538}
539
540impl ModifierLocalManager {
541    pub fn new() -> Self {
542        Self::default()
543    }
544
545    #[expect(private_interfaces)]
546    pub fn sync(
547        &mut self,
548        chain: &ModifierNodeChain,
549        ancestor_lookup: &mut ModifierLocalAncestorResolver<'_>,
550    ) -> ModifierInvalidations {
551        if !chain.has_capability(NodeCapabilities::MODIFIER_LOCALS) {
552            self.providers.clear();
553            self.consumers.clear();
554            return ModifierInvalidations::new();
555        }
556
557        let mut providers: HashMap<ModifierLocalId, ProviderRecord> = HashMap::default();
558        let mut seen_consumers: HashSet<usize> = HashSet::default();
559        let mut invalidations = ModifierInvalidations::new();
560
561        chain.for_each_node_with_capability(NodeCapabilities::MODIFIER_LOCALS, |_ref, node| {
562            if let Some(provider) = node.as_any().downcast_ref::<ModifierLocalProviderNode>() {
563                providers.insert(
564                    provider.token().id(),
565                    ProviderRecord::new(provider.value(), provider.version()),
566                );
567                return;
568            }
569
570            if let Some(consumer) = node.as_any().downcast_ref::<ModifierLocalConsumerNode>() {
571                let id = consumer.id();
572                seen_consumers.insert(id);
573                let needs_update = self
574                    .consumers
575                    .get(&id)
576                    .is_none_or(|state| state.needs_update(&providers, ancestor_lookup));
577                if !needs_update {
578                    return;
579                }
580
581                let mut dependencies = Vec::new();
582                {
583                    let mut scope =
584                        ModifierLocalReadScope::new(&providers, ancestor_lookup, &mut dependencies);
585                    consumer.notify(&mut scope);
586                }
587                self.consumers.insert(id, ConsumerState::new(dependencies));
588                if !invalidations
589                    .iter()
590                    .any(|entry: &ModifierInvalidation| entry.kind() == InvalidationKind::Layout)
591                {
592                    invalidations.push(ModifierInvalidation::new(
593                        InvalidationKind::Layout,
594                        NodeCapabilities::LAYOUT | NodeCapabilities::MODIFIER_LOCALS,
595                    ));
596                }
597            }
598        });
599
600        self.providers = providers;
601        self.consumers.retain(|id, _| seen_consumers.contains(id));
602
603        invalidations
604    }
605
606    pub(crate) fn resolve(&self, token: &ModifierLocalToken) -> Option<ResolvedModifierLocal> {
607        self.providers.get(&token.id()).map(|record| {
608            ResolvedModifierLocal::new(
609                record.value().clone(),
610                record.version(),
611                ModifierLocalSource::Chain,
612            )
613        })
614    }
615}