-
Notifications
You must be signed in to change notification settings - Fork 0
/
UndoRedoController.cs
154 lines (130 loc) · 4.39 KB
/
UndoRedoController.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//
// Cuboid (http://github.com/arjonagelhout/cuboid)
// Copyright (c) 2023 Arjo Nagelhout
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Cuboid
{
/// <summary>
/// <para>Contains Undo and Redo stacks with <see cref="Command"/>s.</para>
///
/// <para>This, to store the history of edits made in the <see cref="Models.OldRealityScene"/></para>
///
/// <para>To execute a <see cref="Command"/>, call <see cref="Execute(Command)"/>.
/// The <see cref="Command"/> then gets added to the Undo stack.</para>
///
/// </summary>
public sealed class UndoRedoController : MonoBehaviour
{
// Singleton implementation
private static UndoRedoController _instance;
public static UndoRedoController Instance => _instance;
[NonSerialized] public Binding<UndoRedoData> UndoRedoData = new Binding<UndoRedoData>();
private void Awake()
{
// Singleton implementation
if (_instance != null && _instance != this) { Destroy(this); } else { _instance = this; }
}
/// <summary>
/// Clear the Undo and Redo stacks.
/// </summary>
public void ClearStacks()
{
UndoRedoData.Value.Clear();
UndoRedoData.ValueChanged();
}
public void SetLastStackCount()
{
UndoRedoData.Value._lastStackCount = UndoRedoData.Value.UndoStack.Count;
UndoRedoData.ValueChanged();
}
/// <summary>
/// Performs a new command
/// </summary>
/// <param name="command"></param>
public void Execute(Command command)
{
//Debug.Log($"Execute {command}");
command.Do();
Add(command);
}
/// <summary>
/// Adds the command to the Undo stack.
/// Should be used only if the command is already executed
/// (e.g. when having to combine multiple commands).
/// </summary>
/// <param name="command"></param>
public void Add(Command command)
{
//Debug.Log($"Add {command}");
UndoRedoData data = UndoRedoData.Value;
if (!command.Changes) { return; }
data.UndoStack.Push(command);
data.RedoStack.Clear();
UndoRedoData.ValueChanged();
}
/// <summary>
/// Undoes the last command on the undo stack
/// </summary>
public void Undo()
{
UndoRedoData data = UndoRedoData.Value;
// Safety check: Don't undo if there is nothing to undo
if (!data.CanUndo) { return; }
Command command = data.UndoStack.Pop();
command.Undo();
data.RedoStack.Push(command);
UndoRedoData.ValueChanged();
}
/// <summary>
/// Redoes the last command on the redo stack
/// </summary>
public void Redo()
{
UndoRedoData data = UndoRedoData.Value;
if (!data.CanRedo) { return; }
Command command = data.RedoStack.Pop();
command.Do();
data.UndoStack.Push(command);
UndoRedoData.ValueChanged();
}
}
public class UndoRedoData
{
public Stack<Command> UndoStack = new Stack<Command>();
public Stack<Command> RedoStack = new Stack<Command>();
public bool CanUndo => UndoStack.Count > 0;
public bool CanRedo => RedoStack.Count > 0;
internal int _lastStackCount;
public bool NeedsSaving
{
get
{
if (UndoStack.Count != _lastStackCount)
{
IEnumerable<Command> newCommands = null;
if (UndoStack.Count > _lastStackCount)
{
newCommands = UndoStack.Take(UndoStack.Count - _lastStackCount);
}
else
{
newCommands = RedoStack.Take(_lastStackCount - UndoStack.Count);
}
return newCommands.Any(command => command.NeedsSaving);
}
return false;
}
}
public void Clear()
{
UndoStack.Clear();
RedoStack.Clear();
_lastStackCount = 0;
}
}
}