using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Ink;
using System.Windows.Threading;
namespace InkCanvasForClass.IccInkCanvas {
///
/// 指示时光机的一个Action的类型
///
public enum TimeMachineActionType {
///
/// 用户绘制的墨迹被添加
///
UserInputStrokeAdded,
///
/// 墨迹被添加,这可能是由软件添加的
///
StrokeAdded,
///
/// 墨迹被移除
///
StrokeRemoved,
///
/// 用户使用了面积擦进行擦除
///
UserGeometryErased,
///
/// 用户使用了墨迹擦进行擦除
///
UserStrokeErased,
///
/// 用户使用了墨迹擦进行擦除
///
UserAreaErased,
Test
}
public class TimeMachineAction {
///
/// Action的类型
///
public TimeMachineActionType ActionType { get; protected set ; }
private bool _isReversed = false;
public event EventHandler ActionIsReversedChanged;
///
/// 指示该Action是否被逆操作了。
/// 比如现在如果这是一个StrokeAdded的Action,当执行撤销后,墨迹消失,此时IsReversed应该为True。
///
public bool IsReversed {
get => _isReversed;
set {
if (value == _isReversed) return;
_isReversed = value;
ActionIsReversedChanged?.Invoke(this,null);
}
}
}
///
/// 墨迹操作的Action
///
public class TimeMachineStrokeAction : TimeMachineAction {
public event EventHandler AsyncPerformerLocked;
public event EventHandler AsyncPerformerUnlocked;
public bool IsLocked { get; protected set; } = false;
public TimeMachineStrokeAction(TimeMachineActionType type, IccStroke stroke) {
ActionType = (new TimeMachineActionType[] {
TimeMachineActionType.UserInputStrokeAdded,
TimeMachineActionType.StrokeAdded,
TimeMachineActionType.StrokeRemoved,
}).Contains(type)
? TimeMachineActionType.StrokeAdded
: type;
Stroke = stroke;
}
public TimeMachineStrokeAction(TimeMachineActionType type, IccStroke stroke, long timeStamp) {
ActionType = (new TimeMachineActionType[] {
TimeMachineActionType.UserInputStrokeAdded,
TimeMachineActionType.StrokeAdded,
TimeMachineActionType.StrokeRemoved,
}).Contains(type)
? TimeMachineActionType.StrokeAdded
: type;
Stroke = stroke;
TimeStamp = timeStamp;
}
///
/// 墨迹。如果为UserInputStrokeAdded或StrokeAdded则为添加的墨迹,否则,StrokeRemoved就是被移除的墨迹。
///
public IccStroke Stroke { get; private set; }
///
/// 执行这个操作的时间戳,-1为没有时间。
///
public long TimeStamp { get; private set; } = -1;
///
/// 应用该Action所导致的更改到画布上,需要指定Dispatcher,StrokeCollection
///
public void Perform(Dispatcher dispatcher, StrokeCollection strokes) {
IsLocked = true;
AsyncPerformerLocked?.Invoke(this,null);
var task = dispatcher.InvokeAsync(() => {
if (IsReversed) {
if (ActionType == TimeMachineActionType.StrokeAdded ||
ActionType == TimeMachineActionType.UserInputStrokeAdded) strokes.Remove(Stroke);
else if (ActionType == TimeMachineActionType.StrokeRemoved)
try {
strokes.Add(Stroke);
}
catch { }
} else {
if (ActionType == TimeMachineActionType.StrokeAdded ||
ActionType == TimeMachineActionType.UserInputStrokeAdded)
try {
strokes.Add(Stroke);
}
catch { }
else if (ActionType == TimeMachineActionType.StrokeRemoved) strokes.Remove(Stroke);
}
});
task.Completed += (_, __) => {
IsLocked = false;
AsyncPerformerUnlocked?.Invoke(this, null);
};
}
}
///
/// only internal usage
///
internal class TimeMachineTestAction : TimeMachineAction {
private long timeStamp;
public TimeMachineTestAction() {
ActionType = TimeMachineActionType.Test;
timeStamp = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds();
}
public void Perform() {
Trace.WriteLine($"helloworld! reversed:{IsReversed} timestamp: {timeStamp}");
}
}
///
/// 基于 icc 修改的 时光机 V2 版本,提供了更精简的设计
///
public class TimeMachineV2 {
///
/// 当前的Index
///
private int _currentIndex = 0;
public int CurrentIndex => _currentIndex;
private List _actions = new List();
public TimeMachineAction[] Actions => _actions.ToArray();
public int ActionsCount => _actions.Count;
///
/// 在撤销重做状态发生改变时触发
///
public event EventHandler UndoRedoStateChanged;
///
/// 执行action的perform的时候用于锁住时光机
///
private bool _asyncPerformerLocker = false;
private void asyncPerformerLockedOrUnLocked(object sender, EventArgs e) {
if (sender is TimeMachineStrokeAction) {
_asyncPerformerLocker = (sender as TimeMachineStrokeAction).IsLocked;
}
}
///
/// 丢弃指针Index后的所有Actions并销毁锁事件
///
private void DropActionsAfterCursorIndex() {
var range = _actions.GetRange(_currentIndex, _actions.Count - _currentIndex);
foreach (var a in range) {
if (a is TimeMachineStrokeAction) {
(a as TimeMachineStrokeAction).AsyncPerformerUnlocked -= asyncPerformerLockedOrUnLocked;
(a as TimeMachineStrokeAction).AsyncPerformerLocked -= asyncPerformerLockedOrUnLocked;
}
}
_actions.RemoveRange(_currentIndex, _actions.Count - _currentIndex);
}
public void PushStrokeAction(TimeMachineActionType type, IccStroke stroke, long timeStamp) {
var action = new TimeMachineStrokeAction(type, stroke, timeStamp);
_actions.RemoveRange(_currentIndex, _actions.Count - _currentIndex);
_actions.Add(action);
_currentIndex++;
UndoRedoStateChanged?.Invoke(this,null);
}
///
/// only internal usage
///
public void PushTestAction() {
var action = new TimeMachineTestAction();
_actions.RemoveRange(_currentIndex, _actions.Count - _currentIndex);
_actions.Add(action);
_currentIndex++;
UndoRedoStateChanged?.Invoke(this,null);
}
public void Undo() {
Undo(false);
}
///
/// 撤销
///
public void Undo(bool doNotNotifyStateUpdate = false) {
if (!(_currentIndex > 0)) return;
if (_asyncPerformerLocker) return;
var item = _actions[Math.Max(_currentIndex-1,0)];
if ((new TimeMachineActionType[] {
TimeMachineActionType.Test,
}).Contains(item.ActionType)) {
(item as TimeMachineTestAction).IsReversed = true;
(item as TimeMachineTestAction).Perform();
}
var index = Math.Min(Math.Max(_currentIndex - 1, 0), _actions.Count);
_currentIndex = index;
if (!doNotNotifyStateUpdate) UndoRedoStateChanged?.Invoke(this,null);
}
///
/// 多步撤销
///
/// 步长
public void Undo(bool doNotNotifyStateUpdate = false, int steps = 1) {
for (int i = 0; i < steps; i++) {
Undo(true);
}
if (!doNotNotifyStateUpdate) UndoRedoStateChanged?.Invoke(this,null);
}
///
/// 多步重做
///
/// 步长
public void Redo(bool doNotNotifyStateUpdate = false, int steps = 1) {
for (int i = 0; i < steps; i++) {
Redo(true);
}
if (!doNotNotifyStateUpdate) UndoRedoStateChanged?.Invoke(this,null);
}
public void Redo() {
Redo(false);
}
///
/// 重做
///
public void Redo(bool doNotNotifyStateUpdate = false) {
if (!(_actions.Count - _currentIndex > 0)) return;
if (_asyncPerformerLocker) return;
var item = _actions[Math.Min(_currentIndex, _actions.Count - 1)];
if ((new TimeMachineActionType[] {
TimeMachineActionType.Test,
}).Contains(item.ActionType)) {
(item as TimeMachineTestAction).IsReversed = false;
(item as TimeMachineTestAction).Perform();
}
var index = Math.Min(Math.Max(_currentIndex + 1, 0), _actions.Count);
_currentIndex = index;
if (!doNotNotifyStateUpdate) UndoRedoStateChanged?.Invoke(this,null);
}
}
}