mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 08:52:55 +08:00
Merge pull request #24291 from peppy/editor-cyclic-selection
Add support for cyclic selection in beatmap editor
This commit is contained in:
commit
166efd1aa8
@ -1,8 +1,6 @@
|
|||||||
// 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 System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -82,7 +80,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNudgeSelection()
|
public void TestNudgeSelection()
|
||||||
{
|
{
|
||||||
HitCircle[] addedObjects = null;
|
HitCircle[] addedObjects = null!;
|
||||||
|
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||||
{
|
{
|
||||||
@ -104,7 +102,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestRotateHotkeys()
|
public void TestRotateHotkeys()
|
||||||
{
|
{
|
||||||
HitCircle[] addedObjects = null;
|
HitCircle[] addedObjects = null!;
|
||||||
|
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||||
{
|
{
|
||||||
@ -136,7 +134,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestGlobalFlipHotkeys()
|
public void TestGlobalFlipHotkeys()
|
||||||
{
|
{
|
||||||
HitCircle addedObject = null;
|
HitCircle addedObject = null!;
|
||||||
|
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.Add(addedObject = new HitCircle { StartTime = 100 }));
|
AddStep("add hitobjects", () => EditorBeatmap.Add(addedObject = new HitCircle { StartTime = 100 }));
|
||||||
|
|
||||||
@ -286,11 +284,104 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCyclicSelection()
|
||||||
|
{
|
||||||
|
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 };
|
||||||
|
|
||||||
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||||
|
|
||||||
|
moveMouseToObject(() => firstObject);
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||||
|
|
||||||
|
// cycle around
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCyclicSelectionOutwards()
|
||||||
|
{
|
||||||
|
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 };
|
||||||
|
|
||||||
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||||
|
|
||||||
|
moveMouseToObject(() => firstObject);
|
||||||
|
|
||||||
|
AddStep("seek near second", () => EditorClock.Seek(320));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
|
|
||||||
|
// cycle around
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
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 };
|
||||||
|
|
||||||
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||||
|
|
||||||
|
moveMouseToObject(() => firstObject);
|
||||||
|
|
||||||
|
AddStep("seek to third", () => EditorClock.Seek(600));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
|
|
||||||
|
// cycle around
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDoubleClickToSeek()
|
||||||
|
{
|
||||||
|
var hitCircle = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||||
|
|
||||||
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { hitCircle }));
|
||||||
|
|
||||||
|
moveMouseToObject(() => hitCircle);
|
||||||
|
|
||||||
|
AddRepeatStep("double click", () => InputManager.Click(MouseButton.Left), 2);
|
||||||
|
|
||||||
|
AddUntilStep("seeked to circle", () => EditorClock.CurrentTime, () => Is.EqualTo(600));
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag)
|
public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag)
|
||||||
{
|
{
|
||||||
HitCircle[] addedObjects = null;
|
HitCircle[] addedObjects = null!;
|
||||||
|
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||||
{
|
{
|
||||||
@ -389,7 +480,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestQuickDeleteRemovesSliderControlPoint()
|
public void TestQuickDeleteRemovesSliderControlPoint()
|
||||||
{
|
{
|
||||||
Slider slider = null;
|
Slider slider = null!;
|
||||||
|
|
||||||
PathControlPoint[] points =
|
PathControlPoint[] points =
|
||||||
{
|
{
|
||||||
|
@ -25,8 +25,6 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private SkinEditor editor { get; set; } = null!;
|
private SkinEditor editor { get; set; } = null!;
|
||||||
|
|
||||||
protected override bool AllowCyclicSelection => true;
|
|
||||||
|
|
||||||
public SkinBlueprintContainer(ISerialisableDrawableContainer targetContainer)
|
public SkinBlueprintContainer(ISerialisableDrawableContainer targetContainer)
|
||||||
{
|
{
|
||||||
this.targetContainer = targetContainer;
|
this.targetContainer = targetContainer;
|
||||||
|
@ -46,15 +46,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
protected readonly BindableList<T> SelectedItems = new BindableList<T>();
|
protected readonly BindableList<T> SelectedItems = new BindableList<T>();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether to allow cyclic selection on clicking multiple times.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Disabled by default as it does not work well with editors that support double-clicking or other advanced interactions.
|
|
||||||
/// Can probably be made to work with more thought.
|
|
||||||
/// </remarks>
|
|
||||||
protected virtual bool AllowCyclicSelection => false;
|
|
||||||
|
|
||||||
protected BlueprintContainer()
|
protected BlueprintContainer()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
@ -167,6 +158,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (ClickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != ClickedBlueprint)
|
if (ClickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != ClickedBlueprint)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
doubleClickHandled = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +169,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
endClickSelection(e);
|
endClickSelection(e);
|
||||||
clickSelectionHandled = false;
|
clickSelectionHandled = false;
|
||||||
|
doubleClickHandled = false;
|
||||||
isDraggingBlueprint = false;
|
isDraggingBlueprint = false;
|
||||||
wasDragStarted = false;
|
wasDragStarted = false;
|
||||||
});
|
});
|
||||||
@ -376,6 +369,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool clickSelectionHandled;
|
private bool clickSelectionHandled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether a blueprint was double-clicked since last mouse down.
|
||||||
|
/// </summary>
|
||||||
|
private bool doubleClickHandled;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the selected blueprint(s) were already selected on mouse down. Generally used to perform selection cycling on mouse up in such a case.
|
/// Whether the selected blueprint(s) were already selected on mouse down. Generally used to perform selection cycling on mouse up in such a case.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -427,8 +425,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <returns>Whether a click selection was active.</returns>
|
/// <returns>Whether a click selection was active.</returns>
|
||||||
private bool endClickSelection(MouseButtonEvent e)
|
private bool endClickSelection(MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
// If already handled a selection or drag, we don't want to perform a mouse up / click action.
|
// If already handled a selection, double-click, or drag, we don't want to perform a mouse up / click action.
|
||||||
if (clickSelectionHandled || isDraggingBlueprint) return true;
|
if (clickSelectionHandled || doubleClickHandled || isDraggingBlueprint) return true;
|
||||||
|
|
||||||
if (e.Button != MouseButton.Left) return false;
|
if (e.Button != MouseButton.Left) return false;
|
||||||
|
|
||||||
@ -444,7 +442,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1 && AllowCyclicSelection)
|
if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1)
|
||||||
{
|
{
|
||||||
// If a click occurred and was handled by the currently selected blueprint but didn't result in a drag,
|
// If a click occurred and was handled by the currently selected blueprint but didn't result in a drag,
|
||||||
// cycle between other blueprints which are also under the cursor.
|
// cycle between other blueprints which are also under the cursor.
|
||||||
|
Loading…
Reference in New Issue
Block a user