1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11#![recursion_limit = "256"]
12#![expect(clippy::type_complexity)]
14
15use std::{
16 any::Any,
17 fmt, mem,
18 sync::{
19 Arc,
20 atomic::{AtomicBool, Ordering},
21 },
22 time::Duration,
23};
24
25use atomic::Atomic;
26use parking_lot::Mutex;
27use zng_app::{
28 AppExtension, DInstant, INSTANT,
29 event::CommandScope,
30 event::{AnyEventArgs, Command, CommandNameExt, command},
31 shortcut::{CommandShortcutExt, shortcut},
32 update::EventUpdate,
33 widget::{
34 WIDGET, WidgetId,
35 info::{WidgetInfo, WidgetInfoBuilder},
36 },
37};
38use zng_app_context::{RunOnDrop, app_local, context_local};
39use zng_clone_move::clmv;
40use zng_ext_input::{focus::cmd::CommandFocusExt, keyboard::KEYBOARD};
41use zng_state_map::{StateId, StateMapRef, static_id};
42use zng_txt::Txt;
43use zng_var::{BoxedVar, Var, VarHandle, VarValue, WeakVar, context_var, var};
44use zng_wgt::{CommandIconExt as _, ICONS, wgt_fn};
45
46mod private {
47 pub trait Sealed {}
49}
50
51#[derive(Default)]
59pub struct UndoManager {}
60
61impl AppExtension for UndoManager {
62 fn event(&mut self, update: &mut EventUpdate) {
63 if let Some(args) = UNDO_CMD.on_unhandled(update) {
65 args.propagation().stop();
66 if let Some(c) = args.param::<u32>() {
67 UNDO.undo_select(*c);
68 } else if let Some(i) = args.param::<Duration>() {
69 UNDO.undo_select(*i);
70 } else if let Some(t) = args.param::<DInstant>() {
71 UNDO.undo_select(*t);
72 } else {
73 UNDO.undo();
74 }
75 } else if let Some(args) = REDO_CMD.on_unhandled(update) {
76 args.propagation().stop();
77 if let Some(c) = args.param::<u32>() {
78 UNDO.redo_select(*c);
79 } else if let Some(i) = args.param::<Duration>() {
80 UNDO.redo_select(*i);
81 } else if let Some(t) = args.param::<DInstant>() {
82 UNDO.redo_select(*t);
83 } else {
84 UNDO.redo();
85 }
86 }
87 }
88}
89
90context_var! {
91 pub static UNDO_LIMIT_VAR: u32 = UNDO.undo_limit();
97
98 pub static UNDO_INTERVAL_VAR: Duration = UNDO.undo_interval();
104}
105
106pub struct UNDO;
108impl UNDO {
109 pub fn undo_limit(&self) -> BoxedVar<u32> {
116 UNDO_SV.read().undo_limit.clone()
117 }
118
119 pub fn undo_interval(&self) -> BoxedVar<Duration> {
133 UNDO_SV.read().undo_interval.clone()
134 }
135
136 pub fn is_enabled(&self) -> bool {
142 UNDO_SCOPE_CTX.get().enabled.load(Ordering::Relaxed) && UNDO_SV.read().undo_limit.get() > 0
143 }
144
145 pub fn undo_select(&self, selector: impl UndoSelector) {
155 UNDO_SCOPE_CTX.get().undo_select(selector);
156 }
157
158 pub fn redo_select(&self, selector: impl UndoSelector) {
160 UNDO_SCOPE_CTX.get().redo_select(selector);
161 }
162
163 pub fn undo(&self) {
167 self.undo_select(UNDO_INTERVAL_VAR.get());
168 }
169
170 pub fn redo(&self) {
174 self.redo_select(UNDO_INTERVAL_VAR.get());
175 }
176
177 pub fn scope(&self) -> Option<WidgetId> {
180 UNDO_SCOPE_CTX.get().id()
181 }
182
183 pub fn register(&self, action: impl UndoAction) {
185 UNDO_SCOPE_CTX.get().register(Box::new(action))
186 }
187
188 pub fn register_op(&self, info: impl UndoInfo, op: impl FnMut(UndoOp) + Send + 'static) {
192 self.register(UndoRedoOp {
193 info: info.into_dyn(),
194 op: Box::new(op),
195 })
196 }
197
198 pub fn register_full_op<D>(&self, data: D, mut op: impl FnMut(&mut D, UndoFullOp) + Send + 'static)
202 where
203 D: Any + Send + 'static,
204 {
205 self.register(UndoRedoFullOp {
206 data: Box::new(data),
207 op: Box::new(move |d, o| {
208 op(d.downcast_mut::<D>().unwrap(), o);
209 }),
210 })
211 }
212
213 pub fn run(&self, action: impl RedoAction) {
215 UNDO_SCOPE_CTX.get().register(Box::new(action).redo())
216 }
217
218 pub fn run_op(&self, info: impl UndoInfo, op: impl FnMut(UndoOp) + Send + 'static) {
220 self.run(UndoRedoOp {
221 info: info.into_dyn(),
222 op: Box::new(op),
223 })
224 }
225
226 pub fn run_full_op<D>(&self, mut data: D, mut op: impl FnMut(&mut D, UndoFullOp) + Send + 'static)
228 where
229 D: Any + Send + 'static,
230 {
231 let mut redo = true;
232 op(&mut data, UndoFullOp::Init { redo: &mut redo });
233
234 if redo {
235 self.run(UndoRedoFullOp {
236 data: Box::new(data),
237 op: Box::new(move |d, o| {
238 op(d.downcast_mut::<D>().unwrap(), o);
239 }),
240 })
241 }
242 }
243
244 pub fn group(&self, info: impl UndoInfo, actions: impl FnOnce()) -> bool {
248 let t = self.transaction(actions);
249 let any = !t.is_empty();
250 if any {
251 t.commit_group(info);
252 }
253 any
254 }
255
256 pub fn transaction(&self, actions: impl FnOnce()) -> UndoTransaction {
261 let mut scope = UndoScope::default();
262 let parent_scope = UNDO_SCOPE_CTX.get();
263 *scope.enabled.get_mut() = parent_scope.enabled.load(Ordering::Relaxed);
264 *scope.id.get_mut() = parent_scope.id.load(Ordering::Relaxed);
265
266 let t_scope = Arc::new(scope);
267 let _panic_undo = RunOnDrop::new(clmv!(t_scope, || {
268 for undo in mem::take(&mut *t_scope.undo.lock()).into_iter().rev() {
269 let _ = undo.action.undo();
270 }
271 }));
272
273 let mut scope = Some(t_scope);
274 UNDO_SCOPE_CTX.with_context(&mut scope, actions);
275
276 let scope = scope.unwrap();
277 let undo = mem::take(&mut *scope.undo.lock());
278
279 UndoTransaction { undo }
280 }
281
282 pub fn try_group<O, E>(&self, info: impl UndoInfo, actions: impl FnOnce() -> Result<O, E>) -> Result<O, E> {
287 let mut r = None;
288 let t = self.transaction(|| r = Some(actions()));
289 let r = r.unwrap();
290 if !t.is_empty() {
291 if r.is_ok() {
292 t.commit_group(info);
293 } else {
294 t.undo();
295 }
296 }
297 r
298 }
299
300 pub fn try_commit<O, E>(&self, actions: impl FnOnce() -> Result<O, E>) -> Result<O, E> {
304 let mut r = None;
305 let t = self.transaction(|| r = Some(actions()));
306 let r = r.unwrap();
307 if !t.is_empty() {
308 if r.is_ok() {
309 t.commit();
310 } else {
311 t.undo();
312 }
313 }
314 r
315 }
316
317 pub fn with_scope<R>(&self, scope: &mut WidgetUndoScope, f: impl FnOnce() -> R) -> R {
319 UNDO_SCOPE_CTX.with_context(&mut scope.0, f)
320 }
321
322 pub fn with_disabled<R>(&self, f: impl FnOnce() -> R) -> R {
324 let mut scope = UndoScope::default();
325 let parent_scope = UNDO_SCOPE_CTX.get();
326 *scope.enabled.get_mut() = false;
327 *scope.id.get_mut() = parent_scope.id.load(Ordering::Relaxed);
328
329 UNDO_SCOPE_CTX.with_context(&mut Some(Arc::new(scope)), f)
330 }
331
332 pub fn watch_var<T: VarValue>(&self, info: impl UndoInfo, var: impl Var<T>) -> VarHandle {
342 if var.capabilities().is_always_read_only() {
343 return VarHandle::dummy();
344 }
345 let var = var.actual_var();
346 let wk_var = var.downgrade();
347
348 let mut prev_value = Some(var.get());
349 let info = info.into_dyn();
350
351 var.trace_value(move |args| {
352 if args.downcast_tags::<UndoVarModifyTag>().next().is_none() {
353 let prev = prev_value.take().unwrap();
354 let new = args.value();
355 if &prev == new {
356 prev_value = Some(prev);
358 return;
359 }
360 prev_value = Some(new.clone());
361 UNDO.register_op(
362 info.clone(),
363 clmv!(wk_var, new, |op| if let Some(var) = wk_var.upgrade() {
364 let _ = match op {
365 UndoOp::Undo => var.modify(clmv!(prev, |args| {
366 args.set(prev);
367 args.push_tag(UndoVarModifyTag);
368 })),
369 UndoOp::Redo => var.modify(clmv!(new, |args| {
370 args.set(new);
371 args.push_tag(UndoVarModifyTag);
372 })),
373 };
374 }),
375 );
376 }
377 })
378 }
379
380 pub fn clear_redo(&self) {
382 UNDO_SCOPE_CTX.get().redo.lock().clear();
383 }
384
385 pub fn clear(&self) {
387 let ctx = UNDO_SCOPE_CTX.get();
388 let mut u = ctx.undo.lock();
389 u.clear();
390 ctx.redo.lock().clear();
391 }
392
393 pub fn can_undo(&self) -> bool {
395 !UNDO_SCOPE_CTX.get().undo.lock().is_empty()
396 }
397
398 pub fn can_redo(&self) -> bool {
400 !UNDO_SCOPE_CTX.get().redo.lock().is_empty()
401 }
402
403 pub fn undo_stack(&self) -> UndoStackInfo {
407 UndoStackInfo::undo(&UNDO_SCOPE_CTX.get(), UNDO_INTERVAL_VAR.get())
408 }
409
410 pub fn redo_stack(&self) -> UndoStackInfo {
416 UndoStackInfo::redo(&UNDO_SCOPE_CTX.get(), UNDO_INTERVAL_VAR.get())
417 }
418}
419
420#[derive(Clone)]
422pub struct UndoStackInfo {
423 pub stack: Vec<(DInstant, Arc<dyn UndoInfo>)>,
431
432 pub undo_interval: Duration,
434}
435impl UndoStackInfo {
436 fn undo(ctx: &UndoScope, undo_interval: Duration) -> Self {
437 Self {
438 stack: ctx.undo.lock().iter_mut().map(|e| (e.timestamp, e.action.info())).collect(),
439 undo_interval,
440 }
441 }
442 fn redo(ctx: &UndoScope, undo_interval: Duration) -> Self {
443 Self {
444 stack: ctx.redo.lock().iter_mut().map(|e| (e.timestamp, e.action.info())).collect(),
445 undo_interval,
446 }
447 }
448
449 pub fn iter_groups(&self) -> impl DoubleEndedIterator<Item = &[(DInstant, Arc<dyn UndoInfo>)]> {
451 struct Iter<'a> {
452 stack: &'a [(DInstant, Arc<dyn UndoInfo>)],
453 interval: Duration,
454 ts_inverted: bool,
455 }
456 impl<'a> Iterator for Iter<'a> {
457 type Item = &'a [(DInstant, Arc<dyn UndoInfo>)];
458
459 fn next(&mut self) -> Option<Self::Item> {
460 if self.stack.is_empty() {
461 None
462 } else {
463 let mut older = self.stack[0].0;
464
465 let mut r = self.stack;
466
467 if let Some(i) = self.stack.iter().position(|(newer, _)| {
468 let (a, b) = if self.ts_inverted { (older, *newer) } else { (*newer, older) };
469 let break_ = a.saturating_duration_since(b) > self.interval;
470 older = *newer;
471 break_
472 }) {
473 r = &self.stack[..i];
474 self.stack = &self.stack[i..];
475 } else {
476 self.stack = &[];
477 }
478
479 Some(r)
480 }
481 }
482 }
483 impl DoubleEndedIterator for Iter<'_> {
484 fn next_back(&mut self) -> Option<Self::Item> {
485 if self.stack.is_empty() {
486 None
487 } else {
488 let mut newer = self.stack[self.stack.len() - 1].0;
489
490 let mut r = self.stack;
491
492 if let Some(i) = self.stack.iter().rposition(|(older, _)| {
493 let (a, b) = if self.ts_inverted { (*older, newer) } else { (newer, *older) };
494 let break_ = a.saturating_duration_since(b) > self.interval;
495 newer = *older;
496 break_
497 }) {
498 let i = i + 1;
499 r = &self.stack[i..];
500 self.stack = &self.stack[..i];
501 } else {
502 self.stack = &[];
503 }
504
505 Some(r)
506 }
507 }
508 }
509 Iter {
510 stack: &self.stack,
511 interval: self.undo_interval,
512 ts_inverted: self.stack.len() > 1 && self.stack[0].0 > self.stack[self.stack.len() - 1].0,
513 }
514 }
515}
516
517#[derive(Debug, Clone, Copy, PartialEq)]
523pub struct UndoVarModifyTag;
524
525pub trait UndoInfo: Send + Sync + Any {
527 fn description(&self) -> Txt;
529
530 fn meta(&self) -> StateMapRef<UNDO> {
535 StateMapRef::empty()
536 }
537
538 fn into_dyn(self) -> Arc<dyn UndoInfo>
540 where
541 Self: Sized,
542 {
543 Arc::new(self)
544 }
545}
546impl UndoInfo for Txt {
547 fn description(&self) -> Txt {
548 self.clone()
549 }
550}
551impl UndoInfo for BoxedVar<Txt> {
552 fn description(&self) -> Txt {
553 self.get()
554 }
555}
556impl UndoInfo for &'static str {
557 fn description(&self) -> Txt {
558 Txt::from_static(self)
559 }
560}
561impl UndoInfo for Arc<dyn UndoInfo> {
562 fn description(&self) -> Txt {
563 self.as_ref().description()
564 }
565
566 fn meta(&self) -> StateMapRef<UNDO> {
567 self.as_ref().meta()
568 }
569
570 fn into_dyn(self) -> Arc<dyn UndoInfo>
571 where
572 Self: Sized,
573 {
574 self
575 }
576}
577pub trait UndoAction: Send + Any {
579 fn info(&mut self) -> Arc<dyn UndoInfo>;
581
582 fn undo(self: Box<Self>) -> Box<dyn RedoAction>;
584
585 fn as_any(&mut self) -> &mut dyn Any;
587
588 fn merge(self: Box<Self>, args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)>;
600}
601
602pub struct UndoActionMergeArgs {
604 pub next: Box<dyn UndoAction>,
606
607 pub prev_timestamp: DInstant,
609
610 pub within_undo_interval: bool,
615}
616
617pub trait RedoAction: Send + Any {
619 fn info(&mut self) -> Arc<dyn UndoInfo>;
621
622 fn redo(self: Box<Self>) -> Box<dyn UndoAction>;
624}
625
626#[derive(Debug, Clone, Copy, PartialEq, Eq)]
634pub enum UndoOp {
635 Undo,
637 Redo,
639}
640impl UndoOp {
641 pub fn cmd(self) -> Command {
643 match self {
644 UndoOp::Undo => UNDO_CMD,
645 UndoOp::Redo => REDO_CMD,
646 }
647 }
648}
649
650pub enum UndoFullOp<'r> {
658 Init {
663 redo: &'r mut bool,
668 },
669
670 Op(UndoOp),
672 Info {
674 info: &'r mut Option<Arc<dyn UndoInfo>>,
678 },
679 Merge {
681 next_data: &'r mut dyn Any,
686
687 prev_timestamp: DInstant,
689
690 within_undo_interval: bool,
695
696 merged: &'r mut bool,
699 },
700}
701impl fmt::Debug for UndoFullOp<'_> {
702 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
703 match self {
704 Self::Init { .. } => f.debug_struct("Init").finish_non_exhaustive(),
705 Self::Op(arg0) => f.debug_tuple("Op").field(arg0).finish(),
706 Self::Info { .. } => f.debug_struct("Info").finish_non_exhaustive(),
707 Self::Merge { .. } => f.debug_struct("Merge").finish_non_exhaustive(),
708 }
709 }
710}
711
712#[must_use = "dropping the transaction undoes all captured actions"]
716pub struct UndoTransaction {
717 undo: Vec<UndoEntry>,
718}
719impl UndoTransaction {
720 pub fn is_empty(&self) -> bool {
722 self.undo.is_empty()
723 }
724
725 pub fn commit(mut self) {
727 let mut undo = mem::take(&mut self.undo);
728 let now = INSTANT.now();
729 for u in &mut undo {
730 u.timestamp = now;
731 }
732 let ctx = UNDO_SCOPE_CTX.get();
733 let mut ctx_undo = ctx.undo.lock();
734 if ctx_undo.is_empty() {
735 *ctx_undo = undo;
736 } else {
737 ctx_undo.extend(undo);
738 }
739 }
740
741 pub fn commit_group(mut self, info: impl UndoInfo) {
746 UNDO.register(UndoGroup {
747 info: info.into_dyn(),
748 undo: mem::take(&mut self.undo),
749 })
750 }
751
752 pub fn undo(self) {
756 let _ = self;
757 }
758}
759impl Drop for UndoTransaction {
760 fn drop(&mut self) {
761 for undo in self.undo.drain(..).rev() {
762 let _ = undo.action.undo();
763 }
764 }
765}
766
767command! {
768 pub static UNDO_CMD = {
783 l10n!: true,
784 name: "Undo",
785 shortcut: [shortcut!(CTRL+'Z')],
786 icon: wgt_fn!(|_| ICONS.get("undo")),
787 };
788
789 pub static REDO_CMD = {
799 l10n!: true,
800 name: "Redo",
801 shortcut: [shortcut!(CTRL+'Y')],
802 icon: wgt_fn!(|_| ICONS.get("redo")),
803 };
804
805 pub static CLEAR_HISTORY_CMD = {
811 l10n!: true,
812 name: "Clear History",
813 };
814}
815
816pub struct WidgetUndoScope(Option<Arc<UndoScope>>);
822impl WidgetUndoScope {
823 pub const fn new() -> Self {
825 Self(None)
826 }
827
828 pub fn is_inited(&self) -> bool {
830 self.0.is_some()
831 }
832
833 pub fn init(&mut self) {
837 let mut scope = UndoScope::default();
838 let id = WIDGET.id();
839 *scope.id.get_mut() = Some(id);
840
841 let scope = Arc::new(scope);
842 let wk_scope = Arc::downgrade(&scope);
843 let interval = UNDO_INTERVAL_VAR.actual_var();
844
845 UNDO_CMD
846 .scoped(id)
847 .with_meta(|m| m.set(*WEAK_UNDO_SCOPE_ID, (wk_scope.clone(), interval.clone())));
848 REDO_CMD.scoped(id).with_meta(|m| m.set(*WEAK_UNDO_SCOPE_ID, (wk_scope, interval)));
849
850 self.0 = Some(scope);
851 }
852
853 pub fn info(&mut self, info: &mut WidgetInfoBuilder) {
857 info.flag_meta(*FOCUS_SCOPE_ID);
858 }
859
860 pub fn deinit(&mut self) {
866 self.0 = None;
867 }
868
869 pub fn set_enabled(&mut self, enabled: bool) {
873 self.0.as_ref().unwrap().enabled.store(enabled, Ordering::Relaxed);
874 }
875
876 pub fn can_undo(&self) -> bool {
878 !self.0.as_ref().unwrap().undo.lock().is_empty()
879 }
880
881 pub fn can_redo(&self) -> bool {
883 !self.0.as_ref().unwrap().redo.lock().is_empty()
884 }
885}
886impl Default for WidgetUndoScope {
887 fn default() -> Self {
888 Self::new()
889 }
890}
891
892struct UndoScope {
893 id: Atomic<Option<WidgetId>>,
894 undo: Mutex<Vec<UndoEntry>>,
895 redo: Mutex<Vec<RedoEntry>>,
896 enabled: AtomicBool,
897}
898impl Default for UndoScope {
899 fn default() -> Self {
900 Self {
901 id: Default::default(),
902 undo: Default::default(),
903 redo: Default::default(),
904 enabled: AtomicBool::new(true),
905 }
906 }
907}
908impl UndoScope {
909 fn with_enabled_undo_redo(&self, f: impl FnOnce(&mut Vec<UndoEntry>, &mut Vec<RedoEntry>)) {
910 let mut undo = self.undo.lock();
911 let mut redo = self.redo.lock();
912
913 let max_undo = if self.enabled.load(Ordering::Relaxed) {
914 UNDO_LIMIT_VAR.get() as usize
915 } else {
916 0
917 };
918
919 if undo.len() > max_undo {
920 undo.reverse();
921 while undo.len() > max_undo {
922 undo.pop();
923 }
924 undo.reverse();
925 }
926
927 if redo.len() > max_undo {
928 redo.reverse();
929 while redo.len() > max_undo {
930 redo.pop();
931 }
932 redo.reverse();
933 }
934
935 if max_undo > 0 {
936 f(&mut undo, &mut redo);
937 }
938 }
939
940 fn register(&self, action: Box<dyn UndoAction>) {
941 self.with_enabled_undo_redo(|undo, redo| {
942 let now = INSTANT.now();
943 if let Some(prev) = undo.pop() {
944 match prev.action.merge(UndoActionMergeArgs {
945 next: action,
946 prev_timestamp: prev.timestamp,
947 within_undo_interval: now.duration_since(prev.timestamp) <= UNDO_SV.read().undo_interval.get(),
948 }) {
949 Ok(merged) => undo.push(UndoEntry {
950 timestamp: now,
951 action: merged,
952 }),
953 Err((p, action)) => {
954 undo.push(UndoEntry {
955 timestamp: prev.timestamp,
956 action: p,
957 });
958 undo.push(UndoEntry { timestamp: now, action });
959 }
960 }
961 } else {
962 undo.push(UndoEntry { timestamp: now, action });
963 }
964 redo.clear();
965 });
966 }
967
968 fn undo_select(&self, selector: impl UndoSelector) {
969 let mut actions = vec![];
970
971 self.with_enabled_undo_redo(|undo, _| {
972 let mut select = selector.select(UndoOp::Undo);
973 while let Some(entry) = undo.last() {
974 if select.include(entry.timestamp) {
975 actions.push(undo.pop().unwrap());
976 } else {
977 break;
978 }
979 }
980 });
981
982 for undo in actions {
983 let redo = undo.action.undo();
984 self.redo.lock().push(RedoEntry {
985 timestamp: undo.timestamp,
986 action: redo,
987 });
988 }
989 }
990
991 fn redo_select(&self, selector: impl UndoSelector) {
992 let mut actions = vec![];
993
994 self.with_enabled_undo_redo(|_, redo| {
995 let mut select = selector.select(UndoOp::Redo);
996 while let Some(entry) = redo.last() {
997 if select.include(entry.timestamp) {
998 actions.push(redo.pop().unwrap());
999 } else {
1000 break;
1001 }
1002 }
1003 });
1004
1005 for redo in actions {
1006 let undo = redo.action.redo();
1007 self.undo.lock().push(UndoEntry {
1008 timestamp: redo.timestamp,
1009 action: undo,
1010 });
1011 }
1012 }
1013
1014 fn id(&self) -> Option<WidgetId> {
1015 self.id.load(Ordering::Relaxed)
1016 }
1017}
1018
1019struct UndoEntry {
1020 timestamp: DInstant,
1021 action: Box<dyn UndoAction>,
1022}
1023
1024struct RedoEntry {
1025 pub timestamp: DInstant,
1026 pub action: Box<dyn RedoAction>,
1027}
1028
1029struct UndoGroup {
1030 info: Arc<dyn UndoInfo>,
1031 undo: Vec<UndoEntry>,
1032}
1033impl UndoAction for UndoGroup {
1034 fn undo(self: Box<Self>) -> Box<dyn RedoAction> {
1035 let mut redo = Vec::with_capacity(self.undo.len());
1036 for undo in self.undo.into_iter().rev() {
1037 redo.push(RedoEntry {
1038 timestamp: undo.timestamp,
1039 action: undo.action.undo(),
1040 });
1041 }
1042 Box::new(RedoGroup { info: self.info, redo })
1043 }
1044
1045 fn info(&mut self) -> Arc<dyn UndoInfo> {
1046 self.info.clone()
1047 }
1048
1049 fn as_any(&mut self) -> &mut dyn Any {
1050 self
1051 }
1052
1053 fn merge(self: Box<Self>, args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)> {
1054 Err((self, args.next))
1055 }
1056}
1057struct RedoGroup {
1058 info: Arc<dyn UndoInfo>,
1059 redo: Vec<RedoEntry>,
1060}
1061impl RedoAction for RedoGroup {
1062 fn redo(self: Box<Self>) -> Box<dyn UndoAction> {
1063 let mut undo = Vec::with_capacity(self.redo.len());
1064 for redo in self.redo.into_iter().rev() {
1065 undo.push(UndoEntry {
1066 timestamp: redo.timestamp,
1067 action: redo.action.redo(),
1068 });
1069 }
1070 Box::new(UndoGroup { info: self.info, undo })
1071 }
1072
1073 fn info(&mut self) -> Arc<dyn UndoInfo> {
1074 self.info.clone()
1075 }
1076}
1077
1078struct UndoRedoOp {
1079 info: Arc<dyn UndoInfo>,
1080 op: Box<dyn FnMut(UndoOp) + Send>,
1081}
1082impl UndoAction for UndoRedoOp {
1083 fn undo(mut self: Box<Self>) -> Box<dyn RedoAction> {
1084 (self.op)(UndoOp::Undo);
1085 self
1086 }
1087
1088 fn info(&mut self) -> Arc<dyn UndoInfo> {
1089 self.info.clone()
1090 }
1091
1092 fn as_any(&mut self) -> &mut dyn Any {
1093 self
1094 }
1095
1096 fn merge(self: Box<Self>, args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)> {
1097 Err((self, args.next))
1098 }
1099}
1100impl RedoAction for UndoRedoOp {
1101 fn redo(mut self: Box<Self>) -> Box<dyn UndoAction> {
1102 (self.op)(UndoOp::Redo);
1103 self
1104 }
1105
1106 fn info(&mut self) -> Arc<dyn UndoInfo> {
1107 self.info.clone()
1108 }
1109}
1110
1111struct UndoRedoFullOp {
1112 data: Box<dyn Any + Send>,
1113 op: Box<dyn FnMut(&mut dyn Any, UndoFullOp) + Send>,
1114}
1115impl UndoAction for UndoRedoFullOp {
1116 fn info(&mut self) -> Arc<dyn UndoInfo> {
1117 let mut info = None;
1118 (self.op)(&mut self.data, UndoFullOp::Info { info: &mut info });
1119 info.unwrap_or_else(|| Arc::new("action"))
1120 }
1121
1122 fn undo(mut self: Box<Self>) -> Box<dyn RedoAction> {
1123 (self.op)(&mut self.data, UndoFullOp::Op(UndoOp::Undo));
1124 self
1125 }
1126
1127 fn merge(mut self: Box<Self>, mut args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)>
1128 where
1129 Self: Sized,
1130 {
1131 if let Some(u) = args.next.as_any().downcast_mut::<Self>() {
1132 let mut merged = false;
1133 (self.op)(
1134 &mut self.data,
1135 UndoFullOp::Merge {
1136 next_data: &mut u.data,
1137 prev_timestamp: args.prev_timestamp,
1138 within_undo_interval: args.within_undo_interval,
1139 merged: &mut merged,
1140 },
1141 );
1142 if merged { Ok(self) } else { Err((self, args.next)) }
1143 } else {
1144 Err((self, args.next))
1145 }
1146 }
1147
1148 fn as_any(&mut self) -> &mut dyn Any {
1149 self
1150 }
1151}
1152impl RedoAction for UndoRedoFullOp {
1153 fn info(&mut self) -> Arc<dyn UndoInfo> {
1154 let mut info = None;
1155 (self.op)(&mut self.data, UndoFullOp::Info { info: &mut info });
1156 info.unwrap_or_else(|| Arc::new("action"))
1157 }
1158
1159 fn redo(mut self: Box<Self>) -> Box<dyn UndoAction> {
1160 (self.op)(&mut self.data, UndoFullOp::Op(UndoOp::Redo));
1161 self
1162 }
1163}
1164
1165struct UndoService {
1166 undo_limit: BoxedVar<u32>,
1167 undo_interval: BoxedVar<Duration>,
1168}
1169
1170impl Default for UndoService {
1171 fn default() -> Self {
1172 Self {
1173 undo_limit: var(u32::MAX).boxed(),
1174 undo_interval: KEYBOARD.repeat_config().map(|c| c.start_delay + c.interval).cow().boxed(),
1175 }
1176 }
1177}
1178
1179context_local! {
1180 static UNDO_SCOPE_CTX: UndoScope = UndoScope::default();
1181}
1182app_local! {
1183 static UNDO_SV: UndoService = UndoService::default();
1184}
1185
1186pub trait WidgetInfoUndoExt {
1188 fn is_undo_scope(&self) -> bool;
1190
1191 fn undo_scope(&self) -> Option<WidgetInfo>;
1193}
1194impl WidgetInfoUndoExt for WidgetInfo {
1195 fn is_undo_scope(&self) -> bool {
1196 self.meta().flagged(*FOCUS_SCOPE_ID)
1197 }
1198
1199 fn undo_scope(&self) -> Option<WidgetInfo> {
1200 self.ancestors().find(WidgetInfoUndoExt::is_undo_scope)
1201 }
1202}
1203
1204static_id! {
1205 static ref FOCUS_SCOPE_ID: StateId<()>;
1206}
1207
1208pub trait CommandUndoExt {
1210 fn undo_scoped(self) -> BoxedVar<Command>;
1213
1214 fn undo_stack(self) -> UndoStackInfo;
1216 fn redo_stack(self) -> UndoStackInfo;
1218}
1219impl CommandUndoExt for Command {
1220 fn undo_scoped(self) -> BoxedVar<Command> {
1221 self.focus_scoped_with(|w| match w {
1222 Some(w) => {
1223 if w.is_undo_scope() {
1224 CommandScope::Widget(w.id())
1225 } else if let Some(scope) = w.undo_scope() {
1226 CommandScope::Widget(scope.id())
1227 } else {
1228 CommandScope::App
1229 }
1230 }
1231 None => CommandScope::App,
1232 })
1233 }
1234
1235 fn undo_stack(self) -> UndoStackInfo {
1236 let scope = self.with_meta(|m| m.get(*WEAK_UNDO_SCOPE_ID));
1237 if let Some(scope) = scope {
1238 if let Some(s) = scope.0.upgrade() {
1239 return UndoStackInfo::undo(&s, scope.1.get());
1240 }
1241 }
1242
1243 if let CommandScope::App = self.scope() {
1244 let mut r = UNDO_SCOPE_CTX.with_default(|| UNDO.undo_stack());
1245 r.undo_interval = UNDO.undo_interval().get();
1246 return r;
1247 }
1248
1249 UndoStackInfo {
1250 stack: vec![],
1251 undo_interval: Duration::ZERO,
1252 }
1253 }
1254
1255 fn redo_stack(self) -> UndoStackInfo {
1256 let scope = self.with_meta(|m| m.get(*WEAK_UNDO_SCOPE_ID));
1257 if let Some(scope) = scope {
1258 if let Some(s) = scope.0.upgrade() {
1259 return UndoStackInfo::redo(&s, scope.1.get());
1260 }
1261 }
1262
1263 if let CommandScope::App = self.scope() {
1264 let mut r = UNDO_SCOPE_CTX.with_default(|| UNDO.redo_stack());
1265 r.undo_interval = UNDO.undo_interval().get();
1266 return r;
1267 }
1268
1269 UndoStackInfo {
1270 stack: vec![],
1271 undo_interval: Duration::ZERO,
1272 }
1273 }
1274}
1275
1276static_id! {
1277 static ref WEAK_UNDO_SCOPE_ID: StateId<(std::sync::Weak<UndoScope>, BoxedVar<Duration>)>;
1278}
1279
1280pub trait UndoSelector: crate::private::Sealed {
1286 type Select: UndoSelect;
1288
1289 fn select(self, op: UndoOp) -> Self::Select;
1291}
1292
1293pub trait UndoSelect {
1295 fn include(&mut self, timestamp: DInstant) -> bool;
1300}
1301impl crate::private::Sealed for u32 {}
1302impl UndoSelector for u32 {
1303 type Select = u32;
1304
1305 fn select(self, op: UndoOp) -> Self::Select {
1306 let _ = op;
1307 self
1308 }
1309}
1310impl UndoSelect for u32 {
1311 fn include(&mut self, _: DInstant) -> bool {
1312 let i = *self > 0;
1313 if i {
1314 *self -= 1;
1315 }
1316 i
1317 }
1318}
1319impl crate::private::Sealed for Duration {}
1320impl UndoSelector for Duration {
1321 type Select = UndoSelectInterval;
1322
1323 fn select(self, op: UndoOp) -> Self::Select {
1324 UndoSelectInterval {
1325 prev: None,
1326 interval: self,
1327 op,
1328 }
1329 }
1330}
1331#[doc(hidden)]
1332pub struct UndoSelectInterval {
1333 prev: Option<DInstant>,
1334 interval: Duration,
1335 op: UndoOp,
1336}
1337impl UndoSelect for UndoSelectInterval {
1338 fn include(&mut self, timestamp: DInstant) -> bool {
1339 if let Some(prev) = &mut self.prev {
1340 let (older, newer) = match self.op {
1341 UndoOp::Undo => (timestamp, *prev),
1342 UndoOp::Redo => (*prev, timestamp),
1343 };
1344 if newer.saturating_duration_since(older) <= self.interval {
1345 *prev = timestamp;
1346 true
1347 } else {
1348 false
1349 }
1350 } else {
1351 self.prev = Some(timestamp);
1352 true
1353 }
1354 }
1355}
1356impl crate::private::Sealed for DInstant {}
1357impl UndoSelector for DInstant {
1358 type Select = UndoSelectLtEq;
1359
1360 fn select(self, op: UndoOp) -> Self::Select {
1361 UndoSelectLtEq { instant: self, op }
1362 }
1363}
1364#[doc(hidden)]
1365pub struct UndoSelectLtEq {
1366 instant: DInstant,
1367 op: UndoOp,
1368}
1369impl UndoSelect for UndoSelectLtEq {
1370 fn include(&mut self, timestamp: DInstant) -> bool {
1371 match self.op {
1372 UndoOp::Undo => timestamp >= self.instant,
1373 UndoOp::Redo => timestamp <= self.instant,
1374 }
1375 }
1376}
1377
1378#[cfg(test)]
1379mod tests {
1380 use zng_app::APP;
1381
1382 use super::*;
1383
1384 #[test]
1385 fn register() {
1386 let _a = APP.minimal();
1387 let data = Arc::new(Mutex::new(vec![1, 2]));
1388
1389 UNDO.register(PushAction {
1390 data: data.clone(),
1391 item: 1,
1392 });
1393 UNDO.register(PushAction {
1394 data: data.clone(),
1395 item: 2,
1396 });
1397 assert_eq!(&[1, 2], &data.lock()[..]);
1398
1399 UNDO.undo_select(1);
1400 assert_eq!(&[1], &data.lock()[..]);
1401 UNDO.undo_select(1);
1402 assert_eq!(&[] as &[u8], &data.lock()[..]);
1403
1404 UNDO.redo_select(1);
1405 assert_eq!(&[1], &data.lock()[..]);
1406 UNDO.redo_select(1);
1407 assert_eq!(&[1, 2], &data.lock()[..]);
1408 }
1409
1410 fn push_1_2(data: &Arc<Mutex<Vec<u8>>>) {
1411 UNDO.run_op(
1412 "push 1",
1413 clmv!(data, |op| match op {
1414 UndoOp::Undo => assert_eq!(data.lock().pop(), Some(1)),
1415 UndoOp::Redo => data.lock().push(1),
1416 }),
1417 );
1418 UNDO.run_op(
1419 "push 2",
1420 clmv!(data, |op| match op {
1421 UndoOp::Undo => assert_eq!(data.lock().pop(), Some(2)),
1422 UndoOp::Redo => data.lock().push(2),
1423 }),
1424 );
1425 }
1426
1427 #[test]
1428 fn run_op() {
1429 let _a = APP.minimal();
1430 let data = Arc::new(Mutex::new(vec![]));
1431
1432 push_1_2(&data);
1433 assert_eq!(&[1, 2], &data.lock()[..]);
1434
1435 UNDO.undo_select(1);
1436 assert_eq!(&[1], &data.lock()[..]);
1437 UNDO.undo_select(1);
1438 assert_eq!(&[] as &[u8], &data.lock()[..]);
1439
1440 UNDO.redo_select(1);
1441 assert_eq!(&[1], &data.lock()[..]);
1442 UNDO.redo_select(1);
1443 assert_eq!(&[1, 2], &data.lock()[..]);
1444 }
1445
1446 #[test]
1447 fn transaction_undo() {
1448 let _a = APP.minimal();
1449 let data = Arc::new(Mutex::new(vec![]));
1450
1451 let t = UNDO.transaction(|| {
1452 push_1_2(&data);
1453 });
1454
1455 assert_eq!(&[1, 2], &data.lock()[..]);
1456 UNDO.undo_select(1);
1457 assert_eq!(&[1, 2], &data.lock()[..]);
1458
1459 t.undo();
1460 assert_eq!(&[] as &[u8], &data.lock()[..]);
1461 }
1462
1463 #[test]
1464 fn transaction_commit() {
1465 let _a = APP.minimal();
1466 let data = Arc::new(Mutex::new(vec![]));
1467
1468 let t = UNDO.transaction(|| {
1469 push_1_2(&data);
1470 });
1471
1472 assert_eq!(&[1, 2], &data.lock()[..]);
1473 UNDO.undo_select(1);
1474 assert_eq!(&[1, 2], &data.lock()[..]);
1475
1476 t.commit();
1477
1478 UNDO.undo_select(1);
1479 assert_eq!(&[1], &data.lock()[..]);
1480 UNDO.undo_select(1);
1481 assert_eq!(&[] as &[u8], &data.lock()[..]);
1482
1483 UNDO.redo_select(1);
1484 assert_eq!(&[1], &data.lock()[..]);
1485 UNDO.redo_select(1);
1486 assert_eq!(&[1, 2], &data.lock()[..]);
1487 }
1488
1489 #[test]
1490 fn transaction_group() {
1491 let _a = APP.minimal();
1492 let data = Arc::new(Mutex::new(vec![]));
1493
1494 let t = UNDO.transaction(|| {
1495 push_1_2(&data);
1496 });
1497
1498 assert_eq!(&[1, 2], &data.lock()[..]);
1499 UNDO.undo_select(1);
1500 assert_eq!(&[1, 2], &data.lock()[..]);
1501
1502 t.commit_group("push 1, 2");
1503
1504 UNDO.undo_select(1);
1505 assert_eq!(&[] as &[u8], &data.lock()[..]);
1506
1507 UNDO.redo_select(1);
1508 assert_eq!(&[1, 2], &data.lock()[..]);
1509 }
1510
1511 fn push_1_sleep_2(data: &Arc<Mutex<Vec<u8>>>) {
1512 UNDO.run_op(
1513 "push 1",
1514 clmv!(data, |op| match op {
1515 UndoOp::Undo => assert_eq!(data.lock().pop(), Some(1)),
1516 UndoOp::Redo => data.lock().push(1),
1517 }),
1518 );
1519 std::thread::sleep(Duration::from_millis(100));
1520 UNDO.run_op(
1521 "push 2",
1522 clmv!(data, |op| match op {
1523 UndoOp::Undo => assert_eq!(data.lock().pop(), Some(2)),
1524 UndoOp::Redo => data.lock().push(2),
1525 }),
1526 );
1527 }
1528
1529 #[test]
1530 fn undo_redo_t_zero() {
1531 let _a = APP.minimal();
1532 let data = Arc::new(Mutex::new(vec![]));
1533
1534 push_1_sleep_2(&data);
1535 assert_eq!(&[1, 2], &data.lock()[..]);
1536
1537 UNDO.undo_select(Duration::ZERO);
1538 assert_eq!(&[1], &data.lock()[..]);
1539 UNDO.undo_select(Duration::ZERO);
1540 assert_eq!(&[] as &[u8], &data.lock()[..]);
1541
1542 UNDO.redo_select(Duration::ZERO);
1543 assert_eq!(&[1], &data.lock()[..]);
1544 UNDO.redo_select(Duration::ZERO);
1545 assert_eq!(&[1, 2], &data.lock()[..]);
1546 }
1547
1548 #[test]
1549 fn undo_redo_t_max() {
1550 undo_redo_t_large(Duration::MAX);
1551 }
1552
1553 #[test]
1554 fn undo_redo_t_10s() {
1555 undo_redo_t_large(Duration::from_secs(10));
1556 }
1557
1558 fn undo_redo_t_large(t: Duration) {
1559 let _a = APP.minimal();
1560 let data = Arc::new(Mutex::new(vec![]));
1561
1562 push_1_sleep_2(&data);
1563 assert_eq!(&[1, 2], &data.lock()[..]);
1564
1565 UNDO.undo_select(t);
1566 assert_eq!(&[] as &[u8], &data.lock()[..]);
1567
1568 UNDO.redo_select(t);
1569 assert_eq!(&[1, 2], &data.lock()[..]);
1570 }
1571
1572 #[test]
1573 fn watch_var() {
1574 let mut app = APP.minimal().run_headless(false);
1575
1576 let test_var = var(0);
1577 UNDO.watch_var("set test var", test_var.clone()).perm();
1578
1579 test_var.set(10);
1580 app.update(false).assert_wait();
1581
1582 test_var.set(20);
1583 app.update(false).assert_wait();
1584
1585 assert_eq!(20, test_var.get());
1586
1587 UNDO.undo_select(1);
1588 app.update(false).assert_wait();
1589 assert_eq!(10, test_var.get());
1590
1591 UNDO.undo_select(1);
1592 app.update(false).assert_wait();
1593 assert_eq!(0, test_var.get());
1594
1595 UNDO.redo_select(1);
1596 app.update(false).assert_wait();
1597 assert_eq!(10, test_var.get());
1598
1599 UNDO.redo_select(1);
1600 app.update(false).assert_wait();
1601 assert_eq!(20, test_var.get());
1602 }
1603
1604 struct PushAction {
1605 data: Arc<Mutex<Vec<u8>>>,
1606 item: u8,
1607 }
1608 impl UndoAction for PushAction {
1609 fn undo(self: Box<Self>) -> Box<dyn RedoAction> {
1610 assert_eq!(self.data.lock().pop(), Some(self.item));
1611 self
1612 }
1613
1614 fn info(&mut self) -> Arc<dyn UndoInfo> {
1615 Arc::new("push")
1616 }
1617
1618 fn as_any(&mut self) -> &mut dyn Any {
1619 self
1620 }
1621
1622 fn merge(self: Box<Self>, args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)> {
1623 Err((self, args.next))
1624 }
1625 }
1626 impl RedoAction for PushAction {
1627 fn redo(self: Box<Self>) -> Box<dyn UndoAction> {
1628 self.data.lock().push(self.item);
1629 self
1630 }
1631
1632 fn info(&mut self) -> Arc<dyn UndoInfo> {
1633 Arc::new("push")
1634 }
1635 }
1636}