From 9634560d4b761a2a3aa90d78ffb1933e2c96c7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Mar 2021 21:36:28 +0100 Subject: [PATCH 1/4] Fix control point visualiser crashing after deselections `SliderSelectionBlueprint.OnDeselected()` would expire the `ControlPointVisualiser` on deselection, leading to its removal from the blueprint and eventual disposal, but still kept a separate reference to said visualiser in another field. This could lead to that stale reference to a disposed child getting read in `ReceivePositionalInputAt()`, crashing quite a ways down over at the framework side on futilely trying to compute the bounding box of a drawable with no parent. --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 3d3dff653a..befe3c6695 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -114,6 +114,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // throw away frame buffers on deselection. ControlPointVisualiser?.Expire(); + ControlPointVisualiser = null; + BodyPiece.RecyclePath(); } From e67c759eef36a409e4b57ab1c7643eb307280cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Mar 2021 22:44:31 +0100 Subject: [PATCH 2/4] Mark control point visualiser as possibly-null --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index befe3c6695..ba9bb3c485 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -28,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected SliderBodyPiece BodyPiece { get; private set; } protected SliderCircleSelectionBlueprint HeadBlueprint { get; private set; } protected SliderCircleSelectionBlueprint TailBlueprint { get; private set; } + + [CanBeNull] protected PathControlPointVisualiser ControlPointVisualiser { get; private set; } private readonly DrawableSlider slider; From 8e0536e1e2d2eb66a7572dcf27f19d65cb4f5e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Mar 2021 22:20:26 +0100 Subject: [PATCH 3/4] Add failing test scene --- .../Editing/TestSceneBlueprintSelection.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs new file mode 100644 index 0000000000..fd9c09fd5f --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs @@ -0,0 +1,70 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Tests.Beatmaps; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneBlueprintSelection : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + private BlueprintContainer blueprintContainer + => Editor.ChildrenOfType().First(); + + [Test] + public void TestSelectedObjectHasPriorityWhenOverlapping() + { + var firstSlider = new Slider + { + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2()), + new PathControlPoint(new Vector2(150, -50)), + new PathControlPoint(new Vector2(300, 0)) + }), + Position = new Vector2(0, 100) + }; + var secondSlider = new Slider + { + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2()), + new PathControlPoint(new Vector2(-50, 50)), + new PathControlPoint(new Vector2(-100, 100)) + }), + Position = new Vector2(200, 0) + }; + + AddStep("add overlapping sliders", () => + { + EditorBeatmap.Add(firstSlider); + EditorBeatmap.Add(secondSlider); + }); + AddStep("select first slider", () => EditorBeatmap.SelectedHitObjects.Add(firstSlider)); + + AddStep("move mouse to common point", () => + { + var pos = blueprintContainer.ChildrenOfType().ElementAt(1).ScreenSpaceDrawQuad.Centre; + InputManager.MoveMouseTo(pos); + }); + AddStep("right click", () => InputManager.Click(MouseButton.Right)); + + AddAssert("selection is unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == firstSlider); + } + } +} From dd48b68f8ad6fc513d17f9847d0863df2c90dc1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Mar 2021 22:20:40 +0100 Subject: [PATCH 4/4] Ensure selected blueprints are given selection priority --- osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 051d0766bf..7def7e1d16 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -338,7 +338,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private bool beginClickSelection(MouseButtonEvent e) { // Iterate from the top of the input stack (blueprints closest to the front of the screen first). - foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse()) + // Priority is given to already-selected blueprints. + foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse().OrderByDescending(b => b.IsSelected)) { if (!blueprint.IsHovered) continue;