mirror of
https://github.com/ppy/osu.git
synced 2024-11-07 16:47:24 +08:00
Add optional support for cyclic selection to BlueprintContainer
This commit is contained in:
parent
ff59a236ae
commit
21bdbb20e6
@ -25,6 +25,8 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
[Resolved]
|
||||
private SkinEditor editor { get; set; } = null!;
|
||||
|
||||
protected override bool AllowCyclicSelection => true;
|
||||
|
||||
public SkinBlueprintContainer(ISerialisableDrawableContainer targetContainer)
|
||||
{
|
||||
this.targetContainer = targetContainer;
|
||||
|
@ -45,6 +45,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
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()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
@ -167,7 +176,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
Schedule(() =>
|
||||
{
|
||||
endClickSelection(e);
|
||||
clickSelectionBegan = false;
|
||||
clickSelectionHandled = false;
|
||||
isDraggingBlueprint = false;
|
||||
});
|
||||
|
||||
@ -339,7 +348,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <summary>
|
||||
/// Whether a blueprint was selected by a previous click event.
|
||||
/// </summary>
|
||||
private bool clickSelectionBegan;
|
||||
private bool clickSelectionHandled;
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
private bool selectedBlueprintAlreadySelectedOnMouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to select any hovered blueprints.
|
||||
@ -354,7 +368,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
if (!blueprint.IsHovered) continue;
|
||||
|
||||
return clickSelectionBegan = SelectionHandler.MouseDownSelectionRequested(blueprint, e);
|
||||
selectedBlueprintAlreadySelectedOnMouseDown = blueprint.State == SelectionState.Selected;
|
||||
return clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, e);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -367,18 +382,45 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <returns>Whether a click selection was active.</returns>
|
||||
private bool endClickSelection(MouseButtonEvent e)
|
||||
{
|
||||
if (!clickSelectionBegan && !isDraggingBlueprint)
|
||||
if (!clickSelectionHandled && !isDraggingBlueprint)
|
||||
{
|
||||
// if a selection didn't occur, we may want to trigger a deselection.
|
||||
if (e.ControlPressed && e.Button == MouseButton.Left)
|
||||
if (e.Button == MouseButton.Left)
|
||||
{
|
||||
// Iterate from the top of the input stack (blueprints closest to the front of the screen first).
|
||||
// Priority is given to already-selected blueprints.
|
||||
foreach (SelectionBlueprint<T> blueprint in SelectionBlueprints.AliveChildren.Reverse().OrderByDescending(b => b.IsSelected))
|
||||
if (e.ControlPressed)
|
||||
{
|
||||
if (!blueprint.IsHovered) continue;
|
||||
// if a selection didn't occur, we may want to trigger a deselection.
|
||||
|
||||
return clickSelectionBegan = SelectionHandler.MouseUpSelectionRequested(blueprint, e);
|
||||
// Iterate from the top of the input stack (blueprints closest to the front of the screen first).
|
||||
// Priority is given to already-selected blueprints.
|
||||
foreach (SelectionBlueprint<T> blueprint in SelectionBlueprints.AliveChildren.OrderByDescending(b => b.IsSelected))
|
||||
{
|
||||
if (!blueprint.IsHovered) continue;
|
||||
|
||||
return clickSelectionHandled = SelectionHandler.MouseUpSelectionRequested(blueprint, e);
|
||||
}
|
||||
}
|
||||
else if (selectedBlueprintAlreadySelectedOnMouseDown && AllowCyclicSelection)
|
||||
{
|
||||
// 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.
|
||||
|
||||
// The depth of blueprints is constantly changing (see above where selected blueprints are brought to the front).
|
||||
// For this logic, we want a stable sort order so we can correctly cycle, thus using the blueprintMap instead.
|
||||
IEnumerable<SelectionBlueprint<T>> cyclingSelectionBlueprints = blueprintMap.Values;
|
||||
|
||||
// If there's already a selection, let's start from the blueprint after the selection.
|
||||
cyclingSelectionBlueprints = cyclingSelectionBlueprints.SkipWhile(b => !b.IsSelected).Skip(1);
|
||||
|
||||
// Add the blueprints from before the selection to the end of the enumerable to allow for cyclic selection.
|
||||
cyclingSelectionBlueprints = cyclingSelectionBlueprints.Concat(blueprintMap.Values.TakeWhile(b => !b.IsSelected));
|
||||
|
||||
foreach (SelectionBlueprint<T> blueprint in cyclingSelectionBlueprints)
|
||||
{
|
||||
if (!blueprint.IsHovered) continue;
|
||||
|
||||
// We are performing a mouse up, but selection handlers perform selection on mouse down, so we need to call that instead.
|
||||
return clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,7 +483,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
private Vector2[][] movementBlueprintsOriginalPositions;
|
||||
private SelectionBlueprint<T>[] movementBlueprints;
|
||||
private bool isDraggingBlueprint;
|
||||
|
||||
/// <summary>
|
||||
/// Whether a blueprint is currently being dragged.
|
||||
/// </summary>
|
||||
private bool isDraggingBlueprint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to begin the movement of any selected blueprints.
|
||||
@ -454,7 +500,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
// Any selected blueprint that is hovered can begin the movement of the group, however only the first item (according to SortForMovement) is used for movement.
|
||||
// A special case is added for when a click selection occurred before the drag
|
||||
if (!clickSelectionBegan && !SelectionHandler.SelectedBlueprints.Any(b => b.IsHovered))
|
||||
if (!clickSelectionHandled && !SelectionHandler.SelectedBlueprints.Any(b => b.IsHovered))
|
||||
return false;
|
||||
|
||||
// Movement is tracked from the blueprint of the earliest item, since it only makes sense to distance snap from that item
|
||||
|
Loading…
Reference in New Issue
Block a user