mirror of
https://github.com/ppy/osu.git
synced 2025-01-09 20:32:55 +08:00
Merge branch 'feature/command-handler' into command-pattern-real-2
This commit is contained in:
commit
86a11f6567
@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
private bool isSplittable(PathControlPointPiece<T> p) =>
|
private bool isSplittable(PathControlPointPiece<T> p) =>
|
||||||
// A hit object can only be split on control points which connect two different path segments.
|
// A hit object can only be split on control points which connect two different path segments.
|
||||||
p.ControlPoint.Type.HasValue && p != Pieces.FirstOrDefault() && p != Pieces.LastOrDefault();
|
p.ControlPoint.Type.HasValue && p.ControlPoint != controlPoints.FirstOrDefault() && p.ControlPoint != controlPoints.LastOrDefault();
|
||||||
|
|
||||||
private void onControlPointsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
private void onControlPointsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
@ -402,8 +402,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
selectedControlPoints = new HashSet<PathControlPoint>(Pieces.Where(piece => piece.IsSelected.Value).Select(piece => piece.ControlPoint));
|
selectedControlPoints = new HashSet<PathControlPoint>(Pieces.Where(piece => piece.IsSelected.Value).Select(piece => piece.ControlPoint));
|
||||||
|
|
||||||
Debug.Assert(draggedControlPointIndex >= 0);
|
Debug.Assert(draggedControlPointIndex >= 0);
|
||||||
|
|
||||||
commandHandler?.Commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
|
10
osu.Game/Screens/Edit/Commands/IMergeableCommand.cs
Normal file
10
osu.Game/Screens/Edit/Commands/IMergeableCommand.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// 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 IMergeableCommand : IEditorCommand
|
||||||
|
{
|
||||||
|
public IEditorCommand? MergeWith(IEditorCommand previous);
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Commands
|
namespace osu.Game.Screens.Edit.Commands
|
||||||
{
|
{
|
||||||
public class MoveCommand : IEditorCommand
|
public class MoveCommand : IEditorCommand, IMergeableCommand
|
||||||
{
|
{
|
||||||
public readonly IHasMutablePosition Target;
|
public readonly IHasMutablePosition Target;
|
||||||
|
|
||||||
@ -23,5 +23,13 @@ namespace osu.Game.Screens.Edit.Commands
|
|||||||
public IEditorCommand CreateUndo() => new MoveCommand(Target, Target.Position);
|
public IEditorCommand CreateUndo() => new MoveCommand(Target, Target.Position);
|
||||||
|
|
||||||
public bool IsRedundant => Position == Target.Position;
|
public bool IsRedundant => Position == Target.Position;
|
||||||
|
|
||||||
|
public IEditorCommand? MergeWith(IEditorCommand previous)
|
||||||
|
{
|
||||||
|
if (previous is MoveCommand moveCommand)
|
||||||
|
return moveCommand.Target != Target ? null : this;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
osu.Game/Screens/Edit/Commands/PropertyChangeCommand.cs
Normal file
36
osu.Game/Screens/Edit/Commands/PropertyChangeCommand.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// 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 abstract class PropertyChangeCommand<TTarget, TProperty> : IEditorCommand, IMergeableCommand where TTarget : class
|
||||||
|
{
|
||||||
|
protected abstract TProperty ReadValue(TTarget target);
|
||||||
|
|
||||||
|
protected abstract void WriteValue(TTarget target, TProperty value);
|
||||||
|
|
||||||
|
protected abstract PropertyChangeCommand<TTarget, TProperty> CreateInstance(TTarget target, TProperty value);
|
||||||
|
|
||||||
|
public readonly TTarget Target;
|
||||||
|
|
||||||
|
public readonly TProperty Value;
|
||||||
|
|
||||||
|
protected PropertyChangeCommand(TTarget target, TProperty value)
|
||||||
|
{
|
||||||
|
Target = target;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply() => WriteValue(Target, Value);
|
||||||
|
|
||||||
|
public IEditorCommand CreateUndo() => CreateInstance(Target, Value);
|
||||||
|
|
||||||
|
public IEditorCommand? MergeWith(IEditorCommand previous)
|
||||||
|
{
|
||||||
|
if (previous is PropertyChangeCommand<TTarget, TProperty> command && command.Target == Target)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
public readonly Bindable<bool> CanRedo = new BindableBool();
|
public readonly Bindable<bool> CanRedo = new BindableBool();
|
||||||
|
|
||||||
public bool HasUncommittedChanges => currentTransaction.Entries.Count != 0;
|
public bool HasUncommittedChanges => currentTransaction.Commands.Count != 0;
|
||||||
|
|
||||||
public void Submit(IEditorCommand command, bool commitImmediately = false)
|
public void Submit(IEditorCommand command, bool commitImmediately = false)
|
||||||
{
|
{
|
||||||
@ -56,7 +56,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
undoStack.Push(currentTransaction);
|
undoStack.Push(currentTransaction);
|
||||||
redoStack.Clear();
|
redoStack.Clear();
|
||||||
|
|
||||||
Logger.Log($"Added {currentTransaction.Entries.Count} command(s) to undo stack");
|
Logger.Log($"Added {currentTransaction.Commands.Count} command(s) to undo stack");
|
||||||
|
|
||||||
currentTransaction = new Transaction();
|
currentTransaction = new Transaction();
|
||||||
|
|
||||||
@ -71,10 +71,11 @@ namespace osu.Game.Screens.Edit
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var transaction = undoStack.Pop();
|
var transaction = undoStack.Pop();
|
||||||
|
var redoTransaction = transaction.Reverse();
|
||||||
|
|
||||||
revertTransaction(transaction);
|
revertTransaction(transaction);
|
||||||
|
|
||||||
redoStack.Push(transaction);
|
redoStack.Push(redoTransaction);
|
||||||
|
|
||||||
historyChanged();
|
historyChanged();
|
||||||
|
|
||||||
@ -87,11 +88,12 @@ namespace osu.Game.Screens.Edit
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var transaction = redoStack.Pop();
|
var transaction = redoStack.Pop();
|
||||||
|
var undoTransaction = transaction.Reverse();
|
||||||
|
|
||||||
foreach (var entry in transaction.Entries)
|
foreach (var command in transaction.Commands)
|
||||||
apply(entry.Command);
|
apply(command);
|
||||||
|
|
||||||
undoStack.Push(transaction);
|
undoStack.Push(undoTransaction);
|
||||||
|
|
||||||
historyChanged();
|
historyChanged();
|
||||||
|
|
||||||
@ -112,8 +114,8 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
private void revertTransaction(Transaction transaction)
|
private void revertTransaction(Transaction transaction)
|
||||||
{
|
{
|
||||||
foreach (var entry in transaction.Entries.Reverse())
|
foreach (var command in transaction.Commands.Reverse())
|
||||||
apply(entry.Reverse);
|
apply(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void historyChanged()
|
private void historyChanged()
|
||||||
@ -138,7 +140,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
var reverse = command.CreateUndo();
|
var reverse = command.CreateUndo();
|
||||||
|
|
||||||
currentTransaction.Add(new HistoryEntry(command, reverse));
|
currentTransaction.Add(reverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly record struct HistoryEntry(IEditorCommand Command, IEditorCommand Reverse);
|
private readonly record struct HistoryEntry(IEditorCommand Command, IEditorCommand Reverse);
|
||||||
@ -149,11 +151,47 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<HistoryEntry> entries = new List<HistoryEntry>();
|
private readonly List<IEditorCommand> commands = new List<IEditorCommand>();
|
||||||
|
|
||||||
public IReadOnlyList<HistoryEntry> Entries => entries;
|
public IReadOnlyList<IEditorCommand> Commands => commands;
|
||||||
|
|
||||||
public void Add(HistoryEntry entry) => entries.Add(entry);
|
public void Add(IEditorCommand command)
|
||||||
|
{
|
||||||
|
if (command is IMergeableCommand mergeable)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < commands.Count; i++)
|
||||||
|
{
|
||||||
|
var merged = mergeable.MergeWith(commands[i]);
|
||||||
|
|
||||||
|
if (merged == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
command = merged;
|
||||||
|
commands.RemoveAt(i--);
|
||||||
|
|
||||||
|
if (command is IMergeableCommand newMergeable)
|
||||||
|
{
|
||||||
|
mergeable = newMergeable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.Add(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transaction Reverse()
|
||||||
|
{
|
||||||
|
var reversed = new Transaction();
|
||||||
|
|
||||||
|
foreach (var command in Commands.Reverse())
|
||||||
|
reversed.Add(command.CreateUndo());
|
||||||
|
|
||||||
|
return reversed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ using osu.Game.Screens.Edit.Commands;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit
|
namespace osu.Game.Screens.Edit
|
||||||
{
|
{
|
||||||
public static class EditorCommandManagerExtension
|
public static class EditorCommandHandlerExtension
|
||||||
{
|
{
|
||||||
public static void SafeSubmit(this EditorCommandHandler? manager, IEditorCommand command, bool commitImmediately = false)
|
public static void SafeSubmit(this EditorCommandHandler? manager, IEditorCommand command, bool commitImmediately = false)
|
||||||
{
|
{
|
Loading…
Reference in New Issue
Block a user