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.ProfileEditor; /// /// Represents the command history of a profile configuration. /// public class ProfileEditorHistory { private readonly Subject _canRedo = new(); private readonly Subject _canUndo = new(); private readonly Stack _redoCommands = new(); private readonly Stack _undoCommands = new(); /// /// Creates a new instance of the class. /// /// The profile configuration the history relates to. public ProfileEditorHistory(ProfileConfiguration profileConfiguration) { ProfileConfiguration = profileConfiguration; Execute = ReactiveCommand.Create(ExecuteEditorCommand); Undo = ReactiveCommand.Create(ExecuteUndo, CanUndo); Redo = ReactiveCommand.Create(ExecuteRedo, CanRedo); } /// /// Gets the profile configuration the history relates to. /// public ProfileConfiguration ProfileConfiguration { get; } /// /// Gets an observable sequence containing a boolean value indicating whether history can be undone. /// public IObservable CanUndo => _canUndo.AsObservable().DistinctUntilChanged(); /// /// Gets an observable sequence containing a boolean value indicating whether history can be redone. /// public IObservable CanRedo => _canRedo.AsObservable().DistinctUntilChanged(); /// /// Gets a reactive command that can be executed to execute an instance of a and /// puts it in history. /// public ReactiveCommand Execute { get; } /// /// Gets a reactive command that can be executed to undo history. /// public ReactiveCommand Undo { get; } /// /// Gets a reactive command that can be executed to redo history. /// public ReactiveCommand Redo { get; } /// /// Clears the history. /// public void Clear() { ClearRedo(); ClearUndo(); UpdateSubjects(); } /// /// Executes the provided and puts it in history. /// /// The command to execute public void ExecuteEditorCommand(IProfileEditorCommand command) { command.Execute(); _undoCommands.Push(command); ClearRedo(); UpdateSubjects(); } private void ClearRedo() { foreach (IProfileEditorCommand profileEditorCommand in _redoCommands) { if (profileEditorCommand is IDisposable disposable) disposable.Dispose(); } _redoCommands.Clear(); } private void ClearUndo() { foreach (IProfileEditorCommand profileEditorCommand in _undoCommands) { if (profileEditorCommand is IDisposable disposable) disposable.Dispose(); } _undoCommands.Clear(); } private IProfileEditorCommand? ExecuteUndo() { if (!_undoCommands.TryPop(out IProfileEditorCommand? command)) return null; command.Undo(); _redoCommands.Push(command); UpdateSubjects(); return command; } private IProfileEditorCommand? ExecuteRedo() { if (!_redoCommands.TryPop(out IProfileEditorCommand? command)) return null; command.Execute(); _undoCommands.Push(command); UpdateSubjects(); return command; } private void UpdateSubjects() { _canUndo.OnNext(_undoCommands.Any()); _canRedo.OnNext(_redoCommands.Any()); } }