Files
Artemis/src/Artemis.UI.Shared/Services/NodeEditor/NodeEditorHistory.cs
T
Robert f6090dc296 Code style - Use file scoped namespaces
Code style - Ran code cleanup
2022-08-21 11:36:15 +02:00

140 lines
4.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Artemis.Core;
using ReactiveUI;
namespace Artemis.UI.Shared.Services.NodeEditor;
/// <summary>
/// Represents the command history of a node script.
/// </summary>
public class NodeEditorHistory
{
private readonly Subject<bool> _canRedo = new();
private readonly Subject<bool> _canUndo = new();
private readonly Stack<INodeEditorCommand> _redoCommands = new();
private readonly Stack<INodeEditorCommand> _undoCommands = new();
/// <summary>
/// Creates a new instance of the <see cref="NodeEditorHistory" /> class.
/// </summary>
/// <param name="nodeScript">The node script the history relates to.</param>
public NodeEditorHistory(INodeScript nodeScript)
{
NodeScript = nodeScript;
Execute = ReactiveCommand.Create<INodeEditorCommand>(ExecuteEditorCommand);
Undo = ReactiveCommand.Create(ExecuteUndo, CanUndo);
Redo = ReactiveCommand.Create(ExecuteRedo, CanRedo);
}
/// <summary>
/// Gets the node script the history relates to.
/// </summary>
public INodeScript NodeScript { get; }
/// <summary>
/// Gets an observable sequence containing a boolean value indicating whether history can be undone.
/// </summary>
public IObservable<bool> CanUndo => _canUndo.AsObservable().DistinctUntilChanged();
/// <summary>
/// Gets an observable sequence containing a boolean value indicating whether history can be redone.
/// </summary>
public IObservable<bool> CanRedo => _canRedo.AsObservable().DistinctUntilChanged();
/// <summary>
/// Gets a reactive command that can be executed to execute an instance of a <see cref="INodeEditorCommand" /> and puts
/// it in history.
/// </summary>
public ReactiveCommand<INodeEditorCommand, Unit> Execute { get; }
/// <summary>
/// Gets a reactive command that can be executed to undo history.
/// </summary>
public ReactiveCommand<Unit, INodeEditorCommand?> Undo { get; }
/// <summary>
/// Gets a reactive command that can be executed to redo history.
/// </summary>
public ReactiveCommand<Unit, INodeEditorCommand?> Redo { get; }
/// <summary>
/// Clears the history.
/// </summary>
public void Clear()
{
ClearRedo();
ClearUndo();
UpdateSubjects();
}
/// <summary>
/// Executes the provided <paramref name="command" /> and puts it in history.
/// </summary>
/// <param name="command">The command to execute</param>
public void ExecuteEditorCommand(INodeEditorCommand command)
{
command.Execute();
_undoCommands.Push(command);
ClearRedo();
UpdateSubjects();
}
private void ClearRedo()
{
foreach (INodeEditorCommand nodeEditorCommand in _redoCommands)
{
if (nodeEditorCommand is IDisposable disposable)
disposable.Dispose();
}
_redoCommands.Clear();
}
private void ClearUndo()
{
foreach (INodeEditorCommand nodeEditorCommand in _undoCommands)
{
if (nodeEditorCommand is IDisposable disposable)
disposable.Dispose();
}
_undoCommands.Clear();
}
private INodeEditorCommand? ExecuteUndo()
{
if (!_undoCommands.TryPop(out INodeEditorCommand? command))
return null;
command.Undo();
_redoCommands.Push(command);
UpdateSubjects();
return command;
}
private INodeEditorCommand? ExecuteRedo()
{
if (!_redoCommands.TryPop(out INodeEditorCommand? command))
return null;
command.Execute();
_undoCommands.Push(command);
UpdateSubjects();
return command;
}
private void UpdateSubjects()
{
_canUndo.OnNext(_undoCommands.Any());
_canRedo.OnNext(_redoCommands.Any());
}
}