1use crate::Signal;
30use incular_text::{TextEditingController, TextEditingValue};
31use std::{
32 cell::{Cell, RefCell},
33 rc::{Rc, Weak},
34};
35
36#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
38pub struct UndoHistoryState {
39 pub can_undo: bool,
40 pub can_redo: bool,
41}
42
43struct History {
44 current: TextEditingValue,
45 undo: Vec<TextEditingValue>,
46 redo: Vec<TextEditingValue>,
47 max_entries: usize,
48}
49
50const DEFAULT_MAX_ENTRIES: usize = 100;
51
52fn trim_history(entries: &mut Vec<TextEditingValue>, max_entries: usize) {
53 if entries.len() > max_entries {
54 let remove = entries.len() - max_entries;
55 entries.drain(..remove);
56 }
57}
58
59struct Inner {
60 editor: TextEditingController,
61 history: RefCell<History>,
62 applying: Cell<bool>,
63 restoration_generation: Cell<u64>,
64 state: Signal<UndoHistoryState>,
65 listener: usize,
66}
67
68impl Inner {
69 fn availability(&self) -> UndoHistoryState {
70 let history = self.history.borrow();
71 UndoHistoryState {
72 can_undo: !history.undo.is_empty(),
73 can_redo: !history.redo.is_empty(),
74 }
75 }
76
77 fn sync_state(&self) {
78 let _ = self.state.set(self.availability());
79 }
80
81 fn record(&self, value: TextEditingValue) {
82 let generation = self.editor.restoration_generation();
85 if generation != self.restoration_generation.get() {
86 self.restoration_generation.set(generation);
87 let mut history = self.history.borrow_mut();
88 history.current = value;
89 history.undo.clear();
90 history.redo.clear();
91 drop(history);
92 self.sync_state();
93 return;
94 }
95 if self.applying.get() {
96 self.history.borrow_mut().current = value;
97 return;
98 }
99
100 let changed = {
101 let mut history = self.history.borrow_mut();
102 if history.current == value {
103 false
104 } else if history.current.text == value.text {
105 history.current = value;
108 false
109 } else {
110 let previous = std::mem::replace(&mut history.current, value);
111 if history.max_entries > 0 {
112 history.undo.push(previous);
113 let max_entries = history.max_entries;
114 trim_history(&mut history.undo, max_entries);
115 }
116 history.redo.clear();
117 true
118 }
119 };
120 if changed {
121 self.sync_state();
122 }
123 }
124
125 fn apply_history_value(&self, value: TextEditingValue) {
126 struct RestorePrevious<'a> {
127 inner: &'a Inner,
128 previous: bool,
129 }
130
131 impl Drop for RestorePrevious<'_> {
132 fn drop(&mut self) {
133 self.inner.history.borrow_mut().current = self.inner.editor.value();
134 self.inner.applying.set(self.previous);
135 if !std::thread::panicking() {
136 self.inner.sync_state();
137 }
138 }
139 }
140
141 let previous = self.applying.replace(true);
142 let _guard = RestorePrevious {
143 inner: self,
144 previous,
145 };
146 self.editor.set_value(value);
147 }
148}
149
150impl Drop for Inner {
151 fn drop(&mut self) {
152 self.editor.remove_listener(self.listener);
153 }
154}
155
156#[derive(Clone)]
158pub struct UndoHistoryController {
159 inner: Rc<Inner>,
160}
161
162impl std::fmt::Debug for UndoHistoryController {
163 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 formatter
165 .debug_struct("UndoHistoryController")
166 .field("editor", &self.inner.editor)
167 .field("state", &self.status())
168 .finish()
169 }
170}
171
172impl UndoHistoryController {
173 #[must_use]
176 pub fn new(editor: TextEditingController) -> Self {
177 let initial = editor.value();
178 let inner = Rc::new_cyclic(|weak: &Weak<Inner>| {
179 let listener = {
180 let weak = weak.clone();
181 editor.add_listener(move |_| {
182 if let Some(inner) = weak.upgrade() {
183 inner.record(inner.editor.value());
184 }
185 })
186 };
187 Inner {
188 editor: editor.clone(),
189 history: RefCell::new(History {
190 current: initial.clone(),
191 undo: Vec::new(),
192 redo: Vec::new(),
193 max_entries: DEFAULT_MAX_ENTRIES,
194 }),
195 applying: Cell::new(false),
196 restoration_generation: Cell::new(editor.restoration_generation()),
197 state: Signal::new(UndoHistoryState::default()),
198 listener,
199 }
200 });
201 Self { inner }
202 }
203
204 #[must_use]
205 pub fn editor(&self) -> TextEditingController {
206 self.inner.editor.clone()
207 }
208
209 #[must_use]
211 pub fn state_signal(&self) -> Signal<UndoHistoryState> {
212 self.inner.state.clone()
213 }
214
215 #[must_use]
217 pub fn signal(&self) -> Signal<UndoHistoryState> {
218 self.state_signal()
219 }
220
221 #[must_use]
222 pub fn status(&self) -> UndoHistoryState {
223 self.inner.state.get()
224 }
225
226 #[must_use]
227 pub fn can_undo(&self) -> bool {
228 self.status().can_undo
229 }
230
231 #[must_use]
232 pub fn can_redo(&self) -> bool {
233 self.status().can_redo
234 }
235
236 pub fn set_max_entries(&self, max_entries: usize) {
240 let mut history = self.inner.history.borrow_mut();
241 history.max_entries = max_entries;
242 trim_history(&mut history.undo, max_entries);
243 trim_history(&mut history.redo, max_entries);
244 drop(history);
245 self.inner.sync_state();
246 }
247
248 #[must_use]
249 pub fn max_entries(&self) -> usize {
250 self.inner.history.borrow().max_entries
251 }
252
253 #[must_use]
255 pub fn undo(&self) -> bool {
256 let target = {
257 let mut history = self.inner.history.borrow_mut();
258 let Some(target) = history.undo.pop() else {
259 return false;
260 };
261 let current = std::mem::replace(&mut history.current, target.clone());
262 if history.max_entries > 0 {
263 history.redo.push(current);
264 let max_entries = history.max_entries;
265 trim_history(&mut history.redo, max_entries);
266 }
267 target
268 };
269 self.inner.apply_history_value(target);
270 true
271 }
272
273 #[must_use]
275 pub fn redo(&self) -> bool {
276 let target = {
277 let mut history = self.inner.history.borrow_mut();
278 let Some(target) = history.redo.pop() else {
279 return false;
280 };
281 let current = std::mem::replace(&mut history.current, target.clone());
282 if history.max_entries > 0 {
283 history.undo.push(current);
284 let max_entries = history.max_entries;
285 trim_history(&mut history.undo, max_entries);
286 }
287 target
288 };
289 self.inner.apply_history_value(target);
290 true
291 }
292
293 pub fn clear(&self) {
295 let mut history = self.inner.history.borrow_mut();
296 history.current = self.inner.editor.value();
297 history.undo.clear();
298 history.redo.clear();
299 drop(history);
300 self.inner.sync_state();
301 }
302}