diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs index 8641663ce859..623cefff6bcd 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs @@ -25,6 +25,35 @@ public partial class TestSceneOsuComposerSelection : TestSceneOsuEditor { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + [Test] + public void TestSelectAfterFadedOut() + { + var slider = new Slider + { + StartTime = 0, + Position = new Vector2(100, 100), + Path = new SliderPath + { + ControlPoints = + { + new PathControlPoint(), + new PathControlPoint(new Vector2(100)) + } + } + }; + AddStep("add slider", () => EditorBeatmap.Add(slider)); + + moveMouseToObject(() => slider); + + AddStep("seek after end", () => EditorClock.Seek(750)); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("slider not selected", () => EditorBeatmap.SelectedHitObjects.Count == 0); + + AddStep("seek to visible", () => EditorClock.Seek(650)); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddUntilStep("slider selected", () => EditorBeatmap.SelectedHitObjects.Single() == slider); + } + [Test] public void TestContextMenuShownCorrectlyForSelectedSlider() { diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs index bdd19f91065c..8e8972a665d2 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs @@ -22,7 +22,13 @@ public abstract partial class OsuSelectionBlueprint : HitObjectSelectionBluep protected override bool AlwaysShowWhenSelected => true; protected override bool ShouldBeAlive => base.ShouldBeAlive - || (DrawableObject is not DrawableSpinner && ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION); + || (DrawableObject is not DrawableSpinner && ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime + && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION); + + public override bool IsSelectable => + // Bypass fade out extension from hit markers for selection purposes. + // This is to match stable, where even when the afterimage hit markers are still visible, objects are not selectable. + base.ShouldBeAlive; protected OsuSelectionBlueprint(T hitObject) : base(hitObject) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs index b1a7ef976a90..3884a3108fee 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs @@ -340,14 +340,14 @@ public void TestCyclicSelectionOutwards() public void TestCyclicSelectionBackwards() { var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 }; - var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 }; - var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 }; + var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 200 }; + var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 400 }; AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject })); moveMouseToObject(() => firstObject); - AddStep("seek to third", () => EditorClock.Seek(600)); + AddStep("seek to third", () => EditorClock.Seek(350)); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject)); diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 3c878ffd3366..158c3c102cea 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -32,7 +32,7 @@ public abstract partial class SelectionBlueprint : CompositeDrawable, IStatef /// public event Action> Deselected; - public override bool HandlePositionalInput => ShouldBeAlive; + public override bool HandlePositionalInput => IsSelectable; public override bool RemoveWhenNotAlive => false; protected SelectionBlueprint(T item) @@ -125,6 +125,11 @@ protected virtual void OnSelected() /// public virtual MenuItem[] ContextMenuItems => Array.Empty(); + /// + /// Whether the can be currently selected via a click or a drag box. + /// + public virtual bool IsSelectable => ShouldBeAlive && IsPresent; + /// /// The screen-space main point that causes this to be selected via a drag. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 2ee162bf3b28..1de6c8364ced 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -487,7 +487,7 @@ protected virtual void UpdateSelectionFromDragBox() break; case SelectionState.NotSelected: - if (blueprint.IsAlive && blueprint.IsPresent && quad.Contains(blueprint.ScreenSpaceSelectionPoint)) + if (blueprint.IsSelectable && quad.Contains(blueprint.ScreenSpaceSelectionPoint)) blueprint.Select(); break; }