diff --git a/HistoricalReactiveCommand/HistoricalReactiveCommand.csproj b/HistoricalReactiveCommand/HistoricalReactiveCommand.csproj
index f19debc..ab734fe 100644
--- a/HistoricalReactiveCommand/HistoricalReactiveCommand.csproj
+++ b/HistoricalReactiveCommand/HistoricalReactiveCommand.csproj
@@ -15,6 +15,7 @@
+
diff --git a/HistoricalReactiveCommand/History.cs b/HistoricalReactiveCommand/History.cs
index 54991b0..1ef3425 100644
--- a/HistoricalReactiveCommand/History.cs
+++ b/HistoricalReactiveCommand/History.cs
@@ -12,12 +12,27 @@ public class History : IHistory, IDisposable
{
private Stack StackRedo { get; } = new Stack();
private Stack StackUndo { get; } = new Stack();
-
+
+ public HistoryEntry LastCommand()
+ {
+ return StackUndo.Peek();
+ }
+
private readonly Subject _canRecord = new Subject();
private readonly Subject _canUndo = new Subject();
private readonly Subject _canRedo = new Subject();
private readonly Subject _canClear = new Subject();
+ ///
+ /// Время последней добавленной команды
+ ///
+ /// Время именно добавленной команды, а не из стека
+ public DateTime LastCommandTime { get; set; }
+ ///
+ /// Можно ли добавлять вложенные команды в стек
+ ///
+ public bool CanAddInternalCommand { get; set; } = true;
+
public History(string id)
{
Id = id;
@@ -40,6 +55,7 @@ public IObservable Undo(Func Redo(Func Record(HistoryEntry entry, Func
{
StackUndo.Push(updatedEntry);
+ LastCommandTime = entry.CreationtTime;
UpdateSubjects();
});
}
@@ -76,6 +94,20 @@ public IObservable Clear()
return Observables.Unit;
}
+ public void Add(HistoryEntry entry)
+ {
+ StackRedo.Clear();
+ UpdateSubjects(true);
+ StackUndo.Push(entry);
+ LastCommandTime = entry.CreationtTime;
+ UpdateSubjects();
+ }
+
+ public void UndoPop()
+ {
+ StackUndo.Pop();
+ }
+
public void Dispose()
{
StackRedo.Clear();
diff --git a/HistoricalReactiveCommand/HistoryContext.cs b/HistoricalReactiveCommand/HistoryContext.cs
index 1e78c26..da657de 100644
--- a/HistoricalReactiveCommand/HistoryContext.cs
+++ b/HistoricalReactiveCommand/HistoryContext.cs
@@ -62,7 +62,11 @@ internal HistoryContext(IHistory history, IScheduler outputScheduler)
.CombineLatest(history.CanUndo, (recordable, executable) => recordable && executable);
Undo = ReactiveCommand.CreateFromObservable(
- unit => history.Undo(entry => ResolveCommand(entry).Discard(entry)),
+ unit => history.Undo(entry =>
+ {
+ history.CanAddInternalCommand = false;
+ return ResolveCommand(entry).Discard(entry);
+ }),
CanUndo,
outputScheduler);
@@ -70,7 +74,11 @@ internal HistoryContext(IHistory history, IScheduler outputScheduler)
.CombineLatest(history.CanRedo, (recordable, executable) => recordable && executable);
Redo = ReactiveCommand.CreateFromObservable(
- unit => history.Redo(entry => ResolveCommand(entry).Execute(entry)),
+ unit => history.Redo(entry =>
+ {
+ history.CanAddInternalCommand = false;
+ return ResolveCommand(entry).Execute(entry);
+ }),
CanRedo,
outputScheduler);
@@ -82,6 +90,13 @@ internal HistoryContext(IHistory history, IScheduler outputScheduler)
public IObservable CanRecord => _history.CanRecord;
+ public HistoryEntry LastCommand()
+ {
+ return _history.LastCommand();
+ }
+
+ public DateTime LastCommandTime => _history.LastCommandTime;
+
public ReactiveCommand Undo { get; }
public ReactiveCommand Redo { get; }
@@ -109,5 +124,18 @@ private static IReactiveCommandWithHistory ResolveCommand(HistoryEntry entry)
}
internal IObservable Record(HistoryEntry entry, Func> execute) => _history.Record(entry, execute);
+
+ internal void Record(HistoryEntry entry)
+ {
+ if (_history.CanAddInternalCommand)
+ {
+ _history.Add(entry);
+ }
+ }
+
+ ///
+ /// Удление верхнего элемента
+ ///
+ public void UndoPop() => _history.UndoPop();
}
}
diff --git a/HistoricalReactiveCommand/HistoryEntry.cs b/HistoricalReactiveCommand/HistoryEntry.cs
index 744fd5c..6cd58a8 100644
--- a/HistoricalReactiveCommand/HistoryEntry.cs
+++ b/HistoricalReactiveCommand/HistoryEntry.cs
@@ -1,4 +1,7 @@
-namespace HistoricalReactiveCommand
+using System;
+using System.Data;
+
+namespace HistoricalReactiveCommand
{
public class HistoryEntry
{
@@ -7,11 +10,13 @@ public HistoryEntry(object parameter, object result, string commandKey)
Parameter = parameter;
Result = result;
CommandKey = commandKey;
+ CreationtTime = DateTime.UtcNow;
}
public object Parameter { get; set; }
public object Result { get; set; }
public string CommandKey { get; set; }
+ public DateTime CreationtTime { get; }
}
}
diff --git a/HistoricalReactiveCommand/IHistory.cs b/HistoricalReactiveCommand/IHistory.cs
index 9807736..8e87df6 100644
--- a/HistoricalReactiveCommand/IHistory.cs
+++ b/HistoricalReactiveCommand/IHistory.cs
@@ -10,10 +10,29 @@ public interface IHistory
IObservable CanRedo { get; }
IObservable CanRecord { get; }
IObservable CanClear { get; }
+ ///
+ /// Можно ли вызывать команды внутри команд
+ ///
+ bool CanAddInternalCommand { get; set; }
+ ///
+ /// Время последней добавленной команды
+ ///
+ /// Время именно добавленной команды, а не из стека
+ DateTime LastCommandTime { get; set; }
+
+ ///
+ /// Последняя команда
+ ///
+ HistoryEntry LastCommand();
IObservable Undo(Func> discard);
IObservable Redo(Func> execute);
IObservable Record(HistoryEntry entry, Func> execute);
+ void Add(HistoryEntry entry);
+ ///
+ /// Удаление верхнего элемента
+ ///
+ void UndoPop();
IObservable Clear();
}
}
diff --git a/HistoricalReactiveCommand/IReactiveCommandWithHistory.cs b/HistoricalReactiveCommand/IReactiveCommandWithHistory.cs
index bbfb285..c982d8e 100644
--- a/HistoricalReactiveCommand/IReactiveCommandWithHistory.cs
+++ b/HistoricalReactiveCommand/IReactiveCommandWithHistory.cs
@@ -7,5 +7,7 @@ public interface IReactiveCommandWithHistory : IReactiveCommand
{
IObservable Execute(HistoryEntry entry);
IObservable Discard(HistoryEntry entry);
+ void AddHistoryRecord(dynamic param);
+ void MergeOrAdd(dynamic param, Func checkOutFunc, Func mergeFunc);
}
}
\ No newline at end of file
diff --git a/HistoricalReactiveCommand/ReactiveCommandWithHistory.cs b/HistoricalReactiveCommand/ReactiveCommandWithHistory.cs
index 4646efa..1a4d260 100644
--- a/HistoricalReactiveCommand/ReactiveCommandWithHistory.cs
+++ b/HistoricalReactiveCommand/ReactiveCommandWithHistory.cs
@@ -823,6 +823,50 @@ public override IObservable Execute()
return Execute(default!);
}
+ ///
+ /// Добавление в историю команд
+ ///
+ /// Параметры
+ public void AddHistoryRecord(TParam parameter = default)
+ {
+ var historyEntry = new HistoryEntry(parameter, default(TResult), _commandKey);
+ var withHistory = this as IReactiveCommandWithHistory;
+ History.Record(historyEntry);
+ }
+
+ ///
+ /// Добавление в историю команд с обновление параметров последней команды
+ ///
+ /// Параметры
+ private void Merge(TParam parameter = default)
+ {
+ var historyEntry = new HistoryEntry(parameter, default(TResult), _commandKey);
+ History.UndoPop();
+ History.Record(historyEntry);
+ }
+
+ ///
+ /// Добавление или обновление команды
+ ///
+ /// Параметры
+ /// Функция проверки возможности слияния команд
+ /// Функция слияния команд
+ public void MergeOrAdd(TParam param, Func checkOutFunc, Func mergeFunc)
+ {
+ var prevCommand = History.LastCommand();
+ if (prevCommand.CommandKey == _commandKey
+ && prevCommand.CreationtTime == History.LastCommandTime
+ && History.LastCommandTime.AddSeconds(10) > DateTime.UtcNow
+ && checkOutFunc(param!, prevCommand.Parameter))
+ {
+ Merge(mergeFunc(param!, prevCommand.Parameter));
+ }
+ else
+ {
+ AddHistoryRecord(param);
+ }
+ }
+
protected override void Dispose(bool disposing)
{
_canExecuteSubscription.Dispose();
@@ -845,5 +889,15 @@ IObservable IReactiveCommandWithHistory.Discard(HistoryEntry entry
return _discard.Execute(entry).Select(result => { entry.Result = result; return entry; });
}
+
+ void IReactiveCommandWithHistory.AddHistoryRecord(dynamic parameter)
+ {
+ AddHistoryRecord((TParam)parameter);
+ }
+
+ void IReactiveCommandWithHistory.MergeOrAdd(dynamic param, Func checkOutFunc, Func mergeFunc)
+ {
+ MergeOrAdd((TParam)param, checkOutFunc,mergeFunc);
+ }
}
}