1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/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 APP, AppExtension, DInstant, INSTANT,
29 event::{AnyEventArgs, Command, CommandNameExt, CommandScope, command},
30 shortcut::{CommandShortcutExt, shortcut},
31 update::EventUpdate,
32 widget::{
33 WIDGET, WidgetId,
34 info::{WidgetInfo, WidgetInfoBuilder},
35 },
36};
37use zng_app_context::{RunOnDrop, app_local, context_local};
38use zng_clone_move::clmv;
39use zng_ext_input::{focus::cmd::CommandFocusExt, keyboard::KEYBOARD};
40use zng_state_map::{StateId, StateMapRef, static_id};
41use zng_txt::Txt;
42use zng_var::{Var, VarHandle, VarValue, context_var, var};
43use zng_wgt::{CommandIconExt as _, ICONS, wgt_fn};
44
45mod private {
46 pub trait Sealed {}
48}
49
50#[derive(Default)]
58pub struct UndoManager {}
59
60impl AppExtension for UndoManager {
61 fn event(&mut self, update: &mut EventUpdate) {
62 if let Some(args) = UNDO_CMD.on_unhandled(update) {
64 args.propagation().stop();
65 if let Some(c) = args.param::<u32>() {
66 UNDO.undo_select(*c);
67 } else if let Some(i) = args.param::<Duration>() {
68 UNDO.undo_select(*i);
69 } else if let Some(t) = args.param::<DInstant>() {
70 UNDO.undo_select(*t);
71 } else {
72 UNDO.undo();
73 }
74 } else if let Some(args) = REDO_CMD.on_unhandled(update) {
75 args.propagation().stop();
76 if let Some(c) = args.param::<u32>() {
77 UNDO.redo_select(*c);
78 } else if let Some(i) = args.param::<Duration>() {
79 UNDO.redo_select(*i);
80 } else if let Some(t) = args.param::<DInstant>() {
81 UNDO.redo_select(*t);
82 } else {
83 UNDO.redo();
84 }
85 }
86 }
87}
88
89context_var! {
90 pub static UNDO_LIMIT_VAR: u32 = UNDO.undo_limit();
96
97 pub static UNDO_INTERVAL_VAR: Duration = UNDO.undo_interval();
103}
104
105pub struct UNDO;
111impl UNDO {
112 pub fn undo_limit(&self) -> Var<u32> {
119 UNDO_SV.read().undo_limit.clone()
120 }
121
122 pub fn undo_interval(&self) -> Var<Duration> {
136 UNDO_SV.read().undo_interval.clone()
137 }
138
139 pub fn is_enabled(&self) -> bool {
145 UNDO_SCOPE_CTX.get().enabled.load(Ordering::Relaxed) && UNDO_SV.read().undo_limit.get() > 0
146 }
147
148 pub fn undo_select(&self, selector: impl UndoSelector) {
158 UNDO_SCOPE_CTX.get().undo_select(selector);
159 }
160
161 pub fn redo_select(&self, selector: impl UndoSelector) {
163 UNDO_SCOPE_CTX.get().redo_select(selector);
164 }
165
166 pub fn undo(&self) {
170 self.undo_select(UNDO_INTERVAL_VAR.get());
171 }
172
173 pub fn redo(&self) {
177 self.redo_select(UNDO_INTERVAL_VAR.get());
178 }
179
180 pub fn scope(&self) -> Option<WidgetId> {
183 UNDO_SCOPE_CTX.get().id()
184 }
185
186 pub fn register(&self, action: impl UndoAction) {
188 UNDO_SCOPE_CTX.get().register(Box::new(action))
189 }
190
191 pub fn register_op(&self, info: impl UndoInfo, op: impl FnMut(UndoOp) + Send + 'static) {
195 self.register(UndoRedoOp {
196 info: info.into_dyn(),
197 op: Box::new(op),
198 })
199 }
200
201 pub fn register_full_op<D>(&self, data: D, mut op: impl FnMut(&mut D, UndoFullOp) + Send + 'static)
205 where
206 D: Any + Send + 'static,
207 {
208 self.register(UndoRedoFullOp {
209 data: Box::new(data),
210 op: Box::new(move |d, o| {
211 op(d.downcast_mut::<D>().unwrap(), o);
212 }),
213 })
214 }
215
216 pub fn run(&self, action: impl RedoAction) {
218 UNDO_SCOPE_CTX.get().register(Box::new(action).redo())
219 }
220
221 pub fn run_op(&self, info: impl UndoInfo, op: impl FnMut(UndoOp) + Send + 'static) {
223 self.run(UndoRedoOp {
224 info: info.into_dyn(),
225 op: Box::new(op),
226 })
227 }
228
229 pub fn run_full_op<D>(&self, mut data: D, mut op: impl FnMut(&mut D, UndoFullOp) + Send + 'static)
231 where
232 D: Any + Send + 'static,
233 {
234 let mut redo = true;
235 op(&mut data, UndoFullOp::Init { redo: &mut redo });
236
237 if redo {
238 self.run(UndoRedoFullOp {
239 data: Box::new(data),
240 op: Box::new(move |d, o| {
241 op(d.downcast_mut::<D>().unwrap(), o);
242 }),
243 })
244 }
245 }
246
247 pub fn group(&self, info: impl UndoInfo, actions: impl FnOnce()) -> bool {
251 let t = self.transaction(actions);
252 let any = !t.is_empty();
253 if any {
254 t.commit_group(info);
255 }
256 any
257 }
258
259 pub fn transaction(&self, actions: impl FnOnce()) -> UndoTransaction {
264 let mut scope = UndoScope::default();
265 let parent_scope = UNDO_SCOPE_CTX.get();
266 *scope.enabled.get_mut() = parent_scope.enabled.load(Ordering::Relaxed);
267 *scope.id.get_mut() = parent_scope.id.load(Ordering::Relaxed);
268
269 let t_scope = Arc::new(scope);
270 let _panic_undo = RunOnDrop::new(clmv!(t_scope, || {
271 for undo in mem::take(&mut *t_scope.undo.lock()).into_iter().rev() {
272 let _ = undo.action.undo();
273 }
274 }));
275
276 let mut scope = Some(t_scope);
277 UNDO_SCOPE_CTX.with_context(&mut scope, actions);
278
279 let scope = scope.unwrap();
280 let undo = mem::take(&mut *scope.undo.lock());
281
282 UndoTransaction { undo }
283 }
284
285 pub fn try_group<O, E>(&self, info: impl UndoInfo, actions: impl FnOnce() -> Result<O, E>) -> Result<O, E> {
290 let mut r = None;
291 let t = self.transaction(|| r = Some(actions()));
292 let r = r.unwrap();
293 if !t.is_empty() {
294 if r.is_ok() {
295 t.commit_group(info);
296 } else {
297 t.undo();
298 }
299 }
300 r
301 }
302
303 pub fn try_commit<O, E>(&self, actions: impl FnOnce() -> Result<O, E>) -> Result<O, E> {
307 let mut r = None;
308 let t = self.transaction(|| r = Some(actions()));
309 let r = r.unwrap();
310 if !t.is_empty() {
311 if r.is_ok() {
312 t.commit();
313 } else {
314 t.undo();
315 }
316 }
317 r
318 }
319
320 pub fn with_scope<R>(&self, scope: &mut WidgetUndoScope, f: impl FnOnce() -> R) -> R {
322 UNDO_SCOPE_CTX.with_context(&mut scope.0, f)
323 }
324
325 pub fn with_disabled<R>(&self, f: impl FnOnce() -> R) -> R {
327 let mut scope = UndoScope::default();
328 let parent_scope = UNDO_SCOPE_CTX.get();
329 *scope.enabled.get_mut() = false;
330 *scope.id.get_mut() = parent_scope.id.load(Ordering::Relaxed);
331
332 UNDO_SCOPE_CTX.with_context(&mut Some(Arc::new(scope)), f)
333 }
334
335 pub fn watch_var<T: VarValue>(&self, info: impl UndoInfo, var: Var<T>) -> VarHandle {
345 if var.capabilities().is_always_read_only() {
346 return VarHandle::dummy();
347 }
348 let var = var.current_context();
349 let wk_var = var.downgrade();
350
351 let mut prev_value = Some(var.get());
352 let info = info.into_dyn();
353
354 var.trace_value(move |args| {
355 if args.downcast_tags::<UndoVarModifyTag>().next().is_none() {
356 let prev = prev_value.take().unwrap();
357 let new = args.value();
358 if &prev == new {
359 prev_value = Some(prev);
361 return;
362 }
363 prev_value = Some(new.clone());
364 UNDO.register_op(
365 info.clone(),
366 clmv!(wk_var, new, |op| if let Some(var) = wk_var.upgrade() {
367 match op {
368 UndoOp::Undo => var.modify(clmv!(prev, |args| {
369 args.set(prev);
370 args.push_tag(UndoVarModifyTag);
371 })),
372 UndoOp::Redo => var.modify(clmv!(new, |args| {
373 args.set(new);
374 args.push_tag(UndoVarModifyTag);
375 })),
376 };
377 }),
378 );
379 }
380 })
381 }
382
383 pub fn clear_redo(&self) {
385 UNDO_SCOPE_CTX.get().redo.lock().clear();
386 }
387
388 pub fn clear(&self) {
390 let ctx = UNDO_SCOPE_CTX.get();
391 let mut u = ctx.undo.lock();
392 u.clear();
393 ctx.redo.lock().clear();
394 }
395
396 pub fn can_undo(&self) -> bool {
398 !UNDO_SCOPE_CTX.get().undo.lock().is_empty()
399 }
400
401 pub fn can_redo(&self) -> bool {
403 !UNDO_SCOPE_CTX.get().redo.lock().is_empty()
404 }
405
406 pub fn undo_stack(&self) -> UndoStackInfo {
410 UndoStackInfo::undo(&UNDO_SCOPE_CTX.get(), UNDO_INTERVAL_VAR.get())
411 }
412
413 pub fn redo_stack(&self) -> UndoStackInfo {
419 UndoStackInfo::redo(&UNDO_SCOPE_CTX.get(), UNDO_INTERVAL_VAR.get())
420 }
421}
422
423#[derive(Clone)]
425pub struct UndoStackInfo {
426 pub stack: Vec<(DInstant, Arc<dyn UndoInfo>)>,
434
435 pub undo_interval: Duration,
437}
438impl UndoStackInfo {
439 fn undo(ctx: &UndoScope, undo_interval: Duration) -> Self {
440 Self {
441 stack: ctx.undo.lock().iter_mut().map(|e| (e.timestamp, e.action.info())).collect(),
442 undo_interval,
443 }
444 }
445 fn redo(ctx: &UndoScope, undo_interval: Duration) -> Self {
446 Self {
447 stack: ctx.redo.lock().iter_mut().map(|e| (e.timestamp, e.action.info())).collect(),
448 undo_interval,
449 }
450 }
451
452 pub fn iter_groups(&self) -> impl DoubleEndedIterator<Item = &[(DInstant, Arc<dyn UndoInfo>)]> {
454 struct Iter<'a> {
455 stack: &'a [(DInstant, Arc<dyn UndoInfo>)],
456 interval: Duration,
457 ts_inverted: bool,
458 }
459 impl<'a> Iterator for Iter<'a> {
460 type Item = &'a [(DInstant, Arc<dyn UndoInfo>)];
461
462 fn next(&mut self) -> Option<Self::Item> {
463 if self.stack.is_empty() {
464 None
465 } else {
466 let mut older = self.stack[0].0;
467
468 let mut r = self.stack;
469
470 if let Some(i) = self.stack.iter().position(|(newer, _)| {
471 let (a, b) = if self.ts_inverted { (older, *newer) } else { (*newer, older) };
472 let break_ = a.saturating_duration_since(b) > self.interval;
473 older = *newer;
474 break_
475 }) {
476 r = &self.stack[..i];
477 self.stack = &self.stack[i..];
478 } else {
479 self.stack = &[];
480 }
481
482 Some(r)
483 }
484 }
485 }
486 impl DoubleEndedIterator for Iter<'_> {
487 fn next_back(&mut self) -> Option<Self::Item> {
488 if self.stack.is_empty() {
489 None
490 } else {
491 let mut newer = self.stack[self.stack.len() - 1].0;
492
493 let mut r = self.stack;
494
495 if let Some(i) = self.stack.iter().rposition(|(older, _)| {
496 let (a, b) = if self.ts_inverted { (*older, newer) } else { (newer, *older) };
497 let break_ = a.saturating_duration_since(b) > self.interval;
498 newer = *older;
499 break_
500 }) {
501 let i = i + 1;
502 r = &self.stack[i..];
503 self.stack = &self.stack[..i];
504 } else {
505 self.stack = &[];
506 }
507
508 Some(r)
509 }
510 }
511 }
512 Iter {
513 stack: &self.stack,
514 interval: self.undo_interval,
515 ts_inverted: self.stack.len() > 1 && self.stack[0].0 > self.stack[self.stack.len() - 1].0,
516 }
517 }
518}
519
520#[derive(Debug, Clone, Copy, PartialEq)]
526pub struct UndoVarModifyTag;
527
528pub trait UndoInfo: Send + Sync + Any {
530 fn description(&self) -> Txt;
532
533 fn meta(&self) -> StateMapRef<'_, UNDO> {
538 StateMapRef::empty()
539 }
540
541 fn into_dyn(self) -> Arc<dyn UndoInfo>
543 where
544 Self: Sized,
545 {
546 Arc::new(self)
547 }
548}
549impl UndoInfo for Txt {
550 fn description(&self) -> Txt {
551 self.clone()
552 }
553}
554impl UndoInfo for Var<Txt> {
555 fn description(&self) -> Txt {
556 self.get()
557 }
558}
559impl UndoInfo for &'static str {
560 fn description(&self) -> Txt {
561 Txt::from_static(self)
562 }
563}
564impl UndoInfo for Arc<dyn UndoInfo> {
565 fn description(&self) -> Txt {
566 self.as_ref().description()
567 }
568
569 fn meta(&self) -> StateMapRef<'_, UNDO> {
570 self.as_ref().meta()
571 }
572
573 fn into_dyn(self) -> Arc<dyn UndoInfo>
574 where
575 Self: Sized,
576 {
577 self
578 }
579}
580pub trait UndoAction: Send + Any {
582 fn info(&mut self) -> Arc<dyn UndoInfo>;
584
585 fn undo(self: Box<Self>) -> Box<dyn RedoAction>;
587
588 fn as_any(&mut self) -> &mut dyn Any;
590
591 fn merge(self: Box<Self>, args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)>;
603}
604
605pub struct UndoActionMergeArgs {
607 pub next: Box<dyn UndoAction>,
609
610 pub prev_timestamp: DInstant,
612
613 pub within_undo_interval: bool,
618}
619
620pub trait RedoAction: Send + Any {
622 fn info(&mut self) -> Arc<dyn UndoInfo>;
624
625 fn redo(self: Box<Self>) -> Box<dyn UndoAction>;
627}
628
629#[derive(Debug, Clone, Copy, PartialEq, Eq)]
637pub enum UndoOp {
638 Undo,
640 Redo,
642}
643impl UndoOp {
644 pub fn cmd(self) -> Command {
646 match self {
647 UndoOp::Undo => UNDO_CMD,
648 UndoOp::Redo => REDO_CMD,
649 }
650 }
651}
652
653pub enum UndoFullOp<'r> {
661 Init {
666 redo: &'r mut bool,
671 },
672
673 Op(UndoOp),
675 Info {
677 info: &'r mut Option<Arc<dyn UndoInfo>>,
681 },
682 Merge {
684 next_data: &'r mut dyn Any,
689
690 prev_timestamp: DInstant,
692
693 within_undo_interval: bool,
698
699 merged: &'r mut bool,
702 },
703}
704impl fmt::Debug for UndoFullOp<'_> {
705 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706 match self {
707 Self::Init { .. } => f.debug_struct("Init").finish_non_exhaustive(),
708 Self::Op(arg0) => f.debug_tuple("Op").field(arg0).finish(),
709 Self::Info { .. } => f.debug_struct("Info").finish_non_exhaustive(),
710 Self::Merge { .. } => f.debug_struct("Merge").finish_non_exhaustive(),
711 }
712 }
713}
714
715#[must_use = "dropping the transaction undoes all captured actions"]
719pub struct UndoTransaction {
720 undo: Vec<UndoEntry>,
721}
722impl UndoTransaction {
723 pub fn is_empty(&self) -> bool {
725 self.undo.is_empty()
726 }
727
728 pub fn commit(mut self) {
730 let mut undo = mem::take(&mut self.undo);
731 let now = INSTANT.now();
732 for u in &mut undo {
733 u.timestamp = now;
734 }
735 let ctx = UNDO_SCOPE_CTX.get();
736 let mut ctx_undo = ctx.undo.lock();
737 if ctx_undo.is_empty() {
738 *ctx_undo = undo;
739 } else {
740 ctx_undo.extend(undo);
741 }
742 }
743
744 pub fn commit_group(mut self, info: impl UndoInfo) {
749 UNDO.register(UndoGroup {
750 info: info.into_dyn(),
751 undo: mem::take(&mut self.undo),
752 })
753 }
754
755 pub fn undo(self) {
759 let _ = self;
760 }
761}
762impl Drop for UndoTransaction {
763 fn drop(&mut self) {
764 for undo in self.undo.drain(..).rev() {
765 let _ = undo.action.undo();
766 }
767 }
768}
769
770command! {
771 pub static UNDO_CMD = {
786 l10n!: true,
787 name: "Undo",
788 shortcut: [shortcut!(CTRL + 'Z')],
789 icon: wgt_fn!(|_| ICONS.get("undo")),
790 };
791
792 pub static REDO_CMD = {
802 l10n!: true,
803 name: "Redo",
804 shortcut: [shortcut!(CTRL + 'Y')],
805 icon: wgt_fn!(|_| ICONS.get("redo")),
806 };
807
808 pub static CLEAR_HISTORY_CMD = {
814 l10n!: true,
815 name: "Clear History",
816 };
817}
818
819pub struct WidgetUndoScope(Option<Arc<UndoScope>>);
825impl WidgetUndoScope {
826 pub const fn new() -> Self {
828 Self(None)
829 }
830
831 pub fn is_inited(&self) -> bool {
833 self.0.is_some()
834 }
835
836 pub fn init(&mut self) {
840 let mut scope = UndoScope::default();
841 let id = WIDGET.id();
842 *scope.id.get_mut() = Some(id);
843
844 let scope = Arc::new(scope);
845 let wk_scope = Arc::downgrade(&scope);
846 let interval = UNDO_INTERVAL_VAR.current_context();
847
848 UNDO_CMD
849 .scoped(id)
850 .with_meta(|m| m.set(*WEAK_UNDO_SCOPE_ID, (wk_scope.clone(), interval.clone())));
851 REDO_CMD.scoped(id).with_meta(|m| m.set(*WEAK_UNDO_SCOPE_ID, (wk_scope, interval)));
852
853 self.0 = Some(scope);
854 }
855
856 pub fn info(&mut self, info: &mut WidgetInfoBuilder) {
860 info.flag_meta(*FOCUS_SCOPE_ID);
861 }
862
863 pub fn deinit(&mut self) {
869 self.0 = None;
870 }
871
872 pub fn set_enabled(&mut self, enabled: bool) {
876 self.0.as_ref().unwrap().enabled.store(enabled, Ordering::Relaxed);
877 }
878
879 pub fn can_undo(&self) -> bool {
881 !self.0.as_ref().unwrap().undo.lock().is_empty()
882 }
883
884 pub fn can_redo(&self) -> bool {
886 !self.0.as_ref().unwrap().redo.lock().is_empty()
887 }
888}
889impl Default for WidgetUndoScope {
890 fn default() -> Self {
891 Self::new()
892 }
893}
894
895struct UndoScope {
896 id: Atomic<Option<WidgetId>>,
897 undo: Mutex<Vec<UndoEntry>>,
898 redo: Mutex<Vec<RedoEntry>>,
899 enabled: AtomicBool,
900}
901impl Default for UndoScope {
902 fn default() -> Self {
903 Self {
904 id: Default::default(),
905 undo: Default::default(),
906 redo: Default::default(),
907 enabled: AtomicBool::new(true),
908 }
909 }
910}
911impl UndoScope {
912 fn with_enabled_undo_redo(&self, f: impl FnOnce(&mut Vec<UndoEntry>, &mut Vec<RedoEntry>)) {
913 let mut undo = self.undo.lock();
914 let mut redo = self.redo.lock();
915
916 let max_undo = if self.enabled.load(Ordering::Relaxed) {
917 UNDO_LIMIT_VAR.get() as usize
918 } else {
919 0
920 };
921
922 if undo.len() > max_undo {
923 undo.reverse();
924 while undo.len() > max_undo {
925 undo.pop();
926 }
927 undo.reverse();
928 }
929
930 if redo.len() > max_undo {
931 redo.reverse();
932 while redo.len() > max_undo {
933 redo.pop();
934 }
935 redo.reverse();
936 }
937
938 if max_undo > 0 {
939 f(&mut undo, &mut redo);
940 }
941 }
942
943 fn register(&self, action: Box<dyn UndoAction>) {
944 self.with_enabled_undo_redo(|undo, redo| {
945 let now = INSTANT.now();
946 if let Some(prev) = undo.pop() {
947 match prev.action.merge(UndoActionMergeArgs {
948 next: action,
949 prev_timestamp: prev.timestamp,
950 within_undo_interval: now.duration_since(prev.timestamp) <= UNDO_SV.read().undo_interval.get(),
951 }) {
952 Ok(merged) => undo.push(UndoEntry {
953 timestamp: now,
954 action: merged,
955 }),
956 Err((p, action)) => {
957 undo.push(UndoEntry {
958 timestamp: prev.timestamp,
959 action: p,
960 });
961 undo.push(UndoEntry { timestamp: now, action });
962 }
963 }
964 } else {
965 undo.push(UndoEntry { timestamp: now, action });
966 }
967 redo.clear();
968 });
969 }
970
971 fn undo_select(&self, selector: impl UndoSelector) {
972 let mut actions = vec![];
973
974 self.with_enabled_undo_redo(|undo, _| {
975 let mut select = selector.select(UndoOp::Undo);
976 while let Some(entry) = undo.last() {
977 if select.include(entry.timestamp) {
978 actions.push(undo.pop().unwrap());
979 } else {
980 break;
981 }
982 }
983 });
984
985 for undo in actions {
986 let redo = undo.action.undo();
987 self.redo.lock().push(RedoEntry {
988 timestamp: undo.timestamp,
989 action: redo,
990 });
991 }
992 }
993
994 fn redo_select(&self, selector: impl UndoSelector) {
995 let mut actions = vec![];
996
997 self.with_enabled_undo_redo(|_, redo| {
998 let mut select = selector.select(UndoOp::Redo);
999 while let Some(entry) = redo.last() {
1000 if select.include(entry.timestamp) {
1001 actions.push(redo.pop().unwrap());
1002 } else {
1003 break;
1004 }
1005 }
1006 });
1007
1008 for redo in actions {
1009 let undo = redo.action.redo();
1010 self.undo.lock().push(UndoEntry {
1011 timestamp: redo.timestamp,
1012 action: undo,
1013 });
1014 }
1015 }
1016
1017 fn id(&self) -> Option<WidgetId> {
1018 self.id.load(Ordering::Relaxed)
1019 }
1020}
1021
1022struct UndoEntry {
1023 timestamp: DInstant,
1024 action: Box<dyn UndoAction>,
1025}
1026
1027struct RedoEntry {
1028 pub timestamp: DInstant,
1029 pub action: Box<dyn RedoAction>,
1030}
1031
1032struct UndoGroup {
1033 info: Arc<dyn UndoInfo>,
1034 undo: Vec<UndoEntry>,
1035}
1036impl UndoAction for UndoGroup {
1037 fn undo(self: Box<Self>) -> Box<dyn RedoAction> {
1038 let mut redo = Vec::with_capacity(self.undo.len());
1039 for undo in self.undo.into_iter().rev() {
1040 redo.push(RedoEntry {
1041 timestamp: undo.timestamp,
1042 action: undo.action.undo(),
1043 });
1044 }
1045 Box::new(RedoGroup { info: self.info, redo })
1046 }
1047
1048 fn info(&mut self) -> Arc<dyn UndoInfo> {
1049 self.info.clone()
1050 }
1051
1052 fn as_any(&mut self) -> &mut dyn Any {
1053 self
1054 }
1055
1056 fn merge(self: Box<Self>, args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)> {
1057 Err((self, args.next))
1058 }
1059}
1060struct RedoGroup {
1061 info: Arc<dyn UndoInfo>,
1062 redo: Vec<RedoEntry>,
1063}
1064impl RedoAction for RedoGroup {
1065 fn redo(self: Box<Self>) -> Box<dyn UndoAction> {
1066 let mut undo = Vec::with_capacity(self.redo.len());
1067 for redo in self.redo.into_iter().rev() {
1068 undo.push(UndoEntry {
1069 timestamp: redo.timestamp,
1070 action: redo.action.redo(),
1071 });
1072 }
1073 Box::new(UndoGroup { info: self.info, undo })
1074 }
1075
1076 fn info(&mut self) -> Arc<dyn UndoInfo> {
1077 self.info.clone()
1078 }
1079}
1080
1081struct UndoRedoOp {
1082 info: Arc<dyn UndoInfo>,
1083 op: Box<dyn FnMut(UndoOp) + Send>,
1084}
1085impl UndoAction for UndoRedoOp {
1086 fn undo(mut self: Box<Self>) -> Box<dyn RedoAction> {
1087 (self.op)(UndoOp::Undo);
1088 self
1089 }
1090
1091 fn info(&mut self) -> Arc<dyn UndoInfo> {
1092 self.info.clone()
1093 }
1094
1095 fn as_any(&mut self) -> &mut dyn Any {
1096 self
1097 }
1098
1099 fn merge(self: Box<Self>, args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)> {
1100 Err((self, args.next))
1101 }
1102}
1103impl RedoAction for UndoRedoOp {
1104 fn redo(mut self: Box<Self>) -> Box<dyn UndoAction> {
1105 (self.op)(UndoOp::Redo);
1106 self
1107 }
1108
1109 fn info(&mut self) -> Arc<dyn UndoInfo> {
1110 self.info.clone()
1111 }
1112}
1113
1114struct UndoRedoFullOp {
1115 data: Box<dyn Any + Send>,
1116 op: Box<dyn FnMut(&mut dyn Any, UndoFullOp) + Send>,
1117}
1118impl UndoAction for UndoRedoFullOp {
1119 fn info(&mut self) -> Arc<dyn UndoInfo> {
1120 let mut info = None;
1121 (self.op)(&mut self.data, UndoFullOp::Info { info: &mut info });
1122 info.unwrap_or_else(|| Arc::new("action"))
1123 }
1124
1125 fn undo(mut self: Box<Self>) -> Box<dyn RedoAction> {
1126 (self.op)(&mut self.data, UndoFullOp::Op(UndoOp::Undo));
1127 self
1128 }
1129
1130 fn merge(mut self: Box<Self>, mut args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)>
1131 where
1132 Self: Sized,
1133 {
1134 if let Some(u) = args.next.as_any().downcast_mut::<Self>() {
1135 let mut merged = false;
1136 (self.op)(
1137 &mut self.data,
1138 UndoFullOp::Merge {
1139 next_data: &mut u.data,
1140 prev_timestamp: args.prev_timestamp,
1141 within_undo_interval: args.within_undo_interval,
1142 merged: &mut merged,
1143 },
1144 );
1145 if merged { Ok(self) } else { Err((self, args.next)) }
1146 } else {
1147 Err((self, args.next))
1148 }
1149 }
1150
1151 fn as_any(&mut self) -> &mut dyn Any {
1152 self
1153 }
1154}
1155impl RedoAction for UndoRedoFullOp {
1156 fn info(&mut self) -> Arc<dyn UndoInfo> {
1157 let mut info = None;
1158 (self.op)(&mut self.data, UndoFullOp::Info { info: &mut info });
1159 info.unwrap_or_else(|| Arc::new("action"))
1160 }
1161
1162 fn redo(mut self: Box<Self>) -> Box<dyn UndoAction> {
1163 (self.op)(&mut self.data, UndoFullOp::Op(UndoOp::Redo));
1164 self
1165 }
1166}
1167
1168struct UndoService {
1169 undo_limit: Var<u32>,
1170 undo_interval: Var<Duration>,
1171}
1172
1173impl Default for UndoService {
1174 fn default() -> Self {
1175 Self {
1176 undo_limit: var(u32::MAX),
1177 undo_interval: KEYBOARD.repeat_config().map(|c| c.start_delay + c.interval).cow(),
1178 }
1179 }
1180}
1181
1182context_local! {
1183 static UNDO_SCOPE_CTX: UndoScope = UndoScope::default();
1184}
1185app_local! {
1186 static UNDO_SV: UndoService = {
1187 APP.extensions().require::<UndoManager>();
1188 UndoService::default()
1189 };
1190}
1191
1192pub trait WidgetInfoUndoExt {
1194 fn is_undo_scope(&self) -> bool;
1196
1197 fn undo_scope(&self) -> Option<WidgetInfo>;
1199}
1200impl WidgetInfoUndoExt for WidgetInfo {
1201 fn is_undo_scope(&self) -> bool {
1202 self.meta().flagged(*FOCUS_SCOPE_ID)
1203 }
1204
1205 fn undo_scope(&self) -> Option<WidgetInfo> {
1206 self.ancestors().find(WidgetInfoUndoExt::is_undo_scope)
1207 }
1208}
1209
1210static_id! {
1211 static ref FOCUS_SCOPE_ID: StateId<()>;
1212}
1213
1214pub trait CommandUndoExt {
1216 fn undo_scoped(self) -> Var<Command>;
1219
1220 fn undo_stack(self) -> UndoStackInfo;
1222 fn redo_stack(self) -> UndoStackInfo;
1224}
1225impl CommandUndoExt for Command {
1226 fn undo_scoped(self) -> Var<Command> {
1227 self.focus_scoped_with(|w| match w {
1228 Some(w) => {
1229 if w.is_undo_scope() {
1230 CommandScope::Widget(w.id())
1231 } else if let Some(scope) = w.undo_scope() {
1232 CommandScope::Widget(scope.id())
1233 } else {
1234 CommandScope::App
1235 }
1236 }
1237 None => CommandScope::App,
1238 })
1239 }
1240
1241 fn undo_stack(self) -> UndoStackInfo {
1242 let scope = self.with_meta(|m| m.get(*WEAK_UNDO_SCOPE_ID));
1243 if let Some(scope) = scope
1244 && let Some(s) = scope.0.upgrade()
1245 {
1246 return UndoStackInfo::undo(&s, scope.1.get());
1247 }
1248
1249 if let CommandScope::App = self.scope() {
1250 let mut r = UNDO_SCOPE_CTX.with_default(|| UNDO.undo_stack());
1251 r.undo_interval = UNDO.undo_interval().get();
1252 return r;
1253 }
1254
1255 UndoStackInfo {
1256 stack: vec![],
1257 undo_interval: Duration::ZERO,
1258 }
1259 }
1260
1261 fn redo_stack(self) -> UndoStackInfo {
1262 let scope = self.with_meta(|m| m.get(*WEAK_UNDO_SCOPE_ID));
1263 if let Some(scope) = scope
1264 && let Some(s) = scope.0.upgrade()
1265 {
1266 return UndoStackInfo::redo(&s, scope.1.get());
1267 }
1268
1269 if let CommandScope::App = self.scope() {
1270 let mut r = UNDO_SCOPE_CTX.with_default(|| UNDO.redo_stack());
1271 r.undo_interval = UNDO.undo_interval().get();
1272 return r;
1273 }
1274
1275 UndoStackInfo {
1276 stack: vec![],
1277 undo_interval: Duration::ZERO,
1278 }
1279 }
1280}
1281
1282static_id! {
1283 static ref WEAK_UNDO_SCOPE_ID: StateId<(std::sync::Weak<UndoScope>, Var<Duration>)>;
1284}
1285
1286pub trait UndoSelector: crate::private::Sealed {
1292 type Select: UndoSelect;
1294
1295 fn select(self, op: UndoOp) -> Self::Select;
1297}
1298
1299pub trait UndoSelect {
1301 fn include(&mut self, timestamp: DInstant) -> bool;
1306}
1307impl crate::private::Sealed for u32 {}
1308impl UndoSelector for u32 {
1309 type Select = u32;
1310
1311 fn select(self, op: UndoOp) -> Self::Select {
1312 let _ = op;
1313 self
1314 }
1315}
1316impl UndoSelect for u32 {
1317 fn include(&mut self, _: DInstant) -> bool {
1318 let i = *self > 0;
1319 if i {
1320 *self -= 1;
1321 }
1322 i
1323 }
1324}
1325impl crate::private::Sealed for Duration {}
1326impl UndoSelector for Duration {
1327 type Select = UndoSelectInterval;
1328
1329 fn select(self, op: UndoOp) -> Self::Select {
1330 UndoSelectInterval {
1331 prev: None,
1332 interval: self,
1333 op,
1334 }
1335 }
1336}
1337#[doc(hidden)]
1338pub struct UndoSelectInterval {
1339 prev: Option<DInstant>,
1340 interval: Duration,
1341 op: UndoOp,
1342}
1343impl UndoSelect for UndoSelectInterval {
1344 fn include(&mut self, timestamp: DInstant) -> bool {
1345 if let Some(prev) = &mut self.prev {
1346 let (older, newer) = match self.op {
1347 UndoOp::Undo => (timestamp, *prev),
1348 UndoOp::Redo => (*prev, timestamp),
1349 };
1350 if newer.saturating_duration_since(older) <= self.interval {
1351 *prev = timestamp;
1352 true
1353 } else {
1354 false
1355 }
1356 } else {
1357 self.prev = Some(timestamp);
1358 true
1359 }
1360 }
1361}
1362impl crate::private::Sealed for DInstant {}
1363impl UndoSelector for DInstant {
1364 type Select = UndoSelectLtEq;
1365
1366 fn select(self, op: UndoOp) -> Self::Select {
1367 UndoSelectLtEq { instant: self, op }
1368 }
1369}
1370#[doc(hidden)]
1371pub struct UndoSelectLtEq {
1372 instant: DInstant,
1373 op: UndoOp,
1374}
1375impl UndoSelect for UndoSelectLtEq {
1376 fn include(&mut self, timestamp: DInstant) -> bool {
1377 match self.op {
1378 UndoOp::Undo => timestamp >= self.instant,
1379 UndoOp::Redo => timestamp <= self.instant,
1380 }
1381 }
1382}
1383
1384#[cfg(test)]
1385mod tests {
1386 use zng_app::APP;
1387 use zng_ext_input::keyboard::KeyboardManager;
1388
1389 use super::*;
1390
1391 #[test]
1392 fn register() {
1393 let _a = APP
1394 .minimal()
1395 .extend(UndoManager::default())
1396 .extend(KeyboardManager::default())
1397 .run_headless(false);
1398 let data = Arc::new(Mutex::new(vec![1, 2]));
1399
1400 UNDO.register(PushAction {
1401 data: data.clone(),
1402 item: 1,
1403 });
1404 UNDO.register(PushAction {
1405 data: data.clone(),
1406 item: 2,
1407 });
1408 assert_eq!(&[1, 2], &data.lock()[..]);
1409
1410 UNDO.undo_select(1);
1411 assert_eq!(&[1], &data.lock()[..]);
1412 UNDO.undo_select(1);
1413 assert_eq!(&[] as &[u8], &data.lock()[..]);
1414
1415 UNDO.redo_select(1);
1416 assert_eq!(&[1], &data.lock()[..]);
1417 UNDO.redo_select(1);
1418 assert_eq!(&[1, 2], &data.lock()[..]);
1419 }
1420
1421 fn push_1_2(data: &Arc<Mutex<Vec<u8>>>) {
1422 UNDO.run_op(
1423 "push 1",
1424 clmv!(data, |op| match op {
1425 UndoOp::Undo => assert_eq!(data.lock().pop(), Some(1)),
1426 UndoOp::Redo => data.lock().push(1),
1427 }),
1428 );
1429 UNDO.run_op(
1430 "push 2",
1431 clmv!(data, |op| match op {
1432 UndoOp::Undo => assert_eq!(data.lock().pop(), Some(2)),
1433 UndoOp::Redo => data.lock().push(2),
1434 }),
1435 );
1436 }
1437
1438 #[test]
1439 fn run_op() {
1440 let _a = APP
1441 .minimal()
1442 .extend(UndoManager::default())
1443 .extend(KeyboardManager::default())
1444 .run_headless(false);
1445 let data = Arc::new(Mutex::new(vec![]));
1446
1447 push_1_2(&data);
1448 assert_eq!(&[1, 2], &data.lock()[..]);
1449
1450 UNDO.undo_select(1);
1451 assert_eq!(&[1], &data.lock()[..]);
1452 UNDO.undo_select(1);
1453 assert_eq!(&[] as &[u8], &data.lock()[..]);
1454
1455 UNDO.redo_select(1);
1456 assert_eq!(&[1], &data.lock()[..]);
1457 UNDO.redo_select(1);
1458 assert_eq!(&[1, 2], &data.lock()[..]);
1459 }
1460
1461 #[test]
1462 fn transaction_undo() {
1463 let _a = APP
1464 .minimal()
1465 .extend(UndoManager::default())
1466 .extend(KeyboardManager::default())
1467 .run_headless(false);
1468 let data = Arc::new(Mutex::new(vec![]));
1469
1470 let t = UNDO.transaction(|| {
1471 push_1_2(&data);
1472 });
1473
1474 assert_eq!(&[1, 2], &data.lock()[..]);
1475 UNDO.undo_select(1);
1476 assert_eq!(&[1, 2], &data.lock()[..]);
1477
1478 t.undo();
1479 assert_eq!(&[] as &[u8], &data.lock()[..]);
1480 }
1481
1482 #[test]
1483 fn transaction_commit() {
1484 let _a = APP
1485 .minimal()
1486 .extend(UndoManager::default())
1487 .extend(KeyboardManager::default())
1488 .run_headless(false);
1489 let data = Arc::new(Mutex::new(vec![]));
1490
1491 let t = UNDO.transaction(|| {
1492 push_1_2(&data);
1493 });
1494
1495 assert_eq!(&[1, 2], &data.lock()[..]);
1496 UNDO.undo_select(1);
1497 assert_eq!(&[1, 2], &data.lock()[..]);
1498
1499 t.commit();
1500
1501 UNDO.undo_select(1);
1502 assert_eq!(&[1], &data.lock()[..]);
1503 UNDO.undo_select(1);
1504 assert_eq!(&[] as &[u8], &data.lock()[..]);
1505
1506 UNDO.redo_select(1);
1507 assert_eq!(&[1], &data.lock()[..]);
1508 UNDO.redo_select(1);
1509 assert_eq!(&[1, 2], &data.lock()[..]);
1510 }
1511
1512 #[test]
1513 fn transaction_group() {
1514 let _a = APP
1515 .minimal()
1516 .extend(UndoManager::default())
1517 .extend(KeyboardManager::default())
1518 .run_headless(false);
1519 let data = Arc::new(Mutex::new(vec![]));
1520
1521 let t = UNDO.transaction(|| {
1522 push_1_2(&data);
1523 });
1524
1525 assert_eq!(&[1, 2], &data.lock()[..]);
1526 UNDO.undo_select(1);
1527 assert_eq!(&[1, 2], &data.lock()[..]);
1528
1529 t.commit_group("push 1, 2");
1530
1531 UNDO.undo_select(1);
1532 assert_eq!(&[] as &[u8], &data.lock()[..]);
1533
1534 UNDO.redo_select(1);
1535 assert_eq!(&[1, 2], &data.lock()[..]);
1536 }
1537
1538 fn push_1_sleep_2(data: &Arc<Mutex<Vec<u8>>>) {
1539 UNDO.run_op(
1540 "push 1",
1541 clmv!(data, |op| match op {
1542 UndoOp::Undo => assert_eq!(data.lock().pop(), Some(1)),
1543 UndoOp::Redo => data.lock().push(1),
1544 }),
1545 );
1546 std::thread::sleep(Duration::from_millis(100));
1547 UNDO.run_op(
1548 "push 2",
1549 clmv!(data, |op| match op {
1550 UndoOp::Undo => assert_eq!(data.lock().pop(), Some(2)),
1551 UndoOp::Redo => data.lock().push(2),
1552 }),
1553 );
1554 }
1555
1556 #[test]
1557 fn undo_redo_t_zero() {
1558 let _a = APP
1559 .minimal()
1560 .extend(UndoManager::default())
1561 .extend(KeyboardManager::default())
1562 .run_headless(false);
1563 let data = Arc::new(Mutex::new(vec![]));
1564
1565 push_1_sleep_2(&data);
1566 assert_eq!(&[1, 2], &data.lock()[..]);
1567
1568 UNDO.undo_select(Duration::ZERO);
1569 assert_eq!(&[1], &data.lock()[..]);
1570 UNDO.undo_select(Duration::ZERO);
1571 assert_eq!(&[] as &[u8], &data.lock()[..]);
1572
1573 UNDO.redo_select(Duration::ZERO);
1574 assert_eq!(&[1], &data.lock()[..]);
1575 UNDO.redo_select(Duration::ZERO);
1576 assert_eq!(&[1, 2], &data.lock()[..]);
1577 }
1578
1579 #[test]
1580 fn undo_redo_t_max() {
1581 undo_redo_t_large(Duration::MAX);
1582 }
1583
1584 #[test]
1585 fn undo_redo_t_10s() {
1586 undo_redo_t_large(Duration::from_secs(10));
1587 }
1588
1589 fn undo_redo_t_large(t: Duration) {
1590 let _a = APP
1591 .minimal()
1592 .extend(UndoManager::default())
1593 .extend(KeyboardManager::default())
1594 .run_headless(false);
1595 let data = Arc::new(Mutex::new(vec![]));
1596
1597 push_1_sleep_2(&data);
1598 assert_eq!(&[1, 2], &data.lock()[..]);
1599
1600 UNDO.undo_select(t);
1601 assert_eq!(&[] as &[u8], &data.lock()[..]);
1602
1603 UNDO.redo_select(t);
1604 assert_eq!(&[1, 2], &data.lock()[..]);
1605 }
1606
1607 #[test]
1608 fn watch_var() {
1609 let mut app = APP
1610 .minimal()
1611 .extend(UndoManager::default())
1612 .extend(KeyboardManager::default())
1613 .run_headless(false);
1614
1615 let test_var = var(0);
1616 UNDO.watch_var("set test var", test_var.clone()).perm();
1617
1618 test_var.set(10);
1619 app.update(false).assert_wait();
1620
1621 test_var.set(20);
1622 app.update(false).assert_wait();
1623
1624 assert_eq!(20, test_var.get());
1625
1626 UNDO.undo_select(1);
1627 app.update(false).assert_wait();
1628 assert_eq!(10, test_var.get());
1629
1630 UNDO.undo_select(1);
1631 app.update(false).assert_wait();
1632 assert_eq!(0, test_var.get());
1633
1634 UNDO.redo_select(1);
1635 app.update(false).assert_wait();
1636 assert_eq!(10, test_var.get());
1637
1638 UNDO.redo_select(1);
1639 app.update(false).assert_wait();
1640 assert_eq!(20, test_var.get());
1641 }
1642
1643 struct PushAction {
1644 data: Arc<Mutex<Vec<u8>>>,
1645 item: u8,
1646 }
1647 impl UndoAction for PushAction {
1648 fn undo(self: Box<Self>) -> Box<dyn RedoAction> {
1649 assert_eq!(self.data.lock().pop(), Some(self.item));
1650 self
1651 }
1652
1653 fn info(&mut self) -> Arc<dyn UndoInfo> {
1654 Arc::new("push")
1655 }
1656
1657 fn as_any(&mut self) -> &mut dyn Any {
1658 self
1659 }
1660
1661 fn merge(self: Box<Self>, args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)> {
1662 Err((self, args.next))
1663 }
1664 }
1665 impl RedoAction for PushAction {
1666 fn redo(self: Box<Self>) -> Box<dyn UndoAction> {
1667 self.data.lock().push(self.item);
1668 self
1669 }
1670
1671 fn info(&mut self) -> Arc<dyn UndoInfo> {
1672 Arc::new("push")
1673 }
1674 }
1675}