blob: 34910bac3a95f4770e7543299e2fd7d2bf41523a (
plain)
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
|
import { observable, action } from "mobx";
import { Opt } from "../../fields/Field";
export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
let fn: (...args: any[]) => any;
let patchedFn: Opt<(...args: any[]) => any>;
if (descriptor) {
fn = descriptor.value;
}
return {
configurable: true,
enumerable: false,
get() {
if (!patchedFn) {
patchedFn = (...args: any[]) => {
try {
UndoManager.StartBatch()
return fn.call(this, ...args)
} finally {
UndoManager.EndBatch()
}
};
}
return patchedFn;
},
set(newFn: any) {
patchedFn = undefined;
fn = newFn;
}
}
}
export namespace UndoManager {
export interface UndoEvent {
undo: () => void;
redo: () => void;
}
type UndoBatch = UndoEvent[];
let undoStack: UndoBatch[] = observable([]);
let redoStack: UndoBatch[] = observable([]);
let currentBatch: UndoBatch | undefined;
let batchCounter = 0;
let undoing = false;
export function AddEvent(event: UndoEvent): void {
if (currentBatch && batchCounter && !undoing) {
currentBatch.push(event);
}
}
export function CanUndo(): boolean {
return undoStack.length > 0;
}
export function CanRedo(): boolean {
return redoStack.length > 0;
}
export function StartBatch(): void {
batchCounter++;
if (batchCounter > 0) {
currentBatch = [];
}
}
export const EndBatch = action(() => {
batchCounter--;
if (batchCounter === 0 && currentBatch && currentBatch.length) {
undoStack.push(currentBatch);
redoStack.length = 0;
currentBatch = undefined;
}
})
export function RunInBatch(fn: () => void) {
StartBatch();
fn();
EndBatch();
}
export const Undo = action(() => {
if (undoStack.length === 0) {
return;
}
let commands = undoStack.pop();
if (!commands) {
return;
}
undoing = true;
for (let i = commands.length - 1; i >= 0; i--) {
commands[i].undo();
}
undoing = false;
redoStack.push(commands);
})
export const Redo = action(() => {
if (redoStack.length === 0) {
return;
}
let commands = redoStack.pop();
if (!commands) {
return;
}
undoing = true;
for (let i = 0; i < commands.length; i++) {
commands[i].redo();
}
undoing = false;
undoStack.push(commands);
})
}
|