fret_runtime/window_command_gating/
service.rs1use std::collections::HashMap;
2
3use fret_core::AppWindowId;
4
5use super::snapshot::WindowCommandGatingSnapshot;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12struct WindowCommandGatingToken(u64);
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct WindowCommandGatingHandle {
19 window: AppWindowId,
20 token: WindowCommandGatingToken,
21}
22
23#[derive(Debug, Default)]
24struct WindowCommandGatingWindowState {
25 base: Option<WindowCommandGatingSnapshot>,
26 stack: Vec<(WindowCommandGatingToken, WindowCommandGatingSnapshot)>,
27}
28
29#[derive(Debug, Default)]
30pub struct WindowCommandGatingService {
31 by_window: HashMap<AppWindowId, WindowCommandGatingWindowState>,
32 next_token: u64,
33}
34
35impl WindowCommandGatingService {
36 pub fn snapshot(&self, window: AppWindowId) -> Option<&WindowCommandGatingSnapshot> {
37 self.by_window.get(&window).and_then(|state| {
38 state
39 .stack
40 .last()
41 .map(|(_, snap)| snap)
42 .or(state.base.as_ref())
43 })
44 }
45
46 pub fn base_snapshot(&self, window: AppWindowId) -> Option<&WindowCommandGatingSnapshot> {
47 self.by_window
48 .get(&window)
49 .and_then(|state| state.base.as_ref())
50 }
51
52 pub fn set_base_snapshot(
56 &mut self,
57 window: AppWindowId,
58 snapshot: WindowCommandGatingSnapshot,
59 ) {
60 let state = self.by_window.entry(window).or_default();
61 state.base = Some(snapshot);
62 self.gc_window(window);
63 }
64
65 pub fn set_snapshot(&mut self, window: AppWindowId, snapshot: WindowCommandGatingSnapshot) {
69 self.set_base_snapshot(window, snapshot);
70 }
71
72 pub fn clear_base_snapshot(&mut self, window: AppWindowId) {
73 if let Some(state) = self.by_window.get_mut(&window) {
74 state.base = None;
75 }
76 self.gc_window(window);
77 }
78
79 pub fn clear_snapshot(&mut self, window: AppWindowId) {
80 self.clear_base_snapshot(window);
81 }
82
83 pub fn push_snapshot(
87 &mut self,
88 window: AppWindowId,
89 snapshot: WindowCommandGatingSnapshot,
90 ) -> WindowCommandGatingHandle {
91 let token = WindowCommandGatingToken(self.next_token.max(1));
92 self.next_token = token.0.saturating_add(1);
93 let state = self.by_window.entry(window).or_default();
94 state.stack.push((token, snapshot));
95 WindowCommandGatingHandle { window, token }
96 }
97
98 pub fn update_pushed_snapshot(
99 &mut self,
100 handle: WindowCommandGatingHandle,
101 snapshot: WindowCommandGatingSnapshot,
102 ) -> bool {
103 let Some(state) = self.by_window.get_mut(&handle.window) else {
104 return false;
105 };
106 for (t, s) in &mut state.stack {
107 if *t == handle.token {
108 *s = snapshot;
109 return true;
110 }
111 }
112 false
113 }
114
115 pub fn pop_snapshot(
116 &mut self,
117 handle: WindowCommandGatingHandle,
118 ) -> Option<WindowCommandGatingSnapshot> {
119 let state = self.by_window.get_mut(&handle.window)?;
120 let idx = state.stack.iter().position(|(t, _)| *t == handle.token)?;
121 let (_, snapshot) = state.stack.remove(idx);
122 self.gc_window(handle.window);
123 Some(snapshot)
124 }
125
126 pub fn remove_window(&mut self, window: AppWindowId) {
127 self.by_window.remove(&window);
128 }
129
130 fn gc_window(&mut self, window: AppWindowId) {
131 let remove = self
132 .by_window
133 .get(&window)
134 .is_some_and(|state| state.base.is_none() && state.stack.is_empty());
135 if remove {
136 self.by_window.remove(&window);
137 }
138 }
139}