zng_ext_undo/
lib.rs

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//!
4//! Undo-redo app extension, service and commands.
5//!
6//! # Crate
7//!
8#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11#![recursion_limit = "256"]
12// suppress nag about very simple boxed closure signatures.
13#![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    // https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
47    pub trait Sealed {}
48}
49
50/// Undo-redo app extension.
51///
52/// # Services
53///
54/// Services provided by this extension.
55///
56/// * [`UNDO`]
57#[derive(Default)]
58pub struct UndoManager {}
59
60impl AppExtension for UndoManager {
61    fn event(&mut self, update: &mut EventUpdate) {
62        // app scope handler
63        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    /// Contextual undo limit.
91    ///
92    /// Is [`UNDO.undo_limit`] by default.
93    ///
94    /// [`UNDO.undo_limit`]: UNDO::undo_limit
95    pub static UNDO_LIMIT_VAR: u32 = UNDO.undo_limit();
96
97    /// Contextual undo interval.
98    ///
99    /// Is [`UNDO.undo_interval`] by default.
100    ///
101    /// [`UNDO.undo_interval`]: UNDO::undo_interval
102    pub static UNDO_INTERVAL_VAR: Duration = UNDO.undo_interval();
103}
104
105/// Undo-redo service.
106///
107/// # Provider
108///
109/// This service is provided by the [`UndoManager`] extension, it will panic if used in an app not extended.
110pub struct UNDO;
111impl UNDO {
112    /// Gets or sets the maximum length of each undo stack of each scope.
113    ///
114    /// Is `u32::MAX` by default. If the limit is reached the oldest undo action is dropped without redo.
115    ///
116    /// Note that undo scopes get the max undo from [`UNDO_LIMIT_VAR`] in context, the context var is
117    /// set to this var by default.
118    pub fn undo_limit(&self) -> Var<u32> {
119        UNDO_SV.read().undo_limit.clone()
120    }
121
122    /// Gets or sets the time interval that [`undo`] and [`redo`] cover each call.
123    ///
124    /// This value applies to all scopes and defines the max interval between actions
125    /// that are undone in a single call.
126    ///
127    /// Is the [keyboard repeat start delay + interval] by default.
128    ///
129    /// Note that undo scopes get the interval from [`UNDO_INTERVAL_VAR`] in context, the context var is
130    /// set to this var by default.
131    ///
132    /// [`undo`]: Self::undo
133    /// [`redo`]: Self::redo
134    /// [keyboard repeat start delay + interval]: zng_ext_input::keyboard::KEYBOARD::repeat_config
135    pub fn undo_interval(&self) -> Var<Duration> {
136        UNDO_SV.read().undo_interval.clone()
137    }
138
139    /// Gets if the undo service is enabled in the current context.
140    ///
141    /// If `false` calls to [`register`] are ignored.
142    ///
143    /// [`register`]: Self::register
144    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    /// Undo a selection of actions.
149    ///
150    /// # Selectors
151    ///
152    /// These types can be used as selector:
153    ///
154    /// * `u32` - Count of actions to undo.
155    /// * `Duration` - Interval between each action.
156    /// * `DInstant` - Inclusive timestamp to undo back to.
157    pub fn undo_select(&self, selector: impl UndoSelector) {
158        UNDO_SCOPE_CTX.get().undo_select(selector);
159    }
160
161    /// Redo a selection of actions.
162    pub fn redo_select(&self, selector: impl UndoSelector) {
163        UNDO_SCOPE_CTX.get().redo_select(selector);
164    }
165
166    /// Undo all actions within the [`undo_interval`].
167    ///
168    /// [`undo_interval`]: Self::undo_interval
169    pub fn undo(&self) {
170        self.undo_select(UNDO_INTERVAL_VAR.get());
171    }
172
173    /// Redo all actions within the [`undo_interval`].
174    ///
175    /// [`undo_interval`]: Self::undo_interval
176    pub fn redo(&self) {
177        self.redo_select(UNDO_INTERVAL_VAR.get());
178    }
179
180    /// Gets the parent ID that defines an undo scope, or `None` if undo is registered globally for
181    /// the entire app.
182    pub fn scope(&self) -> Option<WidgetId> {
183        UNDO_SCOPE_CTX.get().id()
184    }
185
186    /// Register an already executed action for undo in the current scope.
187    pub fn register(&self, action: impl UndoAction) {
188        UNDO_SCOPE_CTX.get().register(Box::new(action))
189    }
190
191    /// Register an already executed action for undo in the current scope.
192    ///
193    /// The action is defined as a closure `op` that matches over [`UndoOp`] to implement undo and redo.
194    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    /// Register an already executed action for undo in the current scope.
202    ///
203    /// The action is defined as a closure `op` that matches over [`UndoFullOp`] referencing `data` to implement undo and redo.
204    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    /// Run the `action` and register the undo in the current scope.
217    pub fn run(&self, action: impl RedoAction) {
218        UNDO_SCOPE_CTX.get().register(Box::new(action).redo())
219    }
220
221    /// Run the `op` once with [`UndoOp::Redo`] and register it for undo in the current scope.
222    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    /// Run the `op` once with `UndoFullOp::Init { .. }` and `UndoFullOp::Op(UndoOp::Redo)` and register it for undo in the current scope.
230    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    /// Run `actions` as a [`transaction`] and commits as a group if any undo action is captured.
248    ///
249    /// [`transaction`]: Self::transaction
250    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    /// Run `actions` in a new undo scope, capturing all undo actions inside it into a new
260    /// [`UndoTransaction`].
261    ///
262    /// The transaction can be immediately undone or committed.
263    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    /// Run `actions` as a [`transaction`] and commits as a group if the result is `Ok(O)` and at least one
286    /// undo action was registered, or undoes all if result is `Err(E)`.
287    ///
288    /// [`transaction`]: Self::transaction
289    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    /// Run `actions` as a [`transaction`] and commits if the result is `Ok(O)`, or undoes all if result is `Err(E)`.
304    ///
305    /// [`transaction`]: Self::transaction
306    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    /// Runs `f` in a new `scope`. All undo actions inside `f` are registered in the `scope`.
321    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    /// Runs `f` in a disabled scope, all undo actions registered inside `f` are ignored.
326    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    /// Track changes on `var`, registering undo actions for it.
336    ///
337    /// The variable will be tracked until the returned handle or the var is dropped.
338    ///
339    /// Note that this will keep strong clones of previous and new value every time the variable changes, but
340    /// it will only keep weak references to the variable. Dropping the handle or the var will not remove undo/redo
341    /// entries for it, they will still try to assign the variable, failing silently if the variable is dropped too.
342    ///
343    /// Var updates caused by undo and redo are tagged with [`UndoVarModifyTag`].
344    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                    // no actual change
360                    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    /// Clear all redo actions.
384    pub fn clear_redo(&self) {
385        UNDO_SCOPE_CTX.get().redo.lock().clear();
386    }
387
388    /// Clear all undo and redo actions.
389    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    /// If the undo stack is not empty.
397    pub fn can_undo(&self) -> bool {
398        !UNDO_SCOPE_CTX.get().undo.lock().is_empty()
399    }
400
401    /// If the redo stack is not empty.
402    pub fn can_redo(&self) -> bool {
403        !UNDO_SCOPE_CTX.get().redo.lock().is_empty()
404    }
405
406    /// Clones the timestamp and info of all entries in the current undo stack.
407    ///
408    /// The latest undo action is the last entry in the list.
409    pub fn undo_stack(&self) -> UndoStackInfo {
410        UndoStackInfo::undo(&UNDO_SCOPE_CTX.get(), UNDO_INTERVAL_VAR.get())
411    }
412
413    /// Clones the timestamp and info of all entries in the current redo stack.
414    ///
415    /// The latest undone action is the last entry in the list. Note that the
416    /// timestamp marks the moment the original undo registered the action, so the
417    /// newest timestamp is in the first entry.
418    pub fn redo_stack(&self) -> UndoStackInfo {
419        UndoStackInfo::redo(&UNDO_SCOPE_CTX.get(), UNDO_INTERVAL_VAR.get())
420    }
421}
422
423/// Snapshot of the undo or redo stacks in an [`UNDO`] scope.
424#[derive(Clone)]
425pub struct UndoStackInfo {
426    /// Clones the timestamp and info of all entries in the current undo stack.
427    ///
428    /// In an undo list the latest undo action (first to undo) is the last entry in the list and has the latest timestamp.
429    ///
430    /// In an redo list the latest undone action is the last entry (first to redo). Note that the
431    /// timestamp marks the moment the original undo registered the action, so the
432    /// newest timestamp is in the first entry for redo lists.
433    pub stack: Vec<(DInstant, Arc<dyn UndoInfo>)>,
434
435    /// Grouping interval.
436    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    /// Iterate over the `stack`, grouped by `undo_interval`.
453    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/// Identifies var modify requests by undo/redo action.
521///
522/// See [`UNDO.watch_var`] for more details.
523///
524/// [`UNDO.watch_var`]: UNDO::watch_var
525#[derive(Debug, Clone, Copy, PartialEq)]
526pub struct UndoVarModifyTag;
527
528/// Metadata info about an action registered for undo action.
529pub trait UndoInfo: Send + Sync + Any {
530    /// Short display description of the action that will be undone/redone.
531    fn description(&self) -> Txt;
532
533    /// Any extra metadata associated with the item. This can be a thumbnail of an image
534    /// edit action for example, or an icon.
535    ///
536    /// Is empty by default.
537    fn meta(&self) -> StateMapRef<'_, UNDO> {
538        StateMapRef::empty()
539    }
540
541    /// Into `Arc<dyn UndoInfo>` without double wrapping.
542    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}
580/// Represents a single undo action.
581pub trait UndoAction: Send + Any {
582    /// Gets display info about the action that registered this undo.
583    fn info(&mut self) -> Arc<dyn UndoInfo>;
584
585    /// Undo action and returns a [`RedoAction`] that redoes it.
586    fn undo(self: Box<Self>) -> Box<dyn RedoAction>;
587
588    /// Access `dyn Any` methods.
589    fn as_any(&mut self) -> &mut dyn Any;
590
591    /// Try merge the `next` action with the previous `self`.
592    ///
593    /// This is called when `self` is the latest registered action and `next` is registered.
594    ///
595    /// This can be used to optimize high-volume actions, but note that [`UNDO.undo`] will undo all actions
596    /// within the [`UNDO.undo_interval`] of the previous, even if not merged, and merged actions always show as one action
597    /// for [`UNDO.undo_select`].
598    ///
599    /// [`UNDO.undo_interval`]: UNDO::undo_interval
600    /// [`UNDO.undo_select`]: UNDO::undo_select
601    /// [`UNDO.undo`]: UNDO::undo
602    fn merge(self: Box<Self>, args: UndoActionMergeArgs) -> Result<Box<dyn UndoAction>, (Box<dyn UndoAction>, Box<dyn UndoAction>)>;
603}
604
605/// Arguments for [`UndoAction::merge`].
606pub struct UndoActionMergeArgs {
607    /// The action that was registered after the one receiving this arguments.
608    pub next: Box<dyn UndoAction>,
609
610    /// Timestamp of the previous action registered.
611    pub prev_timestamp: DInstant,
612
613    /// If the `prev_timestamp` is within the [`UNDO.undo_interval`]. Undo actions
614    /// can choose to ignore this and merge anyway.
615    ///
616    /// [`UNDO.undo_interval`]: UNDO::undo_interval
617    pub within_undo_interval: bool,
618}
619
620/// Represents a single redo action.
621pub trait RedoAction: Send + Any {
622    /// Gets display info about the action that will be redone.
623    fn info(&mut self) -> Arc<dyn UndoInfo>;
624
625    /// Redo action and returns a [`UndoAction`] that undoes it.
626    fn redo(self: Box<Self>) -> Box<dyn UndoAction>;
627}
628
629/// Represents an undo/redo action.
630///
631/// This can be used to implement undo and redo in a single closure. See [`UNDO.register_op`] and
632/// [`UNDO.run_op`] for more details.
633///
634/// [`UNDO.register_op`]: UNDO::register_op
635/// [`UNDO.run_op`]: UNDO::run_op
636#[derive(Debug, Clone, Copy, PartialEq, Eq)]
637pub enum UndoOp {
638    /// Undo the action.
639    Undo,
640    /// Redo the action.
641    Redo,
642}
643impl UndoOp {
644    /// Gets the command that represents the OP.
645    pub fn cmd(self) -> Command {
646        match self {
647            UndoOp::Undo => UNDO_CMD,
648            UndoOp::Redo => REDO_CMD,
649        }
650    }
651}
652
653/// Represents a full undo/redo action.
654///
655/// This can be used to implement undo and redo in a single closure. See [`UNDO.register_full_op`] and
656/// [`UNDO.run_full_op`] for more details.
657///
658/// [`UNDO.register_full_op`]: UNDO::register_full_op
659/// [`UNDO.run_full_op`]: UNDO::run_full_op
660pub enum UndoFullOp<'r> {
661    /// Initialize data in the execution context.
662    ///
663    /// This is called once before the initial `Op(UndoOp::Redo)` call, it
664    /// can be used to skip registering no-ops.
665    Init {
666        /// If the op must actually be executed.
667        ///
668        /// This is `true` by default, if set to `false` the OP will be dropped without ever executing and
669        /// will not be registered for undo.
670        redo: &'r mut bool,
671    },
672
673    /// Normal undo/redo.
674    Op(UndoOp),
675    /// Collect display info.
676    Info {
677        /// Set this to respond.
678        ///
679        /// If not set the info will be some generic "action" text.
680        info: &'r mut Option<Arc<dyn UndoInfo>>,
681    },
682    /// Try merge the `next_data` onto self data (at the undone state).
683    Merge {
684        /// Closure data for the next undo action.
685        ///
686        /// The data can be from any full undo closure action, only merge if the data
687        /// indicates that it comes from actions that can be covered by the `self` closure.
688        next_data: &'r mut dyn Any,
689
690        /// Timestamp of the previous action registered.
691        prev_timestamp: DInstant,
692
693        /// If the `prev_timestamp` is within the [`UNDO.undo_interval`]. Undo actions
694        /// can choose to ignore this and merge anyway.
695        ///
696        /// [`UNDO.undo_interval`]: UNDO::undo_interval
697        within_undo_interval: bool,
698
699        /// Set this to `true` if the next action can be dropped because the `self` closure
700        /// now also implements it.
701        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/// Represents captured undo actions in an [`UNDO.transaction`] operation.
716///
717/// [`UNDO.transaction`]: UNDO::transaction
718#[must_use = "dropping the transaction undoes all captured actions"]
719pub struct UndoTransaction {
720    undo: Vec<UndoEntry>,
721}
722impl UndoTransaction {
723    /// If the transaction did not capture any undo action.
724    pub fn is_empty(&self) -> bool {
725        self.undo.is_empty()
726    }
727
728    /// Push all undo actions captured by the transaction into the current undo scope.
729    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    /// Push a single action in the current undo scope that undoes/redoes all the captured
745    /// actions in the transaction.
746    ///
747    /// Note that this will register a group item even if the transaction is empty.
748    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    /// Cancel the transaction, undoes all captured actions.
756    ///
757    /// This is the same as dropping the transaction.
758    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    /// Represents the **undo** action.
772    ///
773    /// # Param
774    ///
775    /// If the command parameter is a `u32`, `Duration` or `DInstant` calls [`undo_select`], otherwise calls
776    /// [`undo`].
777    ///
778    /// [`undo_select`]: UNDO::undo_select
779    /// [`undo`]: UNDO::undo
780    ///
781    /// # Scope
782    ///
783    /// You can use [`CommandUndoExt::undo_scoped`] to get a command variable that is always scoped on the
784    /// focused undo scope.
785    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    /// Represents the **redo** action.
793    ///
794    /// # Param
795    ///
796    /// If the command parameter is a `u32`, `Duration` or `DInstant` calls [`redo_select`], otherwise calls
797    /// [`redo`].
798    ///
799    /// [`redo_select`]: UNDO::redo_select
800    /// [`redo`]: UNDO::redo
801    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    /// Represents the **clear history** action.
809    ///
810    /// Implementers call [`clear`] in the undo scope.
811    ///
812    /// [`clear`]: UNDO::clear
813    pub static CLEAR_HISTORY_CMD = {
814        l10n!: true,
815        name: "Clear History",
816    };
817}
818
819/// Represents a widget undo scope.
820///
821/// See [`UNDO.with_scope`] for more details.
822///
823/// [`UNDO.with_scope`]: UNDO::with_scope
824pub struct WidgetUndoScope(Option<Arc<UndoScope>>);
825impl WidgetUndoScope {
826    /// New, not inited in a widget.
827    pub const fn new() -> Self {
828        Self(None)
829    }
830
831    /// if the scope is already inited in a widget.
832    pub fn is_inited(&self) -> bool {
833        self.0.is_some()
834    }
835
836    /// Init the scope in the [`WIDGET`].
837    ///
838    /// [`WIDGET`]: zng_app::widget::WIDGET
839    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    /// Sets the [`WIDGET`] info.
857    ///
858    /// [`WIDGET`]: zng_app::widget::WIDGET
859    pub fn info(&mut self, info: &mut WidgetInfoBuilder) {
860        info.flag_meta(*FOCUS_SCOPE_ID);
861    }
862
863    /// Deinit the scope in the [`WIDGET`].
864    ///
865    /// This clears the undo/redo stack of the scope.
866    ///
867    /// [`WIDGET`]: zng_app::widget::WIDGET
868    pub fn deinit(&mut self) {
869        self.0 = None;
870    }
871
872    /// Sets if the undo/redo is enabled in this scope.
873    ///
874    /// Is `true` by default.
875    pub fn set_enabled(&mut self, enabled: bool) {
876        self.0.as_ref().unwrap().enabled.store(enabled, Ordering::Relaxed);
877    }
878
879    /// Gets if the undo stack is not empty.
880    pub fn can_undo(&self) -> bool {
881        !self.0.as_ref().unwrap().undo.lock().is_empty()
882    }
883
884    /// Gets if the redo stack is not empty.
885    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
1192/// Undo extension methods for widget info.
1193pub trait WidgetInfoUndoExt {
1194    /// Returns `true` if the widget is an undo scope.
1195    fn is_undo_scope(&self) -> bool;
1196
1197    /// Gets the first ancestor that is an undo scope.
1198    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
1214/// Undo extension methods for commands.
1215pub trait CommandUndoExt {
1216    /// Gets the command scoped in the undo scope widget that is or contains the focused widget, or
1217    /// scoped on the app if there is no focused undo scope.
1218    fn undo_scoped(self) -> Var<Command>;
1219
1220    /// Latest undo stack for the given scope, same as calling [`UNDO::undo_stack`] inside the scope.
1221    fn undo_stack(self) -> UndoStackInfo;
1222    /// Latest undo stack for the given scope, same as calling [`UNDO::redo_stack`] inside the scope.
1223    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
1286/// Represents a type that can select actions for undo or redo once.
1287///
1288/// This API is sealed, only core crate can implement it.
1289///
1290/// See [`UNDO::undo_select`] for more details.
1291pub trait UndoSelector: crate::private::Sealed {
1292    /// Selection collector.
1293    type Select: UndoSelect;
1294
1295    /// Start selecting action for the `op`.
1296    fn select(self, op: UndoOp) -> Self::Select;
1297}
1298
1299/// Selects actions to undo or redo.
1300pub trait UndoSelect {
1301    /// Called for each undo or redo action from the last item in the stack and back.
1302    ///
1303    /// The `timestamp` is the moment the item was pushed in the undo stack, if this
1304    /// function is called for [`UndoOp::Redo`] it will not be more recent than the next action.
1305    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}