Skip to content

Commit 874b27d

Browse files
feat(replay_bloc): add suppport for undo/redo multiple steps
1 parent 8a40870 commit 874b27d

File tree

4 files changed

+355
-16
lines changed

4 files changed

+355
-16
lines changed

packages/replay_bloc/lib/src/change_stack.dart

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,21 @@ class _ChangeStack<T> {
1212

1313
int? limit;
1414

15-
bool get canRedo => _redos.any((c) => _shouldReplay(c._newValue));
1615
bool get canUndo => _history.any((c) => _shouldReplay(c._oldValue));
1716

17+
bool get canRedo => _redos.any((c) => _shouldReplay(c._newValue));
18+
1819
void add(_Change<T> change) {
1920
if (limit != null && limit == 0) return;
2021

2122
_history.addLast(change);
23+
2224
_redos.clear();
2325

2426
if (limit != null && _history.length > limit!) {
25-
if (limit! > 0) _history.removeFirst();
27+
if (limit! > 0) {
28+
_history.removeFirst();
29+
}
2630
}
2731
}
2832

@@ -31,19 +35,55 @@ class _ChangeStack<T> {
3135
_redos.clear();
3236
}
3337

34-
void redo() {
35-
if (canRedo) {
36-
final change = _redos.removeFirst();
37-
_history.addLast(change);
38-
return _shouldReplay(change._newValue) ? change.execute() : redo();
38+
void redo(int steps) {
39+
if (steps <= 0) return;
40+
41+
var effectiveSteps = steps;
42+
while (effectiveSteps > 0 && canRedo) {
43+
_Change<T>? changeToExecute;
44+
while (_redos.isNotEmpty) {
45+
final change = _redos.first;
46+
if (_shouldReplay(change._newValue)) {
47+
changeToExecute = _redos.removeFirst();
48+
break;
49+
} else {
50+
_history.addLast(_redos.removeFirst());
51+
}
52+
}
53+
54+
if (changeToExecute != null) {
55+
_history.addLast(changeToExecute);
56+
changeToExecute.execute();
57+
effectiveSteps--;
58+
} else {
59+
break;
60+
}
3961
}
4062
}
4163

42-
void undo() {
43-
if (canUndo) {
44-
final change = _history.removeLast();
45-
_redos.addFirst(change);
46-
return _shouldReplay(change._oldValue) ? change.undo() : undo();
64+
void undo(int steps) {
65+
if (steps <= 0) return;
66+
67+
var effectiveSteps = steps;
68+
while (effectiveSteps > 0 && canUndo) {
69+
_Change<T>? changeToUndo;
70+
while (_history.isNotEmpty) {
71+
final change = _history.last;
72+
if (_shouldReplay(change._oldValue)) {
73+
changeToUndo = _history.removeLast();
74+
break;
75+
} else {
76+
_redos.addFirst(_history.removeLast());
77+
}
78+
}
79+
80+
if (changeToUndo != null) {
81+
_redos.addFirst(changeToUndo);
82+
changeToUndo.undo();
83+
effectiveSteps--;
84+
} else {
85+
break;
86+
}
4787
}
4888
}
4989
}

packages/replay_bloc/lib/src/replay_bloc.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,10 @@ mixin ReplayBlocMixin<Event extends ReplayEvent, State> on Bloc<Event, State> {
132132
}
133133

134134
/// Undo the last change.
135-
void undo() => _changeStack.undo();
135+
void undo([int steps = 1]) => _changeStack.undo(steps);
136136

137137
/// Redo the previous change.
138-
void redo() => _changeStack.redo();
138+
void redo([int steps = 1]) => _changeStack.redo(steps);
139139

140140
/// Checks whether the undo/redo stack is empty.
141141
bool get canUndo => _changeStack.canUndo;

packages/replay_bloc/lib/src/replay_cubit.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ mixin ReplayCubitMixin<State> on Cubit<State> {
7676
}
7777

7878
/// Undo the last change.
79-
void undo() => _changeStack.undo();
79+
void undo([int steps = 1]) => _changeStack.undo(steps);
8080

8181
/// Redo the previous change.
82-
void redo() => _changeStack.redo();
82+
void redo([int steps = 1]) => _changeStack.redo(steps);
8383

8484
/// Checks whether the undo/redo stack is empty.
8585
bool get canUndo => _changeStack.canUndo;

0 commit comments

Comments
 (0)