From 9c4fbf45e9d906cc41c0cc2db28b6267b029c644 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:36:12 +0900 Subject: [PATCH 01/14] Add the ability to enter and exit the skin editor via on-screen buttons --- osu.Game/OsuGame.cs | 2 +- .../Overlays/Settings/Sections/SkinSection.cs | 10 ++++- osu.Game/Skinning/Editor/SkinEditor.cs | 7 +++ osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 44 +++++++++++++------ 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8119df43ac..6741c1cd13 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -757,7 +757,7 @@ namespace osu.Game loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); loadComponentSingleFile(wikiOverlay = new WikiOverlay(), overlayContent.Add, true); - loadComponentSingleFile(skinEditor = new SkinEditorOverlay(screenContainer), overlayContent.Add); + loadComponentSingleFile(skinEditor = new SkinEditorOverlay(screenContainer), overlayContent.Add, true); loadComponentSingleFile(new LoginOverlay { diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 316837d27d..e89f3424d9 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -13,6 +13,7 @@ using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Skinning; +using osu.Game.Skinning.Editor; using osuTK; namespace osu.Game.Overlays.Settings.Sections @@ -57,14 +58,19 @@ namespace osu.Game.Overlays.Settings.Sections private IBindable> managerUpdated; private IBindable> managerRemoved; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuConfigManager config, SkinEditorOverlay skinEditor) { FlowContent.Spacing = new Vector2(0, 5); Children = new Drawable[] { skinDropdown = new SkinSettingsDropdown(), + new SettingsButton + { + Text = "Skin layout editor", + Action = () => skinEditor?.Toggle(), + }, new ExportSkinButton(), new SettingsSlider { diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 07a94cac7a..8de64eecb8 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -56,6 +56,13 @@ namespace osu.Game.Skinning.Editor RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + new TriangleButton + { + Margin = new MarginPadding(10), + Text = "Close", + Width = 100, + Action = Hide, + }, headerText = new OsuTextFlowContainer { TextAnchor = Anchor.TopCentre, diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 88020896bb..34fac9c9cf 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -38,28 +38,44 @@ namespace osu.Game.Skinning.Editor { case GlobalAction.Back: if (skinEditor?.State.Value == Visibility.Visible) - { - skinEditor.ToggleVisibility(); - return true; - } - - break; + Hide(); + return true; case GlobalAction.ToggleSkinEditor: - if (skinEditor == null) - { - LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); - skinEditor.State.BindValueChanged(editorVisibilityChanged); - } - else - skinEditor.ToggleVisibility(); - + Toggle(); return true; } return false; } + public void Toggle() + { + if (skinEditor == null) + Show(); + else + skinEditor.ToggleVisibility(); + } + + public override void Hide() + { + base.Hide(); + skinEditor.Hide(); + } + + public override void Show() + { + base.Show(); + + if (skinEditor == null) + { + LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); + skinEditor.State.BindValueChanged(editorVisibilityChanged); + } + else + skinEditor.Show(); + } + private void editorVisibilityChanged(ValueChangedEvent visibility) { if (visibility.NewValue == Visibility.Visible) From 59457743e57bda7b2e17c5b1afd88dcba581e2da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:41:52 +0900 Subject: [PATCH 02/14] Move further to the right to avoid overlap with toolbox listing --- osu.Game/Skinning/Editor/SkinEditor.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 8de64eecb8..6c2310ce50 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -56,13 +56,6 @@ namespace osu.Game.Skinning.Editor RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new TriangleButton - { - Margin = new MarginPadding(10), - Text = "Close", - Width = 100, - Action = Hide, - }, headerText = new OsuTextFlowContainer { TextAnchor = Anchor.TopCentre, @@ -95,6 +88,13 @@ namespace osu.Game.Skinning.Editor Children = new Drawable[] { new SkinBlueprintContainer(targetScreen), + new TriangleButton + { + Margin = new MarginPadding(10), + Text = "Close", + Width = 100, + Action = Hide, + }, new FillFlowContainer { Direction = FillDirection.Horizontal, From 16a2e63bd43e8ae2060151599ca23d136418ac38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:44:02 +0900 Subject: [PATCH 03/14] Use existing localisation --- osu.Game/Skinning/Editor/SkinEditor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 6c2310ce50..8052f82c93 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Skinning.Editor @@ -91,7 +92,7 @@ namespace osu.Game.Skinning.Editor new TriangleButton { Margin = new MarginPadding(10), - Text = "Close", + Text = CommonStrings.ButtonsClose, Width = 100, Action = Hide, }, From 2beef89c23a779a12ded341bdb84e1f3094904e3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 15:20:33 +0900 Subject: [PATCH 04/14] Add empty juice stream placement blueprint (no implementation) --- .../JuiceStreamPlacementBlueprint.cs | 11 +++++++++ .../Edit/CatchHitObjectComposer.cs | 1 + .../Edit/JuiceStreamCompositionTool.cs | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs new file mode 100644 index 0000000000..0fb8dc32a2 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints +{ + public class JuiceStreamPlacementBlueprint : CatchPlacementBlueprint + { + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index d360274aa6..050c2f625d 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Catch.Edit protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { new FruitCompositionTool(), + new JuiceStreamCompositionTool(), new BananaShowerCompositionTool() }; diff --git a/osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs b/osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs new file mode 100644 index 0000000000..cb66e2952e --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; + +namespace osu.Game.Rulesets.Catch.Edit +{ + public class JuiceStreamCompositionTool : HitObjectCompositionTool + { + public JuiceStreamCompositionTool() + : base(nameof(JuiceStream)) + { + } + + public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders); + + public override PlacementBlueprint CreatePlacementBlueprint() => new JuiceStreamPlacementBlueprint(); + } +} From 64102d297237c203c86ae107ed75f73ef498f036 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 15:41:01 +0900 Subject: [PATCH 05/14] Add initial implementation of juice stream placement --- .../Components/PlacementEditablePath.cs | 44 +++++++ .../JuiceStreamPlacementBlueprint.cs | 107 ++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs new file mode 100644 index 0000000000..13dfe9db54 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Game.Rulesets.Catch.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class PlacementEditablePath : EditablePath + { + private JuiceStreamPathVertex originalNewVertex; + + public PlacementEditablePath(Func positionToDistance) + : base(positionToDistance) + { + } + + public void AddNewVertex() + { + var endVertex = Vertices[^1]; + int index = AddVertex(endVertex.Distance, endVertex.X); + + for (int i = 0; i < VertexCount; i++) + { + VertexStates[i].IsSelected = i == index; + VertexStates[i].VertexBeforeChange = Vertices[i]; + } + + originalNewVertex = Vertices[index]; + } + + /// + /// Move the vertex added by in the last time. + /// + public void MoveLastVertex(Vector2 screenSpacePosition) + { + Vector2 position = ToRelativePosition(screenSpacePosition); + double distanceDelta = PositionToDistance(position.Y) - originalNewVertex.Distance; + float xDelta = position.X - originalNewVertex.X; + MoveSelectedVertices(distanceDelta, xDelta); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index 0fb8dc32a2..32ab1f19c1 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -1,11 +1,118 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Edit; +using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Edit.Blueprints { public class JuiceStreamPlacementBlueprint : CatchPlacementBlueprint { + private readonly ScrollingPath scrollingPath; + + private readonly NestedOutlineContainer nestedOutlineContainer; + + private readonly PlacementEditablePath editablePath; + + private int lastEditablePathId = -1; + + public JuiceStreamPlacementBlueprint() + { + InternalChildren = new Drawable[] + { + scrollingPath = new ScrollingPath(), + nestedOutlineContainer = new NestedOutlineContainer(), + editablePath = new PlacementEditablePath(positionToDistance) + }; + } + + protected override void Update() + { + base.Update(); + + if (PlacementActive == PlacementState.Active) + editablePath.UpdateFrom(HitObjectContainer, HitObject); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + switch (PlacementActive) + { + case PlacementState.Waiting: + if (e.Button != MouseButton.Left) break; + + editablePath.AddNewVertex(); + BeginPlacement(true); + return true; + + case PlacementState.Active: + switch (e.Button) + { + case MouseButton.Left: + editablePath.AddNewVertex(); + return true; + + case MouseButton.Right: + EndPlacement(HitObject.Duration > 0); + return true; + } + + break; + } + + return base.OnMouseDown(e); + } + + public override void UpdateTimeAndPosition(SnapResult result) + { + switch (PlacementActive) + { + case PlacementState.Waiting: + if (!(result.Time is double snappedTime)) return; + + HitObject.OriginalX = ToLocalSpace(result.ScreenSpacePosition).X; + HitObject.StartTime = snappedTime; + break; + + case PlacementState.Active: + Vector2 unsnappedPosition = GetContainingInputManager().CurrentState.Mouse.Position; + editablePath.MoveLastVertex(unsnappedPosition); + break; + + default: + return; + } + + // Make sure the up-to-date position is used for outlines. + Vector2 startPosition = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + editablePath.Position = nestedOutlineContainer.Position = scrollingPath.Position = startPosition; + + updateHitObjectFromPath(); + } + + private void updateHitObjectFromPath() + { + if (lastEditablePathId == editablePath.PathId) + return; + + editablePath.UpdateHitObjectFromPath(HitObject); + ApplyDefaultsToHitObject(); + + scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject); + nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject); + + lastEditablePathId = editablePath.PathId; + } + + private double positionToDistance(float relativeYPosition) + { + double time = HitObjectContainer.TimeAtPosition(relativeYPosition, HitObject.StartTime); + return (time - HitObject.StartTime) * HitObject.Velocity; + } } } From 4e9ac5dc7b108234460050c22e2c986d9e4638b8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 15:46:23 +0900 Subject: [PATCH 06/14] Add tests of juice stream placement blueprint --- .../TestSceneJuiceStreamPlacementBlueprint.cs | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs new file mode 100644 index 0000000000..02861b1adf --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -0,0 +1,146 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK.Input; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public class TestSceneJuiceStreamPlacementBlueprint : CatchPlacementBlueprintTestScene + { + private const double velocity = 0.5; + + private JuiceStream lastObject => LastObject?.HitObject as JuiceStream; + + [BackgroundDependencyLoader] + private void load() + { + Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderTickRate = 5; + Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderMultiplier = velocity * 10; + } + + [Test] + public void TestBasicPlacement() + { + double[] times = { 300, 800 }; + float[] positions = { 100, 200 }; + addPlacementSteps(times, positions); + + AddAssert("juice stream is placed", () => lastObject != null); + AddAssert("start time is correct", () => Precision.AlmostEquals(lastObject.StartTime, times[0])); + AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1])); + AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0])); + AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1])); + } + + [Test] + public void TestEmptyNotCommitted() + { + addMoveAndClickSteps(100, 100); + addMoveAndClickSteps(100, 100); + addMoveAndClickSteps(100, 100, true); + AddAssert("juice stream not placed", () => lastObject == null); + } + + [Test] + public void TestMultipleSegments() + { + double[] times = { 100, 300, 500, 700 }; + float[] positions = { 100, 150, 100, 100 }; + addPlacementSteps(times, positions); + + AddAssert("has 4 vertices", () => lastObject.Path.ControlPoints.Count == 4); + addPathCheckStep(times, positions); + } + + [Test] + public void TestVelocityLimit() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 200, 300, 100 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, new float[] { 200, 200, 100 }); + } + + [Test] + public void TestClampedPositionIsRestored() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 200, 200, 0, 250 }; + + addMoveAndClickSteps(times[0], positions[0]); + addMoveAndClickSteps(times[1], positions[1]); + AddMoveStep(times[2], positions[2]); + addMoveAndClickSteps(times[2], positions[3], true); + + addPathCheckStep(times, new float[] { 200, 200, 250 }); + } + + [Test] + public void TestFirstVertexIsFixed() + { + double[] times = { 100, 200 }; + float[] positions = { 100, 300 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, new float[] { 100, 150 }); + } + + [Test] + public void TestOutOfOrder() + { + double[] times = { 100, 700, 500, 300 }; + float[] positions = { 100, 200, 150, 50 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, positions); + } + + [Test] + public void TestMoveBeforeFirstVertex() + { + double[] times = { 300, 500, 100 }; + float[] positions = { 100, 100, 100 }; + addPlacementSteps(times, positions); + AddAssert("start time is correct", () => Precision.AlmostEquals(lastObject.StartTime, times[0])); + AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1], 1e-3)); + } + + protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableJuiceStream((JuiceStream)hitObject); + + protected override PlacementBlueprint CreateBlueprint() => new JuiceStreamPlacementBlueprint(); + + private void addMoveAndClickSteps(double time, float position, bool end = false) + { + AddMoveStep(time, position); + AddClickStep(end ? MouseButton.Right : MouseButton.Left); + } + + private void addPlacementSteps(double[] times, float[] positions) + { + for (int i = 0; i < times.Length; i++) + addMoveAndClickSteps(times[i], positions[i], i == times.Length - 1); + } + + private void addPathCheckStep(double[] times, float[] positions) => AddStep("assert path is correct", () => + Assert.That(getPositions(times), Is.EqualTo(positions).Within(Precision.FLOAT_EPSILON))); + + private float[] getPositions(IEnumerable times) + { + JuiceStream hitObject = lastObject.AsNonNull(); + return times + .Select(time => (time - hitObject.StartTime) * hitObject.Velocity) + .Select(distance => hitObject.EffectiveX + hitObject.Path.PositionAt(distance / hitObject.Distance).X) + .ToArray(); + } + } +} From 3fd8de3b912c9b8db588ddeef75b1beccc485d8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:57:47 +0900 Subject: [PATCH 07/14] Fix skin editor's fake overlay potentially getting into a bad state --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 34fac9c9cf..1aedd90817 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -59,14 +59,13 @@ namespace osu.Game.Skinning.Editor public override void Hide() { - base.Hide(); + // base call intentionally omitted. skinEditor.Hide(); } public override void Show() { - base.Show(); - + // base call intentionally omitted. if (skinEditor == null) { LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); From 21053381c7046d5de3572c837d42394df9bc5e95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:59:00 +0900 Subject: [PATCH 08/14] Fix skin editor potentially eating `GlobalAction.Back` when not displayed --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 1aedd90817..2562e9c57c 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -37,8 +37,10 @@ namespace osu.Game.Skinning.Editor switch (action) { case GlobalAction.Back: - if (skinEditor?.State.Value == Visibility.Visible) - Hide(); + if (skinEditor?.State.Value != Visibility.Visible) + break; + + Hide(); return true; case GlobalAction.ToggleSkinEditor: From 986910a7e4398fb469c643e8feb53526d26d1493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 22 Jul 2021 22:43:35 +0200 Subject: [PATCH 09/14] Annotate dependency as possibly-null --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index e89f3424d9..9f3543d059 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -59,7 +60,7 @@ namespace osu.Game.Overlays.Settings.Sections private IBindable> managerRemoved; [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config, SkinEditorOverlay skinEditor) + private void load(OsuConfigManager config, [CanBeNull] SkinEditorOverlay skinEditor) { FlowContent.Spacing = new Vector2(0, 5); From d49d303bae69ce6969932e91ef192a81336c588d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:10:55 +0900 Subject: [PATCH 10/14] Call `GetContainingInputManager` at `LoadComplete` --- .../Edit/Blueprints/JuiceStreamPlacementBlueprint.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index 32ab1f19c1..cff5bc2417 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; @@ -21,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private int lastEditablePathId = -1; + private InputManager inputManager; + public JuiceStreamPlacementBlueprint() { InternalChildren = new Drawable[] @@ -39,6 +42,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints editablePath.UpdateFrom(HitObjectContainer, HitObject); } + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + } + protected override bool OnMouseDown(MouseDownEvent e) { switch (PlacementActive) @@ -80,7 +90,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints break; case PlacementState.Active: - Vector2 unsnappedPosition = GetContainingInputManager().CurrentState.Mouse.Position; + Vector2 unsnappedPosition = inputManager.CurrentState.Mouse.Position; editablePath.MoveLastVertex(unsnappedPosition); break; From 4509c8bcfbcf9fc2a9d6010d707868312fc13cea Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:13:55 +0900 Subject: [PATCH 11/14] Use the more consistent `lastVertex`, with a comment --- .../Blueprints/Components/PlacementEditablePath.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs index 13dfe9db54..fedda20b9d 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -9,7 +9,11 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { public class PlacementEditablePath : EditablePath { - private JuiceStreamPathVertex originalNewVertex; + /// + /// The original position of the last added vertex. + /// This is not same as the last vertex of the current path because the vertex ordering can change. + /// + private JuiceStreamPathVertex lastVertex; public PlacementEditablePath(Func positionToDistance) : base(positionToDistance) @@ -27,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components VertexStates[i].VertexBeforeChange = Vertices[i]; } - originalNewVertex = Vertices[index]; + lastVertex = Vertices[index]; } /// @@ -36,8 +40,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components public void MoveLastVertex(Vector2 screenSpacePosition) { Vector2 position = ToRelativePosition(screenSpacePosition); - double distanceDelta = PositionToDistance(position.Y) - originalNewVertex.Distance; - float xDelta = position.X - originalNewVertex.X; + double distanceDelta = PositionToDistance(position.Y) - lastVertex.Distance; + float xDelta = position.X - lastVertex.X; MoveSelectedVertices(distanceDelta, xDelta); } } From bd3386e770d86da2aee1c2cecf539b30dd6bf30f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:17:42 +0900 Subject: [PATCH 12/14] Fix previously placed vertices in juice stream placement A different UX than not fixing vertices. --- .../Edit/Blueprints/Components/PlacementEditablePath.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs index fedda20b9d..158872fbab 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components for (int i = 0; i < VertexCount; i++) { VertexStates[i].IsSelected = i == index; + VertexStates[i].IsFixed = i != index; VertexStates[i].VertexBeforeChange = Vertices[i]; } From b1fd6c0ded7b4a55ecf2489013fcac27ce9d8c3a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:45:33 +0900 Subject: [PATCH 13/14] Apply changes to tests of juice stream placement --- .../TestSceneJuiceStreamPlacementBlueprint.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs index 02861b1adf..cd1fa31b61 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -67,10 +67,19 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor [Test] public void TestVelocityLimit() { - double[] times = { 100, 300, 500 }; - float[] positions = { 200, 300, 100 }; + double[] times = { 100, 300 }; + float[] positions = { 200, 500 }; addPlacementSteps(times, positions); - addPathCheckStep(times, new float[] { 200, 200, 100 }); + addPathCheckStep(times, new float[] { 200, 300 }); + } + + [Test] + public void TestPreviousVerticesAreFixed() + { + double[] times = { 100, 300, 500, 700 }; + float[] positions = { 200, 400, 100, 500 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, new float[] { 200, 300, 200, 300 }); } [Test] From 88d9e2ec06aa450de5bb28fd95807ae40f0fac5c Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 23 Jul 2021 10:23:31 +0800 Subject: [PATCH 14/14] Guard against IndexOutOfRange when parsing launch args --- osu.Desktop/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index cbee1694ba..dc712f2593 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -32,7 +32,7 @@ namespace osu.Desktop var split = arg.Split('='); var key = split[0]; - var val = split[1]; + var val = split.Length > 1 ? split[1] : string.Empty; switch (key) {