mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 19:03:08 +08:00
Merge pull request #21560 from OliBomby/stream-tool-2
Make PathControlPointVisualiser generic
This commit is contained in:
commit
9677a8e3b3
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
public partial class TestScenePathControlPointVisualiser : OsuManualInputManagerTestScene
|
||||
{
|
||||
private Slider slider;
|
||||
private PathControlPointVisualiser visualiser;
|
||||
private PathControlPointVisualiser<Slider> visualiser;
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
assertControlPointPathType(3, null);
|
||||
}
|
||||
|
||||
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser(slider, allowSelection)
|
||||
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser<Slider>(slider, allowSelection)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
|
@ -159,11 +159,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
}
|
||||
|
||||
private void assertSelectionCount(int count) =>
|
||||
AddAssert($"{count} control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == count);
|
||||
AddAssert($"{count} control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == count);
|
||||
|
||||
private void assertSelected(int index) =>
|
||||
AddAssert($"{(index + 1).ToOrdinalWords()} control point piece selected",
|
||||
() => this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[index]).IsSelected.Value);
|
||||
() => this.ChildrenOfType<PathControlPointPiece<Slider>>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[index]).IsSelected.Value);
|
||||
|
||||
private void moveMouseToRelativePosition(Vector2 relativePosition) =>
|
||||
AddStep($"move mouse to {relativePosition}", () =>
|
||||
@ -202,12 +202,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
moveMouseToControlPoint(2);
|
||||
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
|
||||
|
||||
addMovementStep(new Vector2(450, 50));
|
||||
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
|
||||
|
||||
assertControlPointPosition(2, new Vector2(450, 50));
|
||||
assertControlPointType(2, PathType.PerfectCurve);
|
||||
@ -236,12 +236,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
moveMouseToControlPoint(3);
|
||||
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
|
||||
|
||||
addMovementStep(new Vector2(550, 50));
|
||||
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece>().Count(piece => piece.IsSelected.Value) == 3);
|
||||
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
|
||||
|
||||
// note: if the head is part of the selection being moved, the entire slider is moved.
|
||||
// the unselected nodes will therefore change position relative to the slider head.
|
||||
@ -354,7 +354,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
public new SliderBodyPiece BodyPiece => base.BodyPiece;
|
||||
public new TestSliderCircleOverlay HeadOverlay => (TestSliderCircleOverlay)base.HeadOverlay;
|
||||
public new TestSliderCircleOverlay TailOverlay => (TestSliderCircleOverlay)base.TailOverlay;
|
||||
public new PathControlPointVisualiser ControlPointVisualiser => base.ControlPointVisualiser;
|
||||
public new PathControlPointVisualiser<Slider> ControlPointVisualiser => base.ControlPointVisualiser;
|
||||
|
||||
public TestSliderBlueprint(Slider slider)
|
||||
: base(slider)
|
||||
|
@ -199,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
public new SliderBodyPiece BodyPiece => base.BodyPiece;
|
||||
public new TestSliderCircleOverlay HeadOverlay => (TestSliderCircleOverlay)base.HeadOverlay;
|
||||
public new TestSliderCircleOverlay TailOverlay => (TestSliderCircleOverlay)base.TailOverlay;
|
||||
public new PathControlPointVisualiser ControlPointVisualiser => base.ControlPointVisualiser;
|
||||
public new PathControlPointVisualiser<Slider> ControlPointVisualiser => base.ControlPointVisualiser;
|
||||
|
||||
public TestSliderBlueprint(Slider slider)
|
||||
: base(slider)
|
||||
|
@ -72,14 +72,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
[Test]
|
||||
public void TestMovingUnsnappedSliderNodesSnaps()
|
||||
{
|
||||
PathControlPointPiece sliderEnd = null;
|
||||
PathControlPointPiece<Slider> sliderEnd = null;
|
||||
|
||||
assertSliderSnapped(false);
|
||||
|
||||
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||
AddStep("select slider end", () =>
|
||||
{
|
||||
sliderEnd = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints.Last());
|
||||
sliderEnd = this.ChildrenOfType<PathControlPointPiece<Slider>>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints.Last());
|
||||
InputManager.MoveMouseTo(sliderEnd.ScreenSpaceDrawQuad.Centre);
|
||||
});
|
||||
AddStep("move slider end", () =>
|
||||
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||
AddStep("move mouse to new point location", () =>
|
||||
{
|
||||
var firstPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[0]);
|
||||
var firstPiece = this.ChildrenOfType<PathControlPointPiece<Slider>>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[0]);
|
||||
var pos = slider.Path.PositionAt(0.25d) + slider.Position;
|
||||
InputManager.MoveMouseTo(firstPiece.Parent.ToScreenSpace(pos));
|
||||
});
|
||||
@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||
AddStep("move mouse to second control point", () =>
|
||||
{
|
||||
var secondPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[1]);
|
||||
var secondPiece = this.ChildrenOfType<PathControlPointPiece<Slider>>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[1]);
|
||||
InputManager.MoveMouseTo(secondPiece);
|
||||
});
|
||||
AddStep("quick delete", () =>
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
|
||||
|
||||
private Slider? slider;
|
||||
private PathControlPointVisualiser? visualiser;
|
||||
private PathControlPointVisualiser<Slider>? visualiser;
|
||||
|
||||
private const double split_gap = 100;
|
||||
|
||||
@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("select added slider", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser>().First();
|
||||
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser<Slider>>().First();
|
||||
});
|
||||
|
||||
moveMouseToControlPoint(2);
|
||||
@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("select added slider", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser>().First();
|
||||
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser<Slider>>().First();
|
||||
});
|
||||
|
||||
moveMouseToControlPoint(2);
|
||||
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddStep("select added slider", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser>().First();
|
||||
visualiser = blueprintContainer.SelectionBlueprints.First(o => o.Item == slider).ChildrenOfType<PathControlPointVisualiser<Slider>>().First();
|
||||
});
|
||||
|
||||
moveMouseToControlPoint(2);
|
||||
|
@ -8,34 +8,36 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Lines;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// A visualisation of the line between two <see cref="PathControlPointPiece"/>s.
|
||||
/// A visualisation of the line between two <see cref="PathControlPointPiece{T}"/>s.
|
||||
/// </summary>
|
||||
public partial class PathControlPointConnectionPiece : CompositeDrawable
|
||||
/// <typeparam name="T">The type of <see cref="OsuHitObject"/> which this <see cref="PathControlPointConnectionPiece{T}"/> visualises.</typeparam>
|
||||
public partial class PathControlPointConnectionPiece<T> : CompositeDrawable where T : OsuHitObject, IHasPath
|
||||
{
|
||||
public readonly PathControlPoint ControlPoint;
|
||||
|
||||
private readonly Path path;
|
||||
private readonly Slider slider;
|
||||
private readonly T hitObject;
|
||||
public int ControlPointIndex { get; set; }
|
||||
|
||||
private IBindable<Vector2> sliderPosition;
|
||||
private IBindable<Vector2> hitObjectPosition;
|
||||
private IBindable<int> pathVersion;
|
||||
|
||||
public PathControlPointConnectionPiece(Slider slider, int controlPointIndex)
|
||||
public PathControlPointConnectionPiece(T hitObject, int controlPointIndex)
|
||||
{
|
||||
this.slider = slider;
|
||||
this.hitObject = hitObject;
|
||||
ControlPointIndex = controlPointIndex;
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
ControlPoint = slider.Path.ControlPoints[controlPointIndex];
|
||||
ControlPoint = hitObject.Path.ControlPoints[controlPointIndex];
|
||||
|
||||
InternalChild = path = new SmoothPath
|
||||
{
|
||||
@ -48,10 +50,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
sliderPosition = slider.PositionBindable.GetBoundCopy();
|
||||
sliderPosition.BindValueChanged(_ => updateConnectingPath());
|
||||
hitObjectPosition = hitObject.PositionBindable.GetBoundCopy();
|
||||
hitObjectPosition.BindValueChanged(_ => updateConnectingPath());
|
||||
|
||||
pathVersion = slider.Path.Version.GetBoundCopy();
|
||||
pathVersion = hitObject.Path.Version.GetBoundCopy();
|
||||
pathVersion.BindValueChanged(_ => updateConnectingPath());
|
||||
|
||||
updateConnectingPath();
|
||||
@ -62,16 +64,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
/// </summary>
|
||||
private void updateConnectingPath()
|
||||
{
|
||||
Position = slider.StackedPosition + ControlPoint.Position;
|
||||
Position = hitObject.StackedPosition + ControlPoint.Position;
|
||||
|
||||
path.ClearVertices();
|
||||
|
||||
int nextIndex = ControlPointIndex + 1;
|
||||
if (nextIndex == 0 || nextIndex >= slider.Path.ControlPoints.Count)
|
||||
if (nextIndex == 0 || nextIndex >= hitObject.Path.ControlPoints.Count)
|
||||
return;
|
||||
|
||||
path.AddVertex(Vector2.Zero);
|
||||
path.AddVertex(slider.Path.ControlPoints[nextIndex].Position - ControlPoint.Position);
|
||||
path.AddVertex(hitObject.Path.ControlPoints[nextIndex].Position - ControlPoint.Position);
|
||||
|
||||
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
||||
}
|
||||
|
@ -29,11 +29,13 @@ using osuTK.Input;
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// A visualisation of a single <see cref="PathControlPoint"/> in a <see cref="Slider"/>.
|
||||
/// A visualisation of a single <see cref="PathControlPoint"/> in an osu hit object with a path.
|
||||
/// </summary>
|
||||
public partial class PathControlPointPiece : BlueprintPiece<Slider>, IHasTooltip
|
||||
/// <typeparam name="T">The type of <see cref="OsuHitObject"/> which this <see cref="PathControlPointPiece{T}"/> visualises.</typeparam>
|
||||
public partial class PathControlPointPiece<T> : BlueprintPiece<T>, IHasTooltip
|
||||
where T : OsuHitObject, IHasPath
|
||||
{
|
||||
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
||||
public Action<PathControlPointPiece<T>, MouseButtonEvent> RequestSelection;
|
||||
|
||||
public Action<PathControlPoint> DragStarted;
|
||||
public Action<DragEvent> DragInProgress;
|
||||
@ -44,34 +46,34 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
public readonly BindableBool IsSelected = new BindableBool();
|
||||
public readonly PathControlPoint ControlPoint;
|
||||
|
||||
private readonly Slider slider;
|
||||
private readonly T hitObject;
|
||||
private readonly Container marker;
|
||||
private readonly Drawable markerRing;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
private IBindable<Vector2> sliderPosition;
|
||||
private IBindable<float> sliderScale;
|
||||
private IBindable<Vector2> hitObjectPosition;
|
||||
private IBindable<float> hitObjectScale;
|
||||
|
||||
[UsedImplicitly]
|
||||
private readonly IBindable<int> sliderVersion;
|
||||
private readonly IBindable<int> hitObjectVersion;
|
||||
|
||||
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
|
||||
public PathControlPointPiece(T hitObject, PathControlPoint controlPoint)
|
||||
{
|
||||
this.slider = slider;
|
||||
this.hitObject = hitObject;
|
||||
ControlPoint = controlPoint;
|
||||
|
||||
// we don't want to run the path type update on construction as it may inadvertently change the slider.
|
||||
cachePoints(slider);
|
||||
// we don't want to run the path type update on construction as it may inadvertently change the hit object.
|
||||
cachePoints(hitObject);
|
||||
|
||||
sliderVersion = slider.Path.Version.GetBoundCopy();
|
||||
hitObjectVersion = hitObject.Path.Version.GetBoundCopy();
|
||||
|
||||
// schedule ensure that updates are only applied after all operations from a single frame are applied.
|
||||
// this avoids inadvertently changing the slider path type for batch operations.
|
||||
sliderVersion.BindValueChanged(_ => Scheduler.AddOnce(() =>
|
||||
// this avoids inadvertently changing the hit object path type for batch operations.
|
||||
hitObjectVersion.BindValueChanged(_ => Scheduler.AddOnce(() =>
|
||||
{
|
||||
cachePoints(slider);
|
||||
cachePoints(hitObject);
|
||||
updatePathType();
|
||||
}));
|
||||
|
||||
@ -120,11 +122,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
sliderPosition = slider.PositionBindable.GetBoundCopy();
|
||||
sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
|
||||
hitObjectPosition = hitObject.PositionBindable.GetBoundCopy();
|
||||
hitObjectPosition.BindValueChanged(_ => updateMarkerDisplay());
|
||||
|
||||
sliderScale = slider.ScaleBindable.GetBoundCopy();
|
||||
sliderScale.BindValueChanged(_ => updateMarkerDisplay());
|
||||
hitObjectScale = hitObject.ScaleBindable.GetBoundCopy();
|
||||
hitObjectScale.BindValueChanged(_ => updateMarkerDisplay());
|
||||
|
||||
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
|
||||
|
||||
@ -212,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e) => DragEnded?.Invoke();
|
||||
|
||||
private void cachePoints(Slider slider) => PointsInSegment = slider.Path.PointsInSegment(ControlPoint);
|
||||
private void cachePoints(T hitObject) => PointsInSegment = hitObject.Path.PointsInSegment(ControlPoint);
|
||||
|
||||
/// <summary>
|
||||
/// Handles correction of invalid path types.
|
||||
@ -239,7 +241,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
/// </summary>
|
||||
private void updateMarkerDisplay()
|
||||
{
|
||||
Position = slider.StackedPosition + ControlPoint.Position;
|
||||
Position = hitObject.StackedPosition + ControlPoint.Position;
|
||||
|
||||
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
||||
|
||||
@ -249,7 +251,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
colour = colour.Lighten(1);
|
||||
|
||||
marker.Colour = colour;
|
||||
marker.Scale = new Vector2(slider.Scale);
|
||||
marker.Scale = new Vector2(hitObject.Scale);
|
||||
}
|
||||
|
||||
private Color4 getColourFromNodeType()
|
||||
|
@ -29,15 +29,16 @@ using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
public partial class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
||||
public partial class PathControlPointVisualiser<T> : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
||||
where T : OsuHitObject, IHasPath
|
||||
{
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // allow context menu to appear outside of the playfield.
|
||||
|
||||
internal readonly Container<PathControlPointPiece> Pieces;
|
||||
internal readonly Container<PathControlPointConnectionPiece> Connections;
|
||||
internal readonly Container<PathControlPointPiece<T>> Pieces;
|
||||
internal readonly Container<PathControlPointConnectionPiece<T>> Connections;
|
||||
|
||||
private readonly IBindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
|
||||
private readonly Slider slider;
|
||||
private readonly T hitObject;
|
||||
private readonly bool allowSelection;
|
||||
|
||||
private InputManager inputManager;
|
||||
@ -48,17 +49,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IDistanceSnapProvider snapProvider { get; set; }
|
||||
|
||||
public PathControlPointVisualiser(Slider slider, bool allowSelection)
|
||||
public PathControlPointVisualiser(T hitObject, bool allowSelection)
|
||||
{
|
||||
this.slider = slider;
|
||||
this.hitObject = hitObject;
|
||||
this.allowSelection = allowSelection;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
Connections = new Container<PathControlPointConnectionPiece> { RelativeSizeAxes = Axes.Both },
|
||||
Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both }
|
||||
Connections = new Container<PathControlPointConnectionPiece<T>> { RelativeSizeAxes = Axes.Both },
|
||||
Pieces = new Container<PathControlPointPiece<T>> { RelativeSizeAxes = Axes.Both }
|
||||
};
|
||||
}
|
||||
|
||||
@ -69,12 +70,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
inputManager = GetContainingInputManager();
|
||||
|
||||
controlPoints.CollectionChanged += onControlPointsChanged;
|
||||
controlPoints.BindTo(slider.Path.ControlPoints);
|
||||
controlPoints.BindTo(hitObject.Path.ControlPoints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the <see cref="PathControlPointPiece"/> corresponding to the given <paramref name="pathControlPoint"/>,
|
||||
/// and deselects all other <see cref="PathControlPointPiece"/>s.
|
||||
/// Selects the <see cref="PathControlPointPiece{T}"/> corresponding to the given <paramref name="pathControlPoint"/>,
|
||||
/// and deselects all other <see cref="PathControlPointPiece{T}"/>s.
|
||||
/// </summary>
|
||||
public void SetSelectionTo(PathControlPoint pathControlPoint)
|
||||
{
|
||||
@ -124,8 +125,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool isSplittable(PathControlPointPiece p) =>
|
||||
// A slider can only be split on control points which connect two different slider segments.
|
||||
private bool isSplittable(PathControlPointPiece<T> p) =>
|
||||
// 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();
|
||||
|
||||
private void onControlPointsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
@ -150,7 +151,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
var point = (PathControlPoint)e.NewItems[i];
|
||||
|
||||
Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
|
||||
Pieces.Add(new PathControlPointPiece<T>(hitObject, point).With(d =>
|
||||
{
|
||||
if (allowSelection)
|
||||
d.RequestSelection = selectionRequested;
|
||||
@ -160,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
d.DragEnded = dragEnded;
|
||||
}));
|
||||
|
||||
Connections.Add(new PathControlPointConnectionPiece(slider, e.NewStartingIndex + i));
|
||||
Connections.Add(new PathControlPointConnectionPiece<T>(hitObject, e.NewStartingIndex + i));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -219,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
}
|
||||
|
||||
private void selectionRequested(PathControlPointPiece piece, MouseButtonEvent e)
|
||||
private void selectionRequested(PathControlPointPiece<T> piece, MouseButtonEvent e)
|
||||
{
|
||||
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
||||
piece.IsSelected.Toggle();
|
||||
@ -234,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
/// </summary>
|
||||
/// <param name="piece">The control point piece that we want to change the path type of.</param>
|
||||
/// <param name="type">The path type we want to assign to the given control point piece.</param>
|
||||
private void updatePathType(PathControlPointPiece piece, PathType? type)
|
||||
private void updatePathType(PathControlPointPiece<T> piece, PathType? type)
|
||||
{
|
||||
int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint);
|
||||
|
||||
@ -252,7 +253,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
break;
|
||||
}
|
||||
|
||||
slider.Path.ExpectedDistance.Value = null;
|
||||
hitObject.Path.ExpectedDistance.Value = null;
|
||||
piece.ControlPoint.Type = type;
|
||||
}
|
||||
|
||||
@ -268,9 +269,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
|
||||
private void dragStarted(PathControlPoint controlPoint)
|
||||
{
|
||||
dragStartPositions = slider.Path.ControlPoints.Select(point => point.Position).ToArray();
|
||||
dragPathTypes = slider.Path.ControlPoints.Select(point => point.Type).ToArray();
|
||||
draggedControlPointIndex = slider.Path.ControlPoints.IndexOf(controlPoint);
|
||||
dragStartPositions = hitObject.Path.ControlPoints.Select(point => point.Position).ToArray();
|
||||
dragPathTypes = hitObject.Path.ControlPoints.Select(point => point.Type).ToArray();
|
||||
draggedControlPointIndex = hitObject.Path.ControlPoints.IndexOf(controlPoint);
|
||||
selectedControlPoints = new HashSet<PathControlPoint>(Pieces.Where(piece => piece.IsSelected.Value).Select(piece => piece.ControlPoint));
|
||||
|
||||
Debug.Assert(draggedControlPointIndex >= 0);
|
||||
@ -280,25 +281,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
|
||||
private void dragInProgress(DragEvent e)
|
||||
{
|
||||
Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray();
|
||||
var oldPosition = slider.Position;
|
||||
double oldStartTime = slider.StartTime;
|
||||
Vector2[] oldControlPoints = hitObject.Path.ControlPoints.Select(cp => cp.Position).ToArray();
|
||||
var oldPosition = hitObject.Position;
|
||||
double oldStartTime = hitObject.StartTime;
|
||||
|
||||
if (selectedControlPoints.Contains(slider.Path.ControlPoints[0]))
|
||||
if (selectedControlPoints.Contains(hitObject.Path.ControlPoints[0]))
|
||||
{
|
||||
// Special handling for selections containing head control point - the position of the slider changes which means the snapped position and time have to be taken into account
|
||||
// Special handling for selections containing head control point - the position of the hit object changes which means the snapped position and time have to be taken into account
|
||||
Vector2 newHeadPosition = Parent.ToScreenSpace(e.MousePosition + (dragStartPositions[0] - dragStartPositions[draggedControlPointIndex]));
|
||||
var result = snapProvider?.FindSnappedPositionAndTime(newHeadPosition);
|
||||
|
||||
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? newHeadPosition) - slider.Position;
|
||||
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? newHeadPosition) - hitObject.Position;
|
||||
|
||||
slider.Position += movementDelta;
|
||||
slider.StartTime = result?.Time ?? slider.StartTime;
|
||||
hitObject.Position += movementDelta;
|
||||
hitObject.StartTime = result?.Time ?? hitObject.StartTime;
|
||||
|
||||
for (int i = 1; i < slider.Path.ControlPoints.Count; i++)
|
||||
for (int i = 1; i < hitObject.Path.ControlPoints.Count; i++)
|
||||
{
|
||||
var controlPoint = slider.Path.ControlPoints[i];
|
||||
// Since control points are relative to the position of the slider, all points that are _not_ selected
|
||||
var controlPoint = hitObject.Path.ControlPoints[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).
|
||||
@ -310,7 +311,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
var result = snapProvider?.FindSnappedPositionAndTime(Parent.ToScreenSpace(e.MousePosition));
|
||||
|
||||
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? Parent.ToScreenSpace(e.MousePosition)) - dragStartPositions[draggedControlPointIndex] - slider.Position;
|
||||
Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? Parent.ToScreenSpace(e.MousePosition)) - dragStartPositions[draggedControlPointIndex] - hitObject.Position;
|
||||
|
||||
for (int i = 0; i < controlPoints.Count; ++i)
|
||||
{
|
||||
@ -321,23 +322,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
}
|
||||
|
||||
// Snap the path to the current beat divisor before checking length validity.
|
||||
slider.SnapTo(snapProvider);
|
||||
hitObject.SnapTo(snapProvider);
|
||||
|
||||
if (!slider.Path.HasValidLength)
|
||||
if (!hitObject.Path.HasValidLength)
|
||||
{
|
||||
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||
slider.Path.ControlPoints[i].Position = oldControlPoints[i];
|
||||
for (int i = 0; i < hitObject.Path.ControlPoints.Count; i++)
|
||||
hitObject.Path.ControlPoints[i].Position = oldControlPoints[i];
|
||||
|
||||
slider.Position = oldPosition;
|
||||
slider.StartTime = oldStartTime;
|
||||
hitObject.Position = oldPosition;
|
||||
hitObject.StartTime = oldStartTime;
|
||||
// Snap the path length again to undo the invalid length.
|
||||
slider.SnapTo(snapProvider);
|
||||
hitObject.SnapTo(snapProvider);
|
||||
return;
|
||||
}
|
||||
|
||||
// Maintain the path types in case they got defaulted to bezier at some point during the drag.
|
||||
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||
slider.Path.ControlPoints[i].Type = dragPathTypes[i];
|
||||
for (int i = 0; i < hitObject.Path.ControlPoints.Count; i++)
|
||||
hitObject.Path.ControlPoints[i].Type = dragPathTypes[i];
|
||||
}
|
||||
|
||||
private void dragEnded() => changeHandler?.EndChange();
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
private SliderBodyPiece bodyPiece;
|
||||
private HitCirclePiece headCirclePiece;
|
||||
private HitCirclePiece tailCirclePiece;
|
||||
private PathControlPointVisualiser controlPointVisualiser;
|
||||
private PathControlPointVisualiser<Slider> controlPointVisualiser;
|
||||
|
||||
private InputManager inputManager;
|
||||
|
||||
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
bodyPiece = new SliderBodyPiece(),
|
||||
headCirclePiece = new HitCirclePiece(),
|
||||
tailCirclePiece = new HitCirclePiece(),
|
||||
controlPointVisualiser = new PathControlPointVisualiser(HitObject, false)
|
||||
controlPointVisualiser = new PathControlPointVisualiser<Slider>(HitObject, false)
|
||||
};
|
||||
|
||||
setState(SliderPlacementState.Initial);
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
protected SliderCircleOverlay TailOverlay { get; private set; }
|
||||
|
||||
[CanBeNull]
|
||||
protected PathControlPointVisualiser ControlPointVisualiser { get; private set; }
|
||||
protected PathControlPointVisualiser<Slider> ControlPointVisualiser { get; private set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IDistanceSnapProvider snapProvider { get; set; }
|
||||
@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
{
|
||||
if (ControlPointVisualiser == null)
|
||||
{
|
||||
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(HitObject, true)
|
||||
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser<Slider>(HitObject, true)
|
||||
{
|
||||
RemoveControlPointsRequested = removeControlPoints,
|
||||
SplitControlPointsRequested = splitControlPoints
|
||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddStep("move mouse to common point", () =>
|
||||
{
|
||||
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
||||
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
||||
InputManager.MoveMouseTo(pos);
|
||||
});
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
|
@ -286,7 +286,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddStep("move mouse to controlpoint", () =>
|
||||
{
|
||||
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
||||
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
||||
InputManager.MoveMouseTo(pos);
|
||||
});
|
||||
AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft));
|
||||
|
Loading…
Reference in New Issue
Block a user