cranpose_ui/
semantics_dispatch.rs1use cranpose_core::NodeId;
29use std::cell::RefCell;
30use std::collections::HashSet;
31
32struct SemanticsInvalidationManager {
34 dirty_nodes: HashSet<NodeId>,
35 is_processing: bool,
36}
37
38impl SemanticsInvalidationManager {
39 fn new() -> Self {
40 Self {
41 dirty_nodes: HashSet::new(),
42 is_processing: false,
43 }
44 }
45
46 fn schedule_invalidation(&mut self, node_id: NodeId) {
47 self.dirty_nodes.insert(node_id);
48 }
49
50 fn has_pending_invalidation(&self) -> bool {
51 !self.dirty_nodes.is_empty()
52 }
53
54 fn take_pending_for_processing(&mut self) -> Option<Vec<NodeId>> {
55 if self.is_processing {
56 return None;
57 }
58
59 self.is_processing = true;
60 Some(self.dirty_nodes.drain().collect())
61 }
62
63 fn finish_processing<I>(&mut self, remaining: I)
64 where
65 I: IntoIterator<Item = NodeId>,
66 {
67 self.dirty_nodes.extend(remaining);
68 self.is_processing = false;
69 }
70
71 fn clear(&mut self) {
72 self.dirty_nodes.clear();
73 }
74}
75
76pub(crate) struct SemanticsInvalidationState {
77 manager: RefCell<SemanticsInvalidationManager>,
78}
79
80impl SemanticsInvalidationState {
81 pub(crate) fn new() -> Self {
82 Self {
83 manager: RefCell::new(SemanticsInvalidationManager::new()),
84 }
85 }
86
87 fn schedule_invalidation(&self, node_id: NodeId) {
88 self.manager.borrow_mut().schedule_invalidation(node_id);
89 }
90
91 fn has_pending_invalidation(&self) -> bool {
92 self.manager.borrow().has_pending_invalidation()
93 }
94
95 fn process_invalidations<F>(&self, processor: F)
96 where
97 F: FnMut(NodeId),
98 {
99 let Some(nodes) = self.manager.borrow_mut().take_pending_for_processing() else {
100 return;
101 };
102
103 self.process_pending_nodes(nodes, processor);
104 }
105
106 fn clear(&self) {
107 self.manager.borrow_mut().clear();
108 }
109
110 fn process_pending_nodes<F>(&self, nodes: Vec<NodeId>, mut processor: F)
111 where
112 F: FnMut(NodeId),
113 {
114 let mut remaining = nodes.into_iter();
115 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
116 for node_id in remaining.by_ref() {
117 processor(node_id);
118 }
119 }));
120
121 self.manager.borrow_mut().finish_processing(remaining);
122
123 if let Err(payload) = result {
124 std::panic::resume_unwind(payload);
125 }
126 }
127}
128
129pub fn schedule_semantics_invalidation(node_id: NodeId) {
136 crate::render_state::with_semantics_dispatch(|state| {
137 state.schedule_invalidation(node_id);
138 });
139}
140
141pub(crate) fn schedule_semantics_invalidation_in(
142 app_context: crate::render_state::AppContextId,
143 node_id: NodeId,
144) {
145 crate::render_state::with_semantics_dispatch_by_app_context(app_context, |state| {
146 state.schedule_invalidation(node_id);
147 });
148}
149
150pub fn has_pending_semantics_invalidations() -> bool {
152 crate::render_state::with_semantics_dispatch(|state| state.has_pending_invalidation())
153}
154
155pub fn process_semantics_invalidations<F>(processor: F)
160where
161 F: FnMut(NodeId),
162{
163 crate::render_state::with_semantics_dispatch(|state| state.process_invalidations(processor));
164}
165
166pub fn clear_semantics_invalidations() {
168 crate::render_state::with_semantics_dispatch(|state| state.clear());
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn a_scheduled_node_is_handed_to_the_processor_once() {
177 let _app_context = crate::render_state::app_context_test_scope();
178 clear_semantics_invalidations();
179
180 schedule_semantics_invalidation(7);
181 schedule_semantics_invalidation(7);
182 schedule_semantics_invalidation(9);
183 assert!(has_pending_semantics_invalidations());
184
185 let mut seen = Vec::new();
186 process_semantics_invalidations(|node_id| seen.push(node_id));
187 seen.sort_unstable();
188 assert_eq!(seen, vec![7, 9]);
189 assert!(!has_pending_semantics_invalidations());
190 }
191
192 #[test]
193 fn nothing_is_pending_until_something_asks() {
194 let _app_context = crate::render_state::app_context_test_scope();
195 clear_semantics_invalidations();
196 assert!(!has_pending_semantics_invalidations());
197
198 let mut seen = 0;
199 process_semantics_invalidations(|_| seen += 1);
200 assert_eq!(seen, 0);
201 }
202}