Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to command pattern for change handling #30255

Draft
wants to merge 61 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
b2276fb
Add `EditorCommandHandler`
minetoblend Oct 8, 2024
18f7321
Add SafeSubmit extension for nullable command handler
minetoblend Oct 8, 2024
867e986
Add `MoveCommand`
minetoblend Oct 8, 2024
fe9e84b
Add `AddHitObjectCommand` and `RemoveHitObjectCommand`
minetoblend Oct 8, 2024
508701f
Use commands for moving HitObjects in OsuSelectionHandler
minetoblend Oct 8, 2024
fb5d3de
Remove `Component` superclass from `EditorCommandHandler`
minetoblend Oct 8, 2024
c50adc8
Add `SetStartTimeCommand`
minetoblend Oct 8, 2024
1d953e0
Add `MoveXCommand` and `MoveYCommand`
minetoblend Oct 8, 2024
307d525
Make x/y position mutable in OsuHitObject
minetoblend Oct 8, 2024
39dc357
Attempt to convert slider editing to command pattern
minetoblend Oct 9, 2024
05a87c9
Merge branch 'feature/command-handler' of https://github.com/minetobl…
OliBomby Oct 9, 2024
6b1fc29
ensure Proxy created when DI finished
OliBomby Oct 9, 2024
f9b7f26
fix setters
OliBomby Oct 9, 2024
4814ccb
Don't commit in DragStart
minetoblend Oct 9, 2024
28e86ba
attempt proxing
OliBomby Oct 9, 2024
1924463
Add mergeable commands
Oct 10, 2024
0422dc7
Implement variant type generic proxies without heap allocations
OliBomby Oct 10, 2024
4a2995d
Add PropertyChangeCommand
Oct 10, 2024
597396d
Merge branch 'master' into feature/command-handler
Oct 10, 2024
3fb986e
Fix formatting
Oct 10, 2024
86a11f6
Merge branch 'feature/command-handler' into command-pattern-real-2
OliBomby Oct 10, 2024
c30e70c
fix warnings
OliBomby Oct 10, 2024
ffadc7d
fix mergeable commands
OliBomby Oct 10, 2024
fcda194
fix warning
OliBomby Oct 10, 2024
3c3678f
swap type arguments order for something more logical i think
OliBomby Oct 10, 2024
ce12b48
Merge pull request #2 from OliBomby/command-pattern-real-2
minetoblend Oct 10, 2024
7d243eb
Rename `MoveCommand` to `SetPositionCommand`
Oct 10, 2024
9d68673
Merge remote-tracking branch 'origin/feature/command-handler' into fe…
Oct 10, 2024
2cf828b
Rename `SetXCommand` and `SetYCommand`
Oct 10, 2024
6dccccb
Make position commands extends `PropertyChangeCommand`
Oct 10, 2024
2175f77
Use correct value for undo in `PropertyChangeCommand`
Oct 10, 2024
68d3f0a
Fix comparison in `PropertyChangeCommand.MergeWith`
Oct 10, 2024
7e17b64
Add some documentation for Commands
Oct 10, 2024
238f6c8
Implement `IsRedundant` for `PropertyChangeCommand`
Oct 10, 2024
be90c47
Update `SetExpectedDistanceCommand` to extend `PropertyChangeCommand`
Oct 10, 2024
5518db9
Update more commands to extends `PropertyChangeCommand`
Oct 10, 2024
8aa5385
Add `SetNewComboCommand`
Oct 10, 2024
57c1219
Add `SetPathTypeCommand`
Oct 10, 2024
de5864a
Refactor command proxies
Oct 10, 2024
224c39f
Remove `IHasMutableXPosition, IHasMutableYPosition` from `IHasMutable…
Oct 10, 2024
8a55311
Fix command merging for undo
Oct 10, 2024
0e9bafd
Convert more stuff to commands
Oct 10, 2024
76dd26d
Update stuff to command proxies
Oct 10, 2024
23be7fe
Refactor command merging
Oct 11, 2024
4ec51b9
Add documentation to EditorCommandHandler methods
Oct 11, 2024
bf195c0
Convert more stuff to command proxies
Oct 11, 2024
b0eea93
Make `ListCommandProxy.submit` private
Oct 11, 2024
ca00a54
Remove unused imports
Oct 11, 2024
ff26ab3
Use correct namespace in `SliderPathCommandProxyExtensions`
Oct 11, 2024
7698173
move SetPathTypeCommand to correct folder
OliBomby Oct 12, 2024
7a3bc73
clean up SliderPathExtensions
OliBomby Oct 12, 2024
db29feb
Make EditorCommandHandler a TransactionalCommitComponent
OliBomby Oct 12, 2024
8fee3f5
combine change handler into command handler
OliBomby Oct 12, 2024
8cbd40f
prevent hitsample assignment on open sample point
OliBomby Oct 12, 2024
75eefcf
fix mix changehandler and command handler
OliBomby Oct 12, 2024
4ddec49
Create UpdateHitObjectCommand.cs
OliBomby Oct 12, 2024
6741f1c
fix warnings
OliBomby Oct 12, 2024
c8e2adc
fix selection blueprint not updating position when hitobject not sele…
OliBomby Oct 12, 2024
20ce649
fix lacking hitobject updates in undo history
OliBomby Oct 12, 2024
f6881f4
Update IEditorCommand.cs
OliBomby Oct 12, 2024
d004e55
fix warning
OliBomby Oct 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Commands.Proxies;
using osuTK;
using osuTK.Input;

Expand All @@ -42,6 +43,8 @@ public partial class PathControlPointVisualiser<T> : CompositeDrawable, IKeyBind
private readonly T hitObject;
private readonly bool allowSelection;

private CommandProxy<T> proxy => hitObject.AsCommandProxy(commandHandler);

private InputManager inputManager;

public Action<List<PathControlPoint>> RemoveControlPointsRequested;
Expand Down Expand Up @@ -113,15 +116,15 @@ private void ensureValidPathType(IReadOnlyList<PathControlPoint> segment)
return;

if (segment.Count > 3)
first.Type = PathType.BEZIER;
first.AsCommandProxy(commandHandler).SetType(PathType.BEZIER);

if (segment.Count != 3)
return;

ReadOnlySpan<Vector2> points = segment.Select(p => p.Position).ToArray();
RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points);
if (boundingBox.Width >= 640 || boundingBox.Height >= 480)
first.Type = PathType.BEZIER;
first.AsCommandProxy(commandHandler).SetType(PathType.BEZIER);
}

/// <summary>
Expand All @@ -146,9 +149,9 @@ public bool DeleteSelected()
if (toRemove.Count == 0)
return false;

changeHandler?.BeginChange();
commandHandler?.BeginChange();
RemoveControlPointsRequested?.Invoke(toRemove);
changeHandler?.EndChange();
commandHandler?.EndChange();

// Since pieces are re-used, they will not point to the deleted control points while remaining selected
foreach (var piece in Pieces)
Expand All @@ -165,9 +168,9 @@ private bool splitSelected()
if (controlPointsToSplitAt.Count == 0)
return false;

changeHandler?.BeginChange();
commandHandler?.BeginChange();
SplitControlPointsRequested?.Invoke(controlPointsToSplitAt);
changeHandler?.EndChange();
commandHandler?.EndChange();

// Since pieces are re-used, they will not point to the deleted control points while remaining selected
foreach (var piece in Pieces)
Expand Down Expand Up @@ -286,7 +289,7 @@ protected override bool OnKeyDown(KeyDownEvent e)
if (currentTypeIndex < 0 && e.ShiftPressed)
currentTypeIndex = 0;

changeHandler?.BeginChange();
commandHandler?.BeginChange();

do
{
Expand All @@ -295,7 +298,7 @@ protected override bool OnKeyDown(KeyDownEvent e)
updatePathTypeOfSelectedPieces(validTypes[currentTypeIndex]);
} while (selectedPoint.Type != validTypes[currentTypeIndex]);

changeHandler?.EndChange();
commandHandler?.EndChange();

return true;
}
Expand Down Expand Up @@ -351,13 +354,13 @@ private void selectionRequested(PathControlPointPiece<T> piece, MouseButtonEvent
/// <param name="type">The path type we want to assign to the given control point piece.</param>
private void updatePathTypeOfSelectedPieces(PathType? type)
{
changeHandler?.BeginChange();
commandHandler?.BeginChange();

double originalDistance = hitObject.Path.Distance;

foreach (var p in Pieces.Where(p => p.IsSelected.Value))
{
List<PathControlPoint> pointsInSegment = hitObject.Path.PointsInSegment(p.ControlPoint);
var pointsInSegment = hitObject.Path.PointsInSegment(p.ControlPoint).AsListCommandProxy(commandHandler);
int indexInSegment = pointsInSegment.IndexOf(p.ControlPoint);

if (type?.Type == SplineType.PerfectCurve)
Expand All @@ -368,26 +371,23 @@ private void updatePathTypeOfSelectedPieces(PathType? type)
int thirdPointIndex = indexInSegment + 2;

if (pointsInSegment.Count > thirdPointIndex + 1)
pointsInSegment[thirdPointIndex].Type = pointsInSegment[0].Type;
pointsInSegment[thirdPointIndex].SetType(pointsInSegment[0].Type());
}

hitObject.Path.ExpectedDistance.Value = null;
p.ControlPoint.Type = type;
proxy.Path().SetExpectedDistance(null);
p.ControlPoint.AsCommandProxy(commandHandler).SetType(type);
}

EnsureValidPathTypes();

if (hitObject.Path.Distance < originalDistance)
hitObject.SnapTo(distanceSnapProvider);
proxy.SnapTo(distanceSnapProvider);
else
hitObject.Path.ExpectedDistance.Value = originalDistance;
proxy.Path().SetExpectedDistance(originalDistance);

changeHandler?.EndChange();
commandHandler?.EndChange();
}

[Resolved(CanBeNull = true)]
private IEditorChangeHandler changeHandler { get; set; }

#region Drag handling

private Vector2[] dragStartPositions;
Expand All @@ -406,11 +406,15 @@ public void DragStarted(PathControlPoint controlPoint)

Debug.Assert(draggedControlPointIndex >= 0);

changeHandler?.BeginChange();
commandHandler?.BeginChange();
}

[Resolved(CanBeNull = true)]
private EditorCommandHandler commandHandler { get; set; }

public void DragInProgress(DragEvent e)
{
var controlPointsProxy = proxy.Path().ControlPoints();
Vector2[] oldControlPoints = hitObject.Path.ControlPoints.Select(cp => cp.Position).ToArray();
Vector2 oldPosition = hitObject.Position;
double oldStartTime = hitObject.StartTime;
Expand All @@ -423,18 +427,18 @@ public void DragInProgress(DragEvent e)

Vector2 movementDelta = Parent!.ToLocalSpace(result?.ScreenSpacePosition ?? newHeadPosition) - hitObject.Position;

hitObject.Position += movementDelta;
hitObject.StartTime = result?.Time ?? hitObject.StartTime;
proxy.SetPosition(hitObject.Position + movementDelta);
proxy.SetStartTime(result?.Time ?? hitObject.StartTime);

for (int i = 1; i < hitObject.Path.ControlPoints.Count; i++)
for (int i = 1; i < controlPointsProxy.Count; i++)
{
PathControlPoint controlPoint = hitObject.Path.ControlPoints[i];
var controlPointProxy = controlPointsProxy[i];
// Since control points are relative to the position of the hit object, all points that are _not_ selected
// need to be offset _back_ by the delta corresponding to the movement of the head point.
// All other selected control points (if any) will move together with the head point
// (and so they will not move at all, relative to each other).
if (!selectedControlPoints.Contains(controlPoint))
controlPoint.Position -= movementDelta;
if (!selectedControlPoints.Contains(controlPointProxy.Target))
controlPointProxy.SetPosition(controlPointProxy.Position() - movementDelta);
}
}
else
Expand All @@ -443,37 +447,37 @@ public void DragInProgress(DragEvent e)

Vector2 movementDelta = Parent!.ToLocalSpace(result?.ScreenSpacePosition ?? Parent!.ToScreenSpace(e.MousePosition)) - dragStartPositions[draggedControlPointIndex] - hitObject.Position;

for (int i = 0; i < controlPoints.Count; ++i)
for (int i = 0; i < controlPointsProxy.Count; ++i)
{
PathControlPoint controlPoint = controlPoints[i];
if (selectedControlPoints.Contains(controlPoint))
controlPoint.Position = dragStartPositions[i] + movementDelta;
var controlPointProxy = controlPointsProxy[i];
if (selectedControlPoints.Contains(controlPointProxy.Target))
controlPointProxy.SetPosition(dragStartPositions[i] + movementDelta);
}
}

// Snap the path to the current beat divisor before checking length validity.
hitObject.SnapTo(distanceSnapProvider);
proxy.SnapTo(distanceSnapProvider);

if (!hitObject.Path.HasValidLength)
{
for (int i = 0; i < hitObject.Path.ControlPoints.Count; i++)
hitObject.Path.ControlPoints[i].Position = oldControlPoints[i];
for (int i = 0; i < controlPointsProxy.Count; i++)
controlPointsProxy[i].SetPosition(oldControlPoints[i]);

hitObject.Position = oldPosition;
hitObject.StartTime = oldStartTime;
proxy.SetPosition(oldPosition);
proxy.SetStartTime(oldStartTime);
// Snap the path length again to undo the invalid length.
hitObject.SnapTo(distanceSnapProvider);
proxy.SnapTo(distanceSnapProvider);
return;
}

// Maintain the path types in case they got defaulted to bezier at some point during the drag.
for (int i = 0; i < hitObject.Path.ControlPoints.Count; i++)
hitObject.Path.ControlPoints[i].Type = dragPathTypes[i];
for (int i = 0; i < controlPointsProxy.Count; i++)
controlPointsProxy[i].SetType(dragPathTypes[i]);

EnsureValidPathTypes();
}

public void DragEnded() => changeHandler?.EndChange();
public void DragEnded() => commandHandler?.EndChange();

#endregion

Expand Down
Loading
Loading