// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; 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.Drawables; using osu.Game.Tests.Visual; using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Osu.Tests.Editor { public partial class TestSceneSliderSelectionBlueprint : SelectionBlueprintTestScene { private Slider slider = null!; private DrawableSlider drawableObject = null!; private TestSliderBlueprint blueprint = null!; [SetUp] public void Setup() => Schedule(() => { Clear(); slider = new Slider { Position = new Vector2(256, 192), Path = new SliderPath(PathType.BEZIER, new[] { Vector2.Zero, new Vector2(150, 150), new Vector2(300, 0) }) }; slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 }); Add(drawableObject = new DrawableSlider(slider)); AddBlueprint(blueprint = new TestSliderBlueprint(slider), drawableObject); }); [Test] public void TestInitialState() { checkPositions(); } [Test] public void TestMoveHitObject() { moveHitObject(); checkPositions(); } [Test] public void TestMoveAfterApplyingDefaults() { AddStep("apply defaults", () => slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 })); moveHitObject(); checkPositions(); } [Test] public void TestStackedHitObject() { AddStep("set stacking", () => slider.StackHeight = 5); checkPositions(); } [Test] public void TestSingleControlPointSelection() { moveMouseToControlPoint(0); AddStep("click", () => InputManager.Click(MouseButton.Left)); checkControlPointSelected(0, true); checkControlPointSelected(1, false); } [Test] public void TestSingleControlPointDeselectionViaOtherControlPoint() { moveMouseToControlPoint(0); AddStep("click", () => InputManager.Click(MouseButton.Left)); moveMouseToControlPoint(1); AddStep("click", () => InputManager.Click(MouseButton.Left)); checkControlPointSelected(0, false); checkControlPointSelected(1, true); } [Test] public void TestSingleControlPointDeselectionViaClickOutside() { moveMouseToControlPoint(0); AddStep("click", () => InputManager.Click(MouseButton.Left)); AddStep("move mouse outside control point", () => InputManager.MoveMouseTo(drawableObject)); AddStep("click", () => InputManager.Click(MouseButton.Left)); checkControlPointSelected(0, false); checkControlPointSelected(1, false); } [Test] public void TestMultipleControlPointSelection() { moveMouseToControlPoint(0); AddStep("click", () => InputManager.Click(MouseButton.Left)); moveMouseToControlPoint(1); AddStep("ctrl + click", () => { InputManager.PressKey(Key.ControlLeft); InputManager.Click(MouseButton.Left); InputManager.ReleaseKey(Key.ControlLeft); }); checkControlPointSelected(0, true); checkControlPointSelected(1, true); } [Test] public void TestMultipleControlPointDeselectionViaOtherControlPoint() { moveMouseToControlPoint(0); AddStep("click", () => InputManager.Click(MouseButton.Left)); moveMouseToControlPoint(1); AddStep("ctrl + click", () => { InputManager.PressKey(Key.ControlLeft); InputManager.Click(MouseButton.Left); InputManager.ReleaseKey(Key.ControlLeft); }); moveMouseToControlPoint(2); AddStep("click", () => InputManager.Click(MouseButton.Left)); checkControlPointSelected(0, false); checkControlPointSelected(1, false); } [Test] public void TestMultipleControlPointDeselectionViaClickOutside() { moveMouseToControlPoint(0); AddStep("click", () => InputManager.Click(MouseButton.Left)); moveMouseToControlPoint(1); AddStep("ctrl + click", () => { InputManager.PressKey(Key.ControlLeft); InputManager.Click(MouseButton.Left); InputManager.ReleaseKey(Key.ControlLeft); }); AddStep("move mouse outside control point", () => InputManager.MoveMouseTo(drawableObject)); AddStep("click", () => InputManager.Click(MouseButton.Left)); checkControlPointSelected(0, false); checkControlPointSelected(1, false); } [Test] public void TestAdjustLength() { AddStep("move mouse to drag marker", () => { Vector2 position = slider.Position + slider.Path.PositionAt(1) + new Vector2(60, 0); InputManager.MoveMouseTo(drawableObject.Parent!.ToScreenSpace(position)); }); AddStep("start drag", () => InputManager.PressButton(MouseButton.Left)); AddStep("move mouse to control point 1", () => { Vector2 position = slider.Position + slider.Path.ControlPoints[1].Position + new Vector2(60, 0); InputManager.MoveMouseTo(drawableObject.Parent!.ToScreenSpace(position)); }); AddStep("end adjust length", () => InputManager.ReleaseButton(MouseButton.Left)); AddAssert("expected distance halved", () => Precision.AlmostEquals(slider.Path.Distance, 172.2, 0.1)); AddStep("move mouse to drag marker", () => { Vector2 position = slider.Position + slider.Path.PositionAt(1) + new Vector2(60, 0); InputManager.MoveMouseTo(drawableObject.Parent!.ToScreenSpace(position)); }); AddStep("start drag", () => InputManager.PressButton(MouseButton.Left)); AddStep("move mouse beyond last control point", () => { Vector2 position = slider.Position + slider.Path.ControlPoints[2].Position + new Vector2(100, 0); InputManager.MoveMouseTo(drawableObject.Parent!.ToScreenSpace(position)); }); AddStep("end adjust length", () => InputManager.ReleaseButton(MouseButton.Left)); AddAssert("expected distance is calculated distance", () => Precision.AlmostEquals(slider.Path.Distance, slider.Path.CalculatedDistance, 0.1)); moveMouseToControlPoint(1); AddAssert("expected distance is unchanged", () => Precision.AlmostEquals(slider.Path.Distance, slider.Path.CalculatedDistance, 0.1)); } private void moveHitObject() { AddStep("move hitobject", () => { slider.Position = new Vector2(300, 225); }); } private void checkPositions() { AddAssert("body positioned correctly", () => blueprint.BodyPiece.Position == slider.StackedPosition); AddAssert("head positioned correctly", () => Precision.AlmostEquals(blueprint.HeadOverlay.CirclePiece.ScreenSpaceDrawQuad.Centre, drawableObject.HeadCircle.ScreenSpaceDrawQuad.Centre)); AddAssert("tail positioned correctly", () => Precision.AlmostEquals(blueprint.TailOverlay.CirclePiece.ScreenSpaceDrawQuad.Centre, drawableObject.TailCircle.ScreenSpaceDrawQuad.Centre)); AddAssert("end drag marker positioned correctly", () => Precision.AlmostEquals(blueprint.TailOverlay.EndDragMarker!.ToScreenSpace(blueprint.TailOverlay.EndDragMarker.OriginPosition), drawableObject.TailCircle.ScreenSpaceDrawQuad.Centre, 2)); } private void moveMouseToControlPoint(int index) { AddStep($"move mouse to control point {index}", () => { Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position; InputManager.MoveMouseTo(drawableObject.Parent!.ToScreenSpace(position)); }); } private void checkControlPointSelected(int index, bool selected) => AddAssert($"control point {index} {(selected ? "selected" : "not selected")}", () => blueprint.ControlPointVisualiser!.Pieces[index].IsSelected.Value == selected); private partial class TestSliderBlueprint : SliderSelectionBlueprint { 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 TestSliderBlueprint(Slider slider) : base(slider) { } protected override SliderCircleOverlay CreateCircleOverlay(Slider slider, SliderPosition position) => new TestSliderCircleOverlay(slider, position); } private partial class TestSliderCircleOverlay : SliderCircleOverlay { public new HitCirclePiece CirclePiece => base.CirclePiece; public TestSliderCircleOverlay(Slider slider, SliderPosition position) : base(slider, position) { } } } }