agui_widgets/plugins/
provider.rs1use std::{
2 any::TypeId,
3 collections::{HashMap, HashSet},
4 sync::Arc,
5};
6
7use agui_core::{
8 context::{Notify, Value, WidgetContext},
9 event::WidgetEvent,
10 plugin::WidgetPlugin,
11 widget::WidgetId,
12};
13use parking_lot::Mutex;
14
15#[derive(Default)]
16pub struct Provider {
17 providers: Arc<Mutex<HashMap<TypeId, HashSet<WidgetId>>>>,
18 widgets: Arc<Mutex<HashMap<WidgetId, HashSet<TypeId>>>>,
19}
20
21impl WidgetPlugin for Provider {
22 fn on_update(&self, _ctx: &WidgetContext) {}
23
24 fn on_layout(&self, _ctx: &WidgetContext) {}
25
26 fn on_events(&self, _ctx: &WidgetContext, events: &[WidgetEvent]) {
27 for event in events {
28 if let WidgetEvent::Destroyed { widget_id, .. } = event {
29 if let Some(providing) = self.widgets.lock().remove(widget_id) {
30 let mut providers = self.providers.lock();
31
32 for type_id in providing {
33 let widgets = providers.get_mut(&type_id).expect("provider map broken");
34
35 widgets.remove(widget_id);
36 }
37 }
38 }
39 }
40 }
41}
42
43pub trait ProviderExt<'ui> {
44 fn provide(&self, ctx: &WidgetContext);
45}
46
47impl<'ui, V> ProviderExt<'ui> for Notify<V>
48where
49 V: Value,
50{
51 fn provide(&self, ctx: &WidgetContext) {
53 let plugin = ctx.get_plugin_or::<Provider, _>(Provider::default);
54
55 let mut providers = plugin.providers.lock();
56
57 let type_id = TypeId::of::<V>();
58
59 providers
60 .entry(type_id)
61 .or_insert_with(HashSet::default)
62 .insert(ctx.get_self());
63
64 let mut widgets = plugin.widgets.lock();
65
66 let widget_id = ctx.get_self();
67
68 widgets
69 .entry(widget_id)
70 .or_insert_with(HashSet::new)
71 .insert(type_id);
72 }
73}
74
75pub trait ConsumerExt<'ui> {
76 fn consume<V>(&self) -> Option<Notify<V>>
77 where
78 V: Value;
79}
80
81impl<'ui> ConsumerExt<'ui> for WidgetContext<'ui> {
82 fn consume<V>(&self) -> Option<Notify<V>>
84 where
85 V: Value,
86 {
87 if let Some(plugin) = self.get_plugin::<Provider>() {
88 let providers = plugin.providers.lock();
89
90 if let Some(providers) = providers.get(&TypeId::of::<V>()) {
91 for parent_id in self.get_tree().iter_parents(self.get_self()) {
92 if providers.contains(&parent_id) {
93 return Some(
94 self.get_state_for(parent_id, || panic!("provider state broken")),
95 );
96 }
97 }
98 }
99 }
100
101 if let Some(state) = self.try_use_global::<V>() {
103 return Some(state);
104 }
105
106 None
107 }
108}