mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 03:25:11 +08:00
Merge pull request #28509 from bdach/slider-anchor-type-switching
Add ability to cycle slider control point types via keyboard
This commit is contained in:
commit
3f138489a1
@ -15,6 +15,7 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
|||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
{
|
{
|
||||||
@ -177,6 +178,79 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
addAssertPointPositionChanged(points, i);
|
addAssertPointPositionChanged(points, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestChangingControlPointTypeViaTab()
|
||||||
|
{
|
||||||
|
createVisualiser(true);
|
||||||
|
|
||||||
|
addControlPointStep(new Vector2(200), PathType.LINEAR);
|
||||||
|
addControlPointStep(new Vector2(300));
|
||||||
|
addControlPointStep(new Vector2(500, 300));
|
||||||
|
addControlPointStep(new Vector2(700, 200));
|
||||||
|
addControlPointStep(new Vector2(500, 100));
|
||||||
|
|
||||||
|
AddStep("select first control point", () => visualiser.Pieces[0].IsSelected.Value = true);
|
||||||
|
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||||
|
assertControlPointPathType(0, PathType.BEZIER);
|
||||||
|
|
||||||
|
AddStep("press shift-tab", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LShift);
|
||||||
|
InputManager.Key(Key.Tab);
|
||||||
|
InputManager.ReleaseKey(Key.LShift);
|
||||||
|
});
|
||||||
|
assertControlPointPathType(0, PathType.LINEAR);
|
||||||
|
|
||||||
|
AddStep("press shift-tab", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LShift);
|
||||||
|
InputManager.Key(Key.Tab);
|
||||||
|
InputManager.ReleaseKey(Key.LShift);
|
||||||
|
});
|
||||||
|
assertControlPointPathType(0, PathType.BSpline(4));
|
||||||
|
|
||||||
|
AddStep("press shift-tab", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LShift);
|
||||||
|
InputManager.Key(Key.Tab);
|
||||||
|
InputManager.ReleaseKey(Key.LShift);
|
||||||
|
});
|
||||||
|
assertControlPointPathType(0, PathType.PERFECT_CURVE);
|
||||||
|
assertControlPointPathType(2, PathType.BSpline(4));
|
||||||
|
|
||||||
|
AddStep("select third last control point", () =>
|
||||||
|
{
|
||||||
|
visualiser.Pieces[0].IsSelected.Value = false;
|
||||||
|
visualiser.Pieces[2].IsSelected.Value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("press shift-tab", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LShift);
|
||||||
|
InputManager.Key(Key.Tab);
|
||||||
|
InputManager.ReleaseKey(Key.LShift);
|
||||||
|
});
|
||||||
|
assertControlPointPathType(2, PathType.PERFECT_CURVE);
|
||||||
|
|
||||||
|
AddRepeatStep("press tab", () => InputManager.Key(Key.Tab), 2);
|
||||||
|
assertControlPointPathType(0, PathType.BEZIER);
|
||||||
|
assertControlPointPathType(2, null);
|
||||||
|
|
||||||
|
AddStep("select first and third control points", () =>
|
||||||
|
{
|
||||||
|
visualiser.Pieces[0].IsSelected.Value = true;
|
||||||
|
visualiser.Pieces[2].IsSelected.Value = true;
|
||||||
|
});
|
||||||
|
AddStep("press alt-1", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.AltLeft);
|
||||||
|
InputManager.Key(Key.Number1);
|
||||||
|
InputManager.ReleaseKey(Key.AltLeft);
|
||||||
|
});
|
||||||
|
assertControlPointPathType(0, PathType.LINEAR);
|
||||||
|
assertControlPointPathType(2, PathType.LINEAR);
|
||||||
|
}
|
||||||
|
|
||||||
private void addAssertPointPositionChanged(Vector2[] points, int index)
|
private void addAssertPointPositionChanged(Vector2[] points, int index)
|
||||||
{
|
{
|
||||||
AddAssert($"Point at {points.ElementAt(index)} changed",
|
AddAssert($"Point at {points.ElementAt(index)} changed",
|
||||||
|
@ -2,13 +2,16 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
|
||||||
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -57,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertLength(200);
|
assertLength(200);
|
||||||
assertControlPointCount(2);
|
assertControlPointCount(2);
|
||||||
assertControlPointType(0, PathType.LINEAR);
|
assertFinalControlPointType(0, PathType.LINEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -71,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(2);
|
assertControlPointCount(2);
|
||||||
assertControlPointType(0, PathType.LINEAR);
|
assertFinalControlPointType(0, PathType.LINEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -89,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(3);
|
assertControlPointCount(3);
|
||||||
assertControlPointPosition(1, new Vector2(100, 0));
|
assertControlPointPosition(1, new Vector2(100, 0));
|
||||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
assertFinalControlPointType(0, PathType.PERFECT_CURVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -111,7 +114,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertControlPointCount(4);
|
assertControlPointCount(4);
|
||||||
assertControlPointPosition(1, new Vector2(100, 0));
|
assertControlPointPosition(1, new Vector2(100, 0));
|
||||||
assertControlPointPosition(2, new Vector2(100, 100));
|
assertControlPointPosition(2, new Vector2(100, 100));
|
||||||
assertControlPointType(0, PathType.BEZIER);
|
assertFinalControlPointType(0, PathType.BEZIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -130,8 +133,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(3);
|
assertControlPointCount(3);
|
||||||
assertControlPointPosition(1, new Vector2(100, 0));
|
assertControlPointPosition(1, new Vector2(100, 0));
|
||||||
assertControlPointType(0, PathType.LINEAR);
|
assertFinalControlPointType(0, PathType.LINEAR);
|
||||||
assertControlPointType(1, PathType.LINEAR);
|
assertFinalControlPointType(1, PathType.LINEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -149,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(2);
|
assertControlPointCount(2);
|
||||||
assertControlPointType(0, PathType.LINEAR);
|
assertFinalControlPointType(0, PathType.LINEAR);
|
||||||
assertLength(100);
|
assertLength(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(3);
|
assertControlPointCount(3);
|
||||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
assertFinalControlPointType(0, PathType.PERFECT_CURVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -195,7 +198,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(4);
|
assertControlPointCount(4);
|
||||||
assertControlPointType(0, PathType.BEZIER);
|
assertFinalControlPointType(0, PathType.BEZIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -215,8 +218,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertControlPointCount(3);
|
assertControlPointCount(3);
|
||||||
assertControlPointPosition(1, new Vector2(100, 0));
|
assertControlPointPosition(1, new Vector2(100, 0));
|
||||||
assertControlPointPosition(2, new Vector2(100));
|
assertControlPointPosition(2, new Vector2(100));
|
||||||
assertControlPointType(0, PathType.LINEAR);
|
assertFinalControlPointType(0, PathType.LINEAR);
|
||||||
assertControlPointType(1, PathType.LINEAR);
|
assertFinalControlPointType(1, PathType.LINEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -239,8 +242,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertControlPointCount(4);
|
assertControlPointCount(4);
|
||||||
assertControlPointPosition(1, new Vector2(100, 0));
|
assertControlPointPosition(1, new Vector2(100, 0));
|
||||||
assertControlPointPosition(2, new Vector2(100));
|
assertControlPointPosition(2, new Vector2(100));
|
||||||
assertControlPointType(0, PathType.LINEAR);
|
assertFinalControlPointType(0, PathType.LINEAR);
|
||||||
assertControlPointType(1, PathType.PERFECT_CURVE);
|
assertFinalControlPointType(1, PathType.PERFECT_CURVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -268,8 +271,46 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertControlPointPosition(2, new Vector2(100));
|
assertControlPointPosition(2, new Vector2(100));
|
||||||
assertControlPointPosition(3, new Vector2(200, 100));
|
assertControlPointPosition(3, new Vector2(200, 100));
|
||||||
assertControlPointPosition(4, new Vector2(200));
|
assertControlPointPosition(4, new Vector2(200));
|
||||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
assertFinalControlPointType(0, PathType.PERFECT_CURVE);
|
||||||
assertControlPointType(2, PathType.PERFECT_CURVE);
|
assertFinalControlPointType(2, PathType.PERFECT_CURVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestManualPathTypeControlViaKeyboard()
|
||||||
|
{
|
||||||
|
addMovementStep(new Vector2(200));
|
||||||
|
addClickStep(MouseButton.Left);
|
||||||
|
|
||||||
|
addMovementStep(new Vector2(300, 200));
|
||||||
|
addClickStep(MouseButton.Left);
|
||||||
|
|
||||||
|
addMovementStep(new Vector2(300));
|
||||||
|
|
||||||
|
assertControlPointTypeDuringPlacement(0, PathType.PERFECT_CURVE);
|
||||||
|
|
||||||
|
AddRepeatStep("press tab", () => InputManager.Key(Key.Tab), 2);
|
||||||
|
assertControlPointTypeDuringPlacement(0, PathType.LINEAR);
|
||||||
|
|
||||||
|
AddStep("press shift-tab", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ShiftLeft);
|
||||||
|
InputManager.Key(Key.Tab);
|
||||||
|
InputManager.ReleaseKey(Key.ShiftLeft);
|
||||||
|
});
|
||||||
|
assertControlPointTypeDuringPlacement(0, PathType.BSpline(4));
|
||||||
|
|
||||||
|
AddStep("start new segment via S", () => InputManager.Key(Key.S));
|
||||||
|
assertControlPointTypeDuringPlacement(2, PathType.LINEAR);
|
||||||
|
|
||||||
|
addMovementStep(new Vector2(400, 300));
|
||||||
|
addClickStep(MouseButton.Left);
|
||||||
|
|
||||||
|
addMovementStep(new Vector2(400));
|
||||||
|
addClickStep(MouseButton.Right);
|
||||||
|
|
||||||
|
assertPlaced(true);
|
||||||
|
assertFinalControlPointType(0, PathType.BSpline(4));
|
||||||
|
assertFinalControlPointType(2, PathType.PERFECT_CURVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -293,7 +334,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
addClickStep(MouseButton.Right);
|
addClickStep(MouseButton.Right);
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
|
|
||||||
assertControlPointType(0, PathType.BEZIER);
|
assertFinalControlPointType(0, PathType.BEZIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -312,11 +353,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertLength(808, tolerance: 10);
|
assertLength(808, tolerance: 10);
|
||||||
assertControlPointCount(5);
|
assertControlPointCount(5);
|
||||||
assertControlPointType(0, PathType.BSpline(4));
|
assertFinalControlPointType(0, PathType.BSpline(4));
|
||||||
assertControlPointType(1, null);
|
assertFinalControlPointType(1, null);
|
||||||
assertControlPointType(2, null);
|
assertFinalControlPointType(2, null);
|
||||||
assertControlPointType(3, null);
|
assertFinalControlPointType(3, null);
|
||||||
assertControlPointType(4, null);
|
assertFinalControlPointType(4, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -337,10 +378,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertLength(600, tolerance: 10);
|
assertLength(600, tolerance: 10);
|
||||||
assertControlPointCount(4);
|
assertControlPointCount(4);
|
||||||
assertControlPointType(0, PathType.BSpline(4));
|
assertFinalControlPointType(0, PathType.BSpline(4));
|
||||||
assertControlPointType(1, PathType.BSpline(4));
|
assertFinalControlPointType(1, PathType.BSpline(4));
|
||||||
assertControlPointType(2, PathType.BSpline(4));
|
assertFinalControlPointType(2, PathType.BSpline(4));
|
||||||
assertControlPointType(3, null);
|
assertFinalControlPointType(3, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -359,7 +400,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(3);
|
assertControlPointCount(3);
|
||||||
assertControlPointType(0, PathType.BEZIER);
|
assertFinalControlPointType(0, PathType.BEZIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -379,7 +420,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(3);
|
assertControlPointCount(3);
|
||||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
assertFinalControlPointType(0, PathType.PERFECT_CURVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -400,7 +441,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(3);
|
assertControlPointCount(3);
|
||||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
assertFinalControlPointType(0, PathType.PERFECT_CURVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -421,7 +462,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(3);
|
assertControlPointCount(3);
|
||||||
assertControlPointType(0, PathType.BEZIER);
|
assertFinalControlPointType(0, PathType.BEZIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -438,7 +479,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
assertPlaced(true);
|
assertPlaced(true);
|
||||||
assertControlPointCount(3);
|
assertControlPointCount(3);
|
||||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
assertFinalControlPointType(0, PathType.PERFECT_CURVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));
|
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));
|
||||||
@ -454,7 +495,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider()!.Path.ControlPoints.Count, () => Is.EqualTo(expected));
|
private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider()!.Path.ControlPoints.Count, () => Is.EqualTo(expected));
|
||||||
|
|
||||||
private void assertControlPointType(int index, PathType? type) => AddAssert($"control point {index} is {type?.ToString() ?? "inherit"}", () => getSlider()!.Path.ControlPoints[index].Type, () => Is.EqualTo(type));
|
private void assertControlPointTypeDuringPlacement(int index, PathType? type) => AddAssert($"control point {index} is {type?.ToString() ?? "inherit"}",
|
||||||
|
() => this.ChildrenOfType<PathControlPointPiece<Slider>>().ElementAt(index).ControlPoint.Type, () => Is.EqualTo(type));
|
||||||
|
|
||||||
|
private void assertFinalControlPointType(int index, PathType? type) => AddAssert($"control point {index} is {type?.ToString() ?? "inherit"}", () => getSlider()!.Path.ControlPoints[index].Type, () => Is.EqualTo(type));
|
||||||
|
|
||||||
private void assertControlPointPosition(int index, Vector2 position) =>
|
private void assertControlPointPosition(int index, Vector2 position) =>
|
||||||
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider()!.Path.ControlPoints[index].Position, 1));
|
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider()!.Path.ControlPoints[index].Position, 1));
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
public partial class PathControlPointVisualiser<T> : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
public partial class PathControlPointVisualiser<T> : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
||||||
where T : OsuHitObject, IHasPath
|
where T : OsuHitObject, IHasPath
|
||||||
{
|
{
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // allow context menu to appear outside of the playfield.
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // allow context menu to appear outside the playfield.
|
||||||
|
|
||||||
internal readonly Container<PathControlPointPiece<T>> Pieces;
|
internal readonly Container<PathControlPointPiece<T>> Pieces;
|
||||||
|
|
||||||
@ -196,6 +196,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
if (allowSelection)
|
if (allowSelection)
|
||||||
d.RequestSelection = selectionRequested;
|
d.RequestSelection = selectionRequested;
|
||||||
|
|
||||||
|
d.ControlPoint.Changed += controlPointChanged;
|
||||||
d.DragStarted = DragStarted;
|
d.DragStarted = DragStarted;
|
||||||
d.DragInProgress = DragInProgress;
|
d.DragInProgress = DragInProgress;
|
||||||
d.DragEnded = DragEnded;
|
d.DragEnded = DragEnded;
|
||||||
@ -209,6 +210,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
foreach (var point in e.OldItems.Cast<PathControlPoint>())
|
foreach (var point in e.OldItems.Cast<PathControlPoint>())
|
||||||
{
|
{
|
||||||
|
point.Changed -= controlPointChanged;
|
||||||
|
|
||||||
foreach (var piece in Pieces.Where(p => p.ControlPoint == point).ToArray())
|
foreach (var piece in Pieces.Where(p => p.ControlPoint == point).ToArray())
|
||||||
piece.RemoveAndDisposeImmediately();
|
piece.RemoveAndDisposeImmediately();
|
||||||
}
|
}
|
||||||
@ -217,6 +220,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void controlPointChanged() => updateCurveMenuItems();
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
if (Pieces.Any(piece => piece.IsHovered))
|
if (Pieces.Any(piece => piece.IsHovered))
|
||||||
@ -245,6 +250,86 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once StaticMemberInGenericType
|
||||||
|
private static readonly PathType?[] path_types =
|
||||||
|
[
|
||||||
|
PathType.LINEAR,
|
||||||
|
PathType.BEZIER,
|
||||||
|
PathType.PERFECT_CURVE,
|
||||||
|
PathType.BSpline(4),
|
||||||
|
null,
|
||||||
|
];
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
|
{
|
||||||
|
if (e.Repeat)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case Key.Tab:
|
||||||
|
{
|
||||||
|
var selectedPieces = Pieces.Where(p => p.IsSelected.Value).ToArray();
|
||||||
|
if (selectedPieces.Length != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var selectedPiece = selectedPieces.Single();
|
||||||
|
var selectedPoint = selectedPiece.ControlPoint;
|
||||||
|
|
||||||
|
var validTypes = path_types;
|
||||||
|
|
||||||
|
if (selectedPoint == controlPoints[0])
|
||||||
|
validTypes = validTypes.Where(t => t != null).ToArray();
|
||||||
|
|
||||||
|
int currentTypeIndex = Array.IndexOf(validTypes, selectedPoint.Type);
|
||||||
|
|
||||||
|
if (currentTypeIndex < 0 && e.ShiftPressed)
|
||||||
|
currentTypeIndex = 0;
|
||||||
|
|
||||||
|
changeHandler?.BeginChange();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
currentTypeIndex = (validTypes.Length + currentTypeIndex + (e.ShiftPressed ? -1 : 1)) % validTypes.Length;
|
||||||
|
|
||||||
|
updatePathTypeOfSelectedPieces(validTypes[currentTypeIndex]);
|
||||||
|
} while (selectedPoint.Type != validTypes[currentTypeIndex]);
|
||||||
|
|
||||||
|
changeHandler?.EndChange();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Key.Number1:
|
||||||
|
case Key.Number2:
|
||||||
|
case Key.Number3:
|
||||||
|
case Key.Number4:
|
||||||
|
case Key.Number5:
|
||||||
|
{
|
||||||
|
if (!e.AltPressed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var type = path_types[e.Key - Key.Number1];
|
||||||
|
|
||||||
|
if (Pieces[0].IsSelected.Value && type == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
updatePathTypeOfSelectedPieces(type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
foreach (var p in Pieces)
|
||||||
|
p.ControlPoint.Changed -= controlPointChanged;
|
||||||
|
}
|
||||||
|
|
||||||
private void selectionRequested(PathControlPointPiece<T> piece, MouseButtonEvent e)
|
private void selectionRequested(PathControlPointPiece<T> piece, MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
||||||
@ -254,16 +339,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to set the given control point piece to the given path type.
|
/// Attempts to set all selected control point pieces to the given path type.
|
||||||
/// If that would fail, try to change the path such that it instead succeeds
|
/// If that fails, try to change the path such that it instead succeeds
|
||||||
/// in a UX-friendly way.
|
/// in a UX-friendly way.
|
||||||
/// </summary>
|
/// </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>
|
/// <param name="type">The path type we want to assign to the given control point piece.</param>
|
||||||
private void updatePathType(PathControlPointPiece<T> piece, PathType? type)
|
private void updatePathTypeOfSelectedPieces(PathType? type)
|
||||||
{
|
{
|
||||||
var pointsInSegment = hitObject.Path.PointsInSegment(piece.ControlPoint);
|
changeHandler?.BeginChange();
|
||||||
int indexInSegment = pointsInSegment.IndexOf(piece.ControlPoint);
|
|
||||||
|
foreach (var p in Pieces.Where(p => p.IsSelected.Value))
|
||||||
|
{
|
||||||
|
var pointsInSegment = hitObject.Path.PointsInSegment(p.ControlPoint);
|
||||||
|
int indexInSegment = pointsInSegment.IndexOf(p.ControlPoint);
|
||||||
|
|
||||||
if (type?.Type == SplineType.PerfectCurve)
|
if (type?.Type == SplineType.PerfectCurve)
|
||||||
{
|
{
|
||||||
@ -277,7 +365,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
hitObject.Path.ExpectedDistance.Value = null;
|
hitObject.Path.ExpectedDistance.Value = null;
|
||||||
piece.ControlPoint.Type = type;
|
p.ControlPoint.Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureValidPathTypes();
|
||||||
|
|
||||||
|
changeHandler?.EndChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
@ -290,6 +383,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
private int draggedControlPointIndex;
|
private int draggedControlPointIndex;
|
||||||
private HashSet<PathControlPoint> selectedControlPoints;
|
private HashSet<PathControlPoint> selectedControlPoints;
|
||||||
|
|
||||||
|
private List<MenuItem> curveTypeItems;
|
||||||
|
|
||||||
public void DragStarted(PathControlPoint controlPoint)
|
public void DragStarted(PathControlPoint controlPoint)
|
||||||
{
|
{
|
||||||
dragStartPositions = hitObject.Path.ControlPoints.Select(point => point.Position).ToArray();
|
dragStartPositions = hitObject.Path.ControlPoints.Select(point => point.Position).ToArray();
|
||||||
@ -386,22 +481,27 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
var splittablePieces = selectedPieces.Where(isSplittable).ToList();
|
var splittablePieces = selectedPieces.Where(isSplittable).ToList();
|
||||||
int splittableCount = splittablePieces.Count;
|
int splittableCount = splittablePieces.Count;
|
||||||
|
|
||||||
List<MenuItem> curveTypeItems = new List<MenuItem>();
|
curveTypeItems = new List<MenuItem>();
|
||||||
|
|
||||||
if (!selectedPieces.Contains(Pieces[0]))
|
foreach (PathType? type in path_types)
|
||||||
{
|
{
|
||||||
curveTypeItems.Add(createMenuItemForPathType(null));
|
// special inherit case
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
if (selectedPieces.Contains(Pieces[0]))
|
||||||
|
continue;
|
||||||
|
|
||||||
curveTypeItems.Add(new OsuMenuItemSpacer());
|
curveTypeItems.Add(new OsuMenuItemSpacer());
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: hide/disable items which aren't valid for selected points
|
curveTypeItems.Add(createMenuItemForPathType(type));
|
||||||
curveTypeItems.Add(createMenuItemForPathType(PathType.LINEAR));
|
}
|
||||||
curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECT_CURVE));
|
|
||||||
curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER));
|
|
||||||
curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(4)));
|
|
||||||
|
|
||||||
if (selectedPieces.Any(piece => piece.ControlPoint.Type?.Type == SplineType.Catmull))
|
if (selectedPieces.Any(piece => piece.ControlPoint.Type?.Type == SplineType.Catmull))
|
||||||
|
{
|
||||||
|
curveTypeItems.Add(new OsuMenuItemSpacer());
|
||||||
curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL));
|
curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL));
|
||||||
|
}
|
||||||
|
|
||||||
var menuItems = new List<MenuItem>
|
var menuItems = new List<MenuItem>
|
||||||
{
|
{
|
||||||
@ -424,26 +524,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
() => DeleteSelected())
|
() => DeleteSelected())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
updateCurveMenuItems();
|
||||||
|
|
||||||
return menuItems.ToArray();
|
return menuItems.ToArray();
|
||||||
|
|
||||||
|
CurveTypeMenuItem createMenuItemForPathType(PathType? type) => new CurveTypeMenuItem(type, _ => updatePathTypeOfSelectedPieces(type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MenuItem createMenuItemForPathType(PathType? type)
|
private void updateCurveMenuItems()
|
||||||
|
{
|
||||||
|
if (curveTypeItems == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var item in curveTypeItems.OfType<CurveTypeMenuItem>())
|
||||||
{
|
{
|
||||||
int totalCount = Pieces.Count(p => p.IsSelected.Value);
|
int totalCount = Pieces.Count(p => p.IsSelected.Value);
|
||||||
int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type == type);
|
int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type == item.PathType);
|
||||||
|
|
||||||
var item = new TernaryStateRadioMenuItem(type?.Description ?? "Inherit", MenuItemType.Standard, _ =>
|
|
||||||
{
|
|
||||||
changeHandler?.BeginChange();
|
|
||||||
|
|
||||||
foreach (var p in Pieces.Where(p => p.IsSelected.Value))
|
|
||||||
updatePathType(p, type);
|
|
||||||
|
|
||||||
EnsureValidPathTypes();
|
|
||||||
|
|
||||||
changeHandler?.EndChange();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (countOfState == totalCount)
|
if (countOfState == totalCount)
|
||||||
item.State.Value = TernaryState.True;
|
item.State.Value = TernaryState.True;
|
||||||
@ -451,8 +548,18 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
item.State.Value = TernaryState.Indeterminate;
|
item.State.Value = TernaryState.Indeterminate;
|
||||||
else
|
else
|
||||||
item.State.Value = TernaryState.False;
|
item.State.Value = TernaryState.False;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return item;
|
private class CurveTypeMenuItem : TernaryStateRadioMenuItem
|
||||||
|
{
|
||||||
|
public readonly PathType? PathType;
|
||||||
|
|
||||||
|
public CurveTypeMenuItem(PathType? pathType, Action<TernaryState> action)
|
||||||
|
: base(pathType?.Description ?? "Inherit", MenuItemType.Standard, action)
|
||||||
|
{
|
||||||
|
PathType = pathType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
private PathControlPoint segmentStart;
|
private PathControlPoint segmentStart;
|
||||||
private PathControlPoint cursor;
|
private PathControlPoint cursor;
|
||||||
private int currentSegmentLength;
|
private int currentSegmentLength;
|
||||||
|
private bool usingCustomSegmentType;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
@ -149,12 +150,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
case SliderPlacementState.ControlPoints:
|
case SliderPlacementState.ControlPoints:
|
||||||
if (canPlaceNewControlPoint(out var lastPoint))
|
if (canPlaceNewControlPoint(out var lastPoint))
|
||||||
{
|
placeNewControlPoint();
|
||||||
// Place a new point by detatching the current cursor.
|
|
||||||
updateCursor();
|
|
||||||
cursor = null;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
|
beginNewSegment(lastPoint);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beginNewSegment(PathControlPoint lastPoint)
|
||||||
{
|
{
|
||||||
// Transform the last point into a new segment.
|
// Transform the last point into a new segment.
|
||||||
Debug.Assert(lastPoint != null);
|
Debug.Assert(lastPoint != null);
|
||||||
@ -163,12 +169,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
segmentStart.Type = PathType.LINEAR;
|
segmentStart.Type = PathType.LINEAR;
|
||||||
|
|
||||||
currentSegmentLength = 1;
|
currentSegmentLength = 1;
|
||||||
}
|
usingCustomSegmentType = false;
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e)
|
protected override bool OnDragStart(DragStartEvent e)
|
||||||
@ -223,6 +224,72 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
base.OnMouseUp(e);
|
base.OnMouseUp(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly PathType[] path_types =
|
||||||
|
[
|
||||||
|
PathType.LINEAR,
|
||||||
|
PathType.BEZIER,
|
||||||
|
PathType.PERFECT_CURVE,
|
||||||
|
PathType.BSpline(4),
|
||||||
|
];
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
|
{
|
||||||
|
if (e.Repeat)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (state != SliderPlacementState.ControlPoints)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case Key.S:
|
||||||
|
{
|
||||||
|
if (!canPlaceNewControlPoint(out _))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
placeNewControlPoint();
|
||||||
|
var last = HitObject.Path.ControlPoints.Last(p => p != cursor);
|
||||||
|
beginNewSegment(last);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Key.Number1:
|
||||||
|
case Key.Number2:
|
||||||
|
case Key.Number3:
|
||||||
|
case Key.Number4:
|
||||||
|
{
|
||||||
|
if (!e.AltPressed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
usingCustomSegmentType = true;
|
||||||
|
segmentStart.Type = path_types[e.Key - Key.Number1];
|
||||||
|
controlPointVisualiser.EnsureValidPathTypes();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Key.Tab:
|
||||||
|
{
|
||||||
|
usingCustomSegmentType = true;
|
||||||
|
|
||||||
|
int currentTypeIndex = segmentStart.Type.HasValue ? Array.IndexOf(path_types, segmentStart.Type.Value) : -1;
|
||||||
|
|
||||||
|
if (currentTypeIndex < 0 && e.ShiftPressed)
|
||||||
|
currentTypeIndex = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
currentTypeIndex = (path_types.Length + currentTypeIndex + (e.ShiftPressed ? -1 : 1)) % path_types.Length;
|
||||||
|
segmentStart.Type = path_types[currentTypeIndex];
|
||||||
|
controlPointVisualiser.EnsureValidPathTypes();
|
||||||
|
} while (segmentStart.Type != path_types[currentTypeIndex]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -246,6 +313,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
private void updatePathType()
|
private void updatePathType()
|
||||||
{
|
{
|
||||||
|
if (usingCustomSegmentType)
|
||||||
|
{
|
||||||
|
controlPointVisualiser.EnsureValidPathTypes();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (state == SliderPlacementState.Drawing)
|
if (state == SliderPlacementState.Drawing)
|
||||||
{
|
{
|
||||||
segmentStart.Type = PathType.BSpline(4);
|
segmentStart.Type = PathType.BSpline(4);
|
||||||
@ -316,6 +389,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
return lastPiece.IsHovered != true;
|
return lastPiece.IsHovered != true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void placeNewControlPoint()
|
||||||
|
{
|
||||||
|
// Place a new point by detatching the current cursor.
|
||||||
|
updateCursor();
|
||||||
|
cursor = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateSlider()
|
private void updateSlider()
|
||||||
{
|
{
|
||||||
if (state == SliderPlacementState.Drawing)
|
if (state == SliderPlacementState.Drawing)
|
||||||
|
@ -15,6 +15,13 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
public SliderCompositionTool()
|
public SliderCompositionTool()
|
||||||
: base(nameof(Slider))
|
: base(nameof(Slider))
|
||||||
{
|
{
|
||||||
|
TooltipText = """
|
||||||
|
Left click for new point.
|
||||||
|
Left click twice or S key for new segment.
|
||||||
|
Tab, Shift-Tab, or Alt-1~4 to change current segment type.
|
||||||
|
Right click to finish.
|
||||||
|
Click and drag for drawing mode.
|
||||||
|
""";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders);
|
public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders);
|
||||||
|
@ -215,14 +215,14 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
toolboxCollection.Items = CompositionTools
|
toolboxCollection.Items = CompositionTools
|
||||||
.Prepend(new SelectTool())
|
.Prepend(new SelectTool())
|
||||||
.Select(t => new RadioButton(t.Name, () => toolSelected(t), t.CreateIcon))
|
.Select(t => new HitObjectCompositionToolButton(t, () => toolSelected(t)))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var item in toolboxCollection.Items)
|
foreach (var item in toolboxCollection.Items)
|
||||||
{
|
{
|
||||||
item.Selected.DisabledChanged += isDisabled =>
|
item.Selected.DisabledChanged += isDisabled =>
|
||||||
{
|
{
|
||||||
item.TooltipText = isDisabled ? "Add at least one timing point first!" : string.Empty;
|
item.TooltipText = isDisabled ? "Add at least one timing point first!" : ((HitObjectCompositionToolButton)item).TooltipText;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
osu.Game/Rulesets/Edit/HitObjectCompositionToolButton.cs
Normal file
22
osu.Game/Rulesets/Edit/HitObjectCompositionToolButton.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 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 osu.Game.Rulesets.Edit.Tools;
|
||||||
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Edit
|
||||||
|
{
|
||||||
|
public class HitObjectCompositionToolButton : RadioButton
|
||||||
|
{
|
||||||
|
public HitObjectCompositionTool Tool { get; }
|
||||||
|
|
||||||
|
public HitObjectCompositionToolButton(HitObjectCompositionTool tool, Action? action)
|
||||||
|
: base(tool.Name, action, tool.CreateIcon)
|
||||||
|
{
|
||||||
|
Tool = tool;
|
||||||
|
|
||||||
|
TooltipText = tool.TooltipText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit.Tools
|
namespace osu.Game.Rulesets.Edit.Tools
|
||||||
{
|
{
|
||||||
@ -11,14 +10,16 @@ namespace osu.Game.Rulesets.Edit.Tools
|
|||||||
{
|
{
|
||||||
public readonly string Name;
|
public readonly string Name;
|
||||||
|
|
||||||
|
public LocalisableString TooltipText { get; init; }
|
||||||
|
|
||||||
protected HitObjectCompositionTool(string name)
|
protected HitObjectCompositionTool(string name)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract PlacementBlueprint CreatePlacementBlueprint();
|
public abstract PlacementBlueprint? CreatePlacementBlueprint();
|
||||||
|
|
||||||
public virtual Drawable CreateIcon() => null;
|
public virtual Drawable? CreateIcon() => null;
|
||||||
|
|
||||||
public override string ToString() => Name;
|
public override string ToString() => Name;
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,11 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A function which creates a drawable icon to represent this item. If null, a sane default should be used.
|
/// A function which creates a drawable icon to represent this item. If null, a sane default should be used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Func<Drawable>? CreateIcon;
|
public readonly Func<Drawable?>? CreateIcon;
|
||||||
|
|
||||||
private readonly Action? action;
|
private readonly Action? action;
|
||||||
|
|
||||||
public RadioButton(string label, Action? action, Func<Drawable>? createIcon = null)
|
public RadioButton(string label, Action? action, Func<Drawable?>? createIcon = null)
|
||||||
{
|
{
|
||||||
Label = label;
|
Label = label;
|
||||||
CreateIcon = createIcon;
|
CreateIcon = createIcon;
|
||||||
|
Loading…
Reference in New Issue
Block a user