Last active
May 29, 2024 07:43
-
-
Save lvsecoto/fe07363d0e2c9baf8d5a36e65ff4a07e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 对象状态[T]的历史堆栈,可以推入或者弹出 | |
class HistoryStack<T> { | |
/// 最大索引的数据,即为最新的状态 | |
final _store = <T>[]; | |
/// 读取内容,用于保存的数据 | |
({ | |
List<T> store, | |
int step, | |
}) read() { | |
return ( | |
store: _store.toList(), | |
step: _step, | |
); | |
} | |
final List<void Function()> _listeners = []; | |
/// 添加监听器 | |
void addListener(void Function() onChange) { | |
_listeners.add(onChange); | |
} | |
/// 删除监听器 | |
void removeListener(void Function() onChange) { | |
_listeners.remove(onChange); | |
} | |
/// 恢复状态 | |
void restore({ | |
required List<T> store, | |
required int step, | |
}) { | |
_store.clear(); | |
_store.addAll(store); | |
_step = step; | |
} | |
/// 在哪一条历史记录 | |
/// | |
/// 为历史数目时,为历史堆栈的最新状态,越小,代表历史堆栈的最早状态 | |
int _step = 0; | |
/// 压入当前的状态 | |
/// | |
/// **[step]之后的记录会全部抹除** | |
void record(T state) { | |
_store.removeRange(_step, _store.length); | |
_store.add(state); | |
_step = _store.length; | |
_notifyListeners(); | |
} | |
/// 是否可以撤销操作 | |
bool get canUndo { | |
return _step > 1; | |
} | |
/// 撤销 | |
T undo() { | |
if (canUndo) { | |
_step--; | |
_notifyListeners(); | |
} | |
return current; | |
} | |
/// 是否可以重做操作 | |
bool get canRedo { | |
return _step < _store.length; | |
} | |
/// 重做 | |
T redo() { | |
if (canRedo) { | |
_step++; | |
_notifyListeners(); | |
} | |
return current; | |
} | |
/// 清除历史堆栈 | |
void clear() { | |
if (_store.isEmpty) { | |
return; | |
} | |
_store.clear(); | |
_step = 0; | |
} | |
/// 当前状态 | |
T get current => _store.elementAt(_step - 1); | |
/// 通知监听器 | |
void _notifyListeners() { | |
for (final it in _listeners) { | |
it.call(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter_sudoku/common/history/history.dart'; | |
import 'package:test/test.dart'; | |
void main() { | |
late HistoryStack<String> history; | |
test('测试撤销重做逻辑', () { | |
history = HistoryStack<String>(); | |
history.record('hello'); | |
history.record('world'); | |
expect(history.undo(), 'hello'); | |
expect(history.redo(), 'world'); | |
expect(history.undo(), 'hello'); | |
expect(history.redo(), 'world'); | |
expect(history.undo(), 'hello'); | |
}); | |
test('测试在撤销后,再压入新的状态,之前撤销的记录会消失', () { | |
history = HistoryStack<String>(); | |
history.record('hello'); | |
history.record('world'); | |
history.record('now'); | |
expect(history.undo(), 'world'); | |
expect(history.undo(), 'hello'); | |
history.record('again'); | |
expect(history.undo(), 'hello'); | |
expect(history.redo(), 'again'); | |
}); | |
test('测试没有历史时,一直撤销的话,只会保持返回最早的状态', () { | |
history = HistoryStack<String>(); | |
history.record('hello'); | |
history.record('world'); | |
_repeat(10, () { | |
expect(history.undo(), 'hello'); | |
expect(history.undo(), 'hello'); | |
}); | |
}); | |
test('测试当前是最新的状态的话,一直重做,只会保持返回最晚的状态', () { | |
history = HistoryStack<String>(); | |
history.record('hello'); | |
history.record('world'); | |
history.undo(); | |
_repeat(10, () { | |
expect(history.redo(), 'world'); | |
expect(history.redo(), 'world'); | |
}); | |
}); | |
test('测试是否撤销重做可用状态检测', () { | |
history = HistoryStack<String>(); | |
history.record('hello'); | |
history.record('world'); | |
history.record('again'); | |
expect(history.canUndo, true); | |
expect(history.canRedo, false); | |
expect(history.undo(), 'world'); | |
expect(history.canUndo, true); | |
expect(history.canRedo, true); | |
expect(history.undo(), 'hello'); | |
expect(history.canUndo, false); | |
expect(history.canRedo, true); | |
expect(history.redo(), 'world'); | |
expect(history.canUndo, true); | |
expect(history.canRedo, true); | |
expect(history.redo(), 'again'); | |
expect(history.canUndo, true); | |
expect(history.canRedo, false); | |
}); | |
test('测试状态监听回调,同步历史记录相关状态', () { | |
history = HistoryStack<String>(); | |
var canUndo = false; | |
var canRedo = false; | |
history.addListener(() { | |
canUndo = history.canUndo; | |
canRedo = history.canRedo; | |
}); | |
expect(canUndo, false); | |
expect(canRedo, false); | |
history.record('hello'); | |
// 只有一个记录时,是不可以撤销的 | |
expect(canUndo, false); | |
expect(canRedo, false); | |
history.record('world'); | |
expect(canUndo, true); | |
expect(canRedo, false); | |
history.undo(); | |
expect(canUndo, false); | |
expect(canRedo, true); | |
history.redo(); | |
expect(canUndo, true); | |
expect(canRedo, false); | |
}); | |
test('测试清除历史', () { | |
history = HistoryStack<String>(); | |
history.record('hello'); | |
history.record('hello world'); | |
expect(history.canUndo, true); | |
history.clear(); | |
expect(history.canUndo, false); | |
expect(history.canRedo, false); | |
}); | |
} | |
void _repeat(int time, void Function() block) { | |
assert(time > 0); | |
for (int i = 0; i < time; i++) { | |
block(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment