port from perforce
This commit is contained in:
144
intromat/Intromat/ViewModels/UndoRedoViewModel.cs
Normal file
144
intromat/Intromat/ViewModels/UndoRedoViewModel.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using DynamicData;
|
||||
using Intromat.Interfaces;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Intromat.ViewModels
|
||||
{
|
||||
public class UndoRedoViewModel : ReactiveObject
|
||||
{
|
||||
private readonly ObservableAsPropertyHelper<IUndoItem?> _currentRedoItem;
|
||||
private readonly ObservableAsPropertyHelper<IUndoItem?> _currentUndoItem;
|
||||
private UndoItemGroup? _currentGroup;
|
||||
|
||||
public UndoRedoViewModel()
|
||||
{
|
||||
GroupStack.Connect().ActOnEveryObject(g => CurrentGroup = g, _ => CurrentGroup = GroupStack.Count > 0 ? GroupStack.Items.ElementAt(GroupStack.Count - 1) : null);
|
||||
|
||||
this.WhenAnyObservable(vm => vm.UndoStack.CountChanged)
|
||||
.Select(_ => UndoStack.Count > 0 ? UndoStack.Items.ElementAt(UndoStack.Count - 1) : null)
|
||||
.ToProperty(this, vm => vm.CurrentUndoItem, out _currentUndoItem);
|
||||
|
||||
this.WhenAnyObservable(vm => vm.RedoStack.CountChanged)
|
||||
.Select(_ => RedoStack.Count > 0 ? RedoStack.Items.ElementAt(RedoStack.Count - 1) : null)
|
||||
.ToProperty(this, vm => vm.CurrentRedoItem, out _currentRedoItem);
|
||||
|
||||
UndoCommand = ReactiveCommand.Create(Undo,
|
||||
this.WhenAnyValue(vm => vm.CurrentUndoItem).Select(i => i != null));
|
||||
RedoCommand = ReactiveCommand.Create(Redo,
|
||||
this.WhenAnyValue(vm => vm.CurrentRedoItem).Select(i => i != null));
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> RedoCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> UndoCommand { get; }
|
||||
|
||||
public ISourceList<UndoItemGroup> GroupStack { get; } = new SourceList<UndoItemGroup>();
|
||||
|
||||
public UndoItemGroup? CurrentGroup
|
||||
{
|
||||
get => _currentGroup;
|
||||
set => this.RaiseAndSetIfChanged(ref _currentGroup, value);
|
||||
}
|
||||
|
||||
public ISourceList<IUndoItem> UndoStack { get; } = new SourceList<IUndoItem>();
|
||||
public IUndoItem? CurrentUndoItem => _currentUndoItem.Value;
|
||||
|
||||
public ISourceList<IUndoItem> RedoStack { get; } = new SourceList<IUndoItem>();
|
||||
public IUndoItem? CurrentRedoItem => _currentRedoItem.Value;
|
||||
|
||||
public bool Recording { get; set; }
|
||||
|
||||
public void Record(IUndoItem item)
|
||||
{
|
||||
if (!Recording)
|
||||
return;
|
||||
|
||||
if (CurrentGroup != null)
|
||||
{
|
||||
CurrentGroup.Push(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
UndoStack.Add(item);
|
||||
RedoStack.Clear();
|
||||
CommandManager.InvalidateRequerySuggested();
|
||||
}
|
||||
}
|
||||
|
||||
public void PushGroup(IFile file)
|
||||
{
|
||||
GroupStack.Add(new UndoItemGroup(file));
|
||||
}
|
||||
|
||||
public void PopGroup()
|
||||
{
|
||||
var group = CurrentGroup;
|
||||
Debug.Assert(group != null);
|
||||
GroupStack.RemoveAt(GroupStack.Count - 1);
|
||||
if (group.Items.Count > 0)
|
||||
Record(group);
|
||||
}
|
||||
|
||||
public void Execute(IUndoItem item)
|
||||
{
|
||||
item.Redo();
|
||||
item.File.IsDirty = true;
|
||||
Record(item);
|
||||
}
|
||||
|
||||
public void Purge(IFile file)
|
||||
{
|
||||
UndoStack.Edit(list =>
|
||||
{
|
||||
for (var i = list.Count - 1; i >= 0; --i)
|
||||
if (list[i].File != file)
|
||||
list.RemoveAt(i);
|
||||
});
|
||||
|
||||
RedoStack.Edit(list =>
|
||||
{
|
||||
for (var i = list.Count - 1; i >= 0; --i)
|
||||
if (list[i].File != file)
|
||||
list.RemoveAt(i);
|
||||
});
|
||||
|
||||
CommandManager.InvalidateRequerySuggested();
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
{
|
||||
var item = CurrentUndoItem;
|
||||
Debug.Assert(item != null);
|
||||
UndoStack.RemoveAt(UndoStack.Count - 1);
|
||||
var wasRecording = Recording;
|
||||
if (wasRecording)
|
||||
Recording = false;
|
||||
item.Undo();
|
||||
item.File.IsDirty = true;
|
||||
if (wasRecording)
|
||||
Recording = true;
|
||||
RedoStack.Add(item);
|
||||
CommandManager.InvalidateRequerySuggested();
|
||||
}
|
||||
|
||||
public void Redo()
|
||||
{
|
||||
var item = CurrentRedoItem;
|
||||
Debug.Assert(item != null);
|
||||
RedoStack.RemoveAt(RedoStack.Count - 1);
|
||||
var wasRecording = Recording;
|
||||
if (wasRecording)
|
||||
Recording = false;
|
||||
item.Redo();
|
||||
item.File.IsDirty = true;
|
||||
if (wasRecording)
|
||||
Recording = true;
|
||||
UndoStack.Add(item);
|
||||
CommandManager.InvalidateRequerySuggested();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user