egui_macview/state/
undo.rs1use std::collections::VecDeque;
8
9pub const DEFAULT_UNDO_CAPACITY: usize = 50;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct NSUndoManager<T> {
15 undo_stack: VecDeque<T>,
16 redo_stack: VecDeque<T>,
17 capacity: usize,
18 active_group_initial: Option<T>,
19}
20
21impl<T: Clone + PartialEq> Default for NSUndoManager<T> {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl<T: Clone + PartialEq> NSUndoManager<T> {
28 pub fn new() -> Self {
30 Self::with_capacity(DEFAULT_UNDO_CAPACITY)
31 }
32
33 pub fn with_capacity(capacity: usize) -> Self {
35 let cap = capacity.max(1);
36 Self {
37 undo_stack: VecDeque::with_capacity(cap),
38 redo_stack: VecDeque::with_capacity(cap),
39 capacity: cap,
40 active_group_initial: None,
41 }
42 }
43
44 #[inline]
46 pub fn capacity(&self) -> usize {
47 self.capacity
48 }
49
50 #[inline]
52 pub fn undo_count(&self) -> usize {
53 self.undo_stack.len()
54 }
55
56 #[inline]
58 pub fn redo_count(&self) -> usize {
59 self.redo_stack.len()
60 }
61
62 #[inline]
64 pub fn can_undo(&self) -> bool {
65 !self.undo_stack.is_empty()
66 }
67
68 #[inline]
70 pub fn can_redo(&self) -> bool {
71 !self.redo_stack.is_empty()
72 }
73
74 #[inline]
76 pub fn is_grouping(&self) -> bool {
77 self.active_group_initial.is_some()
78 }
79
80 #[inline]
82 pub fn peek_undo(&self) -> Option<&T> {
83 self.undo_stack.back()
84 }
85
86 #[inline]
88 pub fn peek_redo(&self) -> Option<&T> {
89 self.redo_stack.back()
90 }
91
92 pub fn begin_grouping(&mut self, initial_state: T) {
97 if self.active_group_initial.is_none() {
98 self.active_group_initial = Some(initial_state);
99 }
100 }
101
102 pub fn end_grouping(&mut self, final_state: &T) -> bool {
105 if let Some(initial) = self.active_group_initial.take() {
106 if &initial != final_state {
107 self.redo_stack.clear();
108 if self.undo_stack.back() != Some(&initial) {
109 if self.undo_stack.len() >= self.capacity {
110 self.undo_stack.pop_front();
111 }
112 self.undo_stack.push_back(initial);
113 }
114 return true;
115 }
116 }
117 false
118 }
119
120 pub fn push_snapshot(&mut self, state: T) {
128 if self.active_group_initial.is_some() {
129 return;
130 }
131 self.redo_stack.clear();
132 if self.undo_stack.back() == Some(&state) {
133 return;
134 }
135 if self.undo_stack.len() >= self.capacity {
136 self.undo_stack.pop_front();
137 }
138 self.undo_stack.push_back(state);
139 }
140
141 pub fn undo(&mut self, current_state: &mut T) -> bool {
146 if let Some(initial) = self.active_group_initial.take() {
147 if *current_state != initial {
148 if self.redo_stack.len() >= self.capacity {
149 self.redo_stack.pop_front();
150 }
151 self.redo_stack.push_back(current_state.clone());
152 *current_state = initial;
153 return true;
154 }
155 }
156 if let Some(prev) = self.undo_stack.pop_back() {
157 if self.redo_stack.len() >= self.capacity {
158 self.redo_stack.pop_front();
159 }
160 self.redo_stack.push_back(current_state.clone());
161 *current_state = prev;
162 true
163 } else {
164 false
165 }
166 }
167
168 pub fn redo(&mut self, current_state: &mut T) -> bool {
173 self.active_group_initial = None;
174 if let Some(next) = self.redo_stack.pop_back() {
175 if self.undo_stack.len() >= self.capacity {
176 self.undo_stack.pop_front();
177 }
178 self.undo_stack.push_back(current_state.clone());
179 *current_state = next;
180 true
181 } else {
182 false
183 }
184 }
185
186 pub fn clear(&mut self) {
188 self.undo_stack.clear();
189 self.redo_stack.clear();
190 self.active_group_initial = None;
191 }
192
193 pub fn validate_invariants(&self) -> Result<(), String> {
198 if self.capacity == 0 {
199 return Err("NSUndoManager capacity must be >= 1".to_string());
200 }
201 if self.undo_stack.len() > self.capacity {
202 return Err(format!(
203 "undo_stack len ({}) exceeds capacity ({})",
204 self.undo_stack.len(),
205 self.capacity
206 ));
207 }
208 if self.redo_stack.len() > self.capacity {
209 return Err(format!(
210 "redo_stack len ({}) exceeds capacity ({})",
211 self.redo_stack.len(),
212 self.capacity
213 ));
214 }
215 for i in 1..self.undo_stack.len() {
216 if self.undo_stack[i - 1] == self.undo_stack[i] {
217 return Err(format!(
218 "duplicate consecutive undo snapshot at index {}",
219 i
220 ));
221 }
222 }
223 Ok(())
224 }
225}