cranpose_ui/
semantics_dispatch.rs1use std::{cell::RefCell, collections::HashSet};
2
3use cranpose_core::NodeId;
4
5struct SemanticsInvalidationManager {
6 dirty_nodes: HashSet<NodeId>,
7 is_processing: bool,
8}
9
10impl SemanticsInvalidationManager {
11 fn new() -> Self {
12 Self {
13 dirty_nodes: HashSet::new(),
14 is_processing: false,
15 }
16 }
17
18 fn schedule_invalidation(&mut self, node_id: NodeId) {
19 self.dirty_nodes.insert(node_id);
20 }
21
22 fn has_pending_invalidation(&self) -> bool {
23 !self.dirty_nodes.is_empty()
24 }
25
26 fn take_pending_for_processing(&mut self) -> Option<Vec<NodeId>> {
27 if self.is_processing {
28 return None;
29 }
30
31 self.is_processing = true;
32 Some(self.dirty_nodes.drain().collect())
33 }
34
35 fn finish_processing<I>(&mut self, remaining: I)
36 where
37 I: IntoIterator<Item = NodeId>,
38 {
39 self.dirty_nodes.extend(remaining);
40 self.is_processing = false;
41 }
42
43 fn clear(&mut self) {
44 self.dirty_nodes.clear();
45 }
46}
47
48pub(crate) struct SemanticsInvalidationState {
49 manager: RefCell<SemanticsInvalidationManager>,
50}
51
52impl SemanticsInvalidationState {
53 pub(crate) fn new() -> Self {
54 Self {
55 manager: RefCell::new(SemanticsInvalidationManager::new()),
56 }
57 }
58
59 fn schedule_invalidation(&self, node_id: NodeId) {
60 self.manager.borrow_mut().schedule_invalidation(node_id);
61 }
62
63 fn has_pending_invalidation(&self) -> bool {
64 self.manager.borrow().has_pending_invalidation()
65 }
66
67 fn process_invalidations<F>(&self, processor: F)
68 where
69 F: FnMut(NodeId),
70 {
71 let Some(nodes) = self.manager.borrow_mut().take_pending_for_processing() else {
72 return;
73 };
74
75 self.process_pending_nodes(nodes, processor);
76 }
77
78 fn clear(&self) {
79 self.manager.borrow_mut().clear();
80 }
81
82 fn process_pending_nodes<F>(&self, nodes: Vec<NodeId>, mut processor: F)
83 where
84 F: FnMut(NodeId),
85 {
86 let mut remaining = nodes.into_iter();
87 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
88 for node_id in remaining.by_ref() {
89 processor(node_id);
90 }
91 }));
92
93 self.manager.borrow_mut().finish_processing(remaining);
94
95 if let Err(payload) = result {
96 std::panic::resume_unwind(payload);
97 }
98 }
99}
100
101pub fn schedule_semantics_invalidation(node_id: NodeId) {
108 crate::render_state::with_semantics_dispatch(|state| {
109 state.schedule_invalidation(node_id);
110 });
111}
112
113pub(crate) fn schedule_semantics_invalidation_in(
114 app_context: crate::render_state::AppContextId,
115 node_id: NodeId,
116) {
117 crate::render_state::with_semantics_dispatch_by_app_context(app_context, |state| {
118 state.schedule_invalidation(node_id);
119 });
120}
121
122pub fn has_pending_semantics_invalidations() -> bool {
124 crate::render_state::with_semantics_dispatch(
125 SemanticsInvalidationState::has_pending_invalidation,
126 )
127}
128
129pub fn process_semantics_invalidations<F>(processor: F)
134where
135 F: FnMut(NodeId),
136{
137 crate::render_state::with_semantics_dispatch(|state| state.process_invalidations(processor));
138}
139
140pub fn clear_semantics_invalidations() {
142 crate::render_state::with_semantics_dispatch(SemanticsInvalidationState::clear);
143}
144
145#[cfg(test)]
146#[path = "tests/semantics_dispatch_tests.rs"]
147mod tests;