1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-05 10:23:20 +08:00

Add EditorCommandHandler

This commit is contained in:
Marvin Schürz 2024-10-08 20:18:16 +02:00
parent b658d9a681
commit b2276fbee7
3 changed files with 177 additions and 4 deletions

View File

@ -0,0 +1,14 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Screens.Edit.Commands
{
public interface IEditorCommand
{
public void Apply();
public IEditorCommand CreateUndo();
public virtual bool IsRedundant => false;
}
}

View File

@ -179,6 +179,8 @@ namespace osu.Game.Screens.Edit
[CanBeNull] // Should be non-null once it can support custom rulesets.
private EditorChangeHandler changeHandler;
private EditorCommandHandler commandHandler;
private DependencyContainer dependencies;
private bool isNewBeatmap;
@ -300,6 +302,9 @@ namespace osu.Game.Screens.Edit
dependencies.CacheAs<IEditorChangeHandler>(changeHandler);
}
commandHandler = new EditorCommandHandler();
dependencies.CacheAs(commandHandler);
beatDivisor.SetArbitraryDivisor(editorBeatmap.BeatmapInfo.BeatDivisor);
beatDivisor.BindValueChanged(divisor => editorBeatmap.BeatmapInfo.BeatDivisor = divisor.NewValue);
@ -428,8 +433,8 @@ namespace osu.Game.Screens.Edit
}
});
changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
changeHandler?.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
commandHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
commandHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
editorBackgroundDim.BindValueChanged(_ => dimBackground());
}
@ -964,9 +969,9 @@ namespace osu.Game.Screens.Edit
#endregion
protected void Undo() => changeHandler?.RestoreState(-1);
protected void Undo() => commandHandler.Undo();
protected void Redo() => changeHandler?.RestoreState(1);
protected void Redo() => commandHandler.Redo();
protected void SetPreviewPointToCurrentTime()
{

View File

@ -0,0 +1,154 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Screens.Edit.Commands;
namespace osu.Game.Screens.Edit
{
public partial class EditorCommandHandler : Component
{
public EditorCommandHandler()
{
}
public event Action<IEditorCommand>? CommandApplied;
public readonly Bindable<bool> CanUndo = new BindableBool();
public readonly Bindable<bool> CanRedo = new BindableBool();
public bool HasUncommittedChanges => currentTransaction.Entries.Count != 0;
public void Submit(IEditorCommand command, bool commitImmediately = false)
{
if (command.IsRedundant)
return;
record(command);
apply(command);
if (commitImmediately)
Commit();
}
public void Submit(IEnumerable<IEditorCommand> commands, bool commitImmediately = false)
{
foreach (var command in commands)
Submit(command);
if (commitImmediately)
Commit();
}
public bool Commit()
{
if (!HasUncommittedChanges)
return false;
undoStack.Push(currentTransaction);
redoStack.Clear();
currentTransaction = new Transaction();
historyChanged();
return true;
}
public bool Undo()
{
if (undoStack.Count == 0)
return false;
var transaction = undoStack.Pop();
revertTransaction(transaction);
redoStack.Push(transaction);
historyChanged();
return true;
}
public bool Redo()
{
if (redoStack.Count == 0)
return false;
var transaction = redoStack.Pop();
foreach (var entry in transaction.Entries)
apply(entry.Command);
undoStack.Push(transaction);
historyChanged();
return true;
}
public bool RevertUncommitedChanges()
{
if (!HasUncommittedChanges)
return false;
revertTransaction(currentTransaction);
currentTransaction = new Transaction();
return true;
}
private void revertTransaction(Transaction transaction)
{
foreach (var entry in transaction.Entries.Reverse())
apply(entry.Reverse);
}
private void historyChanged()
{
CanUndo.Value = undoStack.Count > 0;
CanRedo.Value = redoStack.Count > 0;
}
private Transaction currentTransaction = new Transaction();
private readonly Stack<Transaction> undoStack = new Stack<Transaction>();
private readonly Stack<Transaction> redoStack = new Stack<Transaction>();
private void apply(IEditorCommand command)
{
command.Apply();
CommandApplied?.Invoke(command);
}
private void record(IEditorCommand command)
{
var reverse = command.CreateUndo();
currentTransaction.Add(new HistoryEntry(command, reverse));
}
private readonly record struct HistoryEntry(IEditorCommand Command, IEditorCommand Reverse);
private readonly struct Transaction
{
public Transaction()
{
}
private readonly List<HistoryEntry> entries = new List<HistoryEntry>();
public IReadOnlyList<HistoryEntry> Entries => entries;
public void Add(HistoryEntry entry) => entries.Add(entry);
}
}
}