using System; using static Editor.Label; using static Sandbox.Scene; namespace Sandbox.Helpers; /// /// A system that aims to wrap the main reusable functionality of an undo system /// public partial class UndoSystem { public class Entry { public string Name { get; set; } public Action Undo { get; set; } public Action Redo { get; set; } /// [Obsolete]? public Object Image { get; set; } public DateTime Timestamp { get; set; } public bool Locked { get; set; } } /// /// Called when an undo is run /// public Action OnUndo; /// /// Called when a redo is run /// public Action OnRedo; /// /// Backwards stack /// public Stack Back { get; } = new(); /// /// Forwards stack, gets cleared when a new undo is added /// public Stack Forward { get; } = new(); /// /// Instigate an undo. Return true if we found a successful undo /// public bool Undo() { if ( !Back.TryPop( out var entry ) ) { next = initial; return false; } next = entry.Undo; try { entry.Undo?.Invoke(); } catch ( System.Exception e ) { Log.Warning( e, $"Error when undoing '{entry.Name}': {e.Message}" ); } if ( entry.Locked ) { Back.Push( entry ); return false; } Forward.Push( entry ); OnUndo?.Invoke( entry ); return true; } /// /// Instigate a redo, returns true if we found a successful undo /// public bool Redo() { if ( !Forward.TryPop( out var entry ) ) return false; next = entry.Redo; Back.Push( entry ); entry.Redo?.Invoke(); OnRedo?.Invoke( entry ); return true; } /// /// Insert a new undo entry /// public Entry Insert( string title, Action undo, Action redo = null ) { var e = new Entry { Name = title, Undo = undo, Redo = redo, Timestamp = DateTime.Now, }; Back.Push( e ); Forward.Clear(); return e; } /// /// Provide a function that returns an action to call on undo/redo. /// This generally is a function that saves and restores the entire state /// of a project. /// [Obsolete( "Auto Snapshotting is obsolete and no longer working. If you really want to use snapshotting for Undo, create/restore the snapshots manually in the undo/redo actions provided to UndoSystem.Insert" )] public void SetSnapshotFunction( Func snapshot ) { } /// /// func getsnapshot() /// { /// var state = currentstate(); /// /// return () => restorestate( state ); /// } /// /// startup() /// { /// -- give a function that creates undo functions /// UndoSystem.SetSnapshotter( getsnapshot ) /// /// -- store current snapshot in `next` /// UndoSystem.Initialize(); /// } /// /// mainloop() /// { /// deleteobject(); /// /// -- store 'next' snapshot as "object deleted" undo /// -- take a new snapshot and store it in next /// UndoSystem.Snapshot( "object deleted" ); /// } /// Action next; Action initial; /// /// Should be called after you make a change to your project. The snapshot system /// is good for self contained projects that can be serialized and deserialized quickly. /// [Obsolete( "Auto Snapshotting is obsolete and no longer working. If you really want to use snapshotting for Undo, create/restore the snapshots manually in the undo/redo actions provided to UndoSystem.Insert" )] public void Snapshot( string changeTitle ) { } /// /// Clear the history and take an initial snapshot. /// You should call this right after a load, or a new project. /// public void Initialize() { Back.Clear(); Forward.Clear(); initial = next; } }