Add the ability to delete slider control points using shift+right click

Closes https://github.com/ppy/osu/issues/10672.

In two minds about how this should be implemented but went in this
direction initially. The other way would be to add local handling of
Shift-Right Click inside PathControlPointPiece (which is already doing
mouse handling itself).
This commit is contained in:
Dean Herbert 2020-11-03 20:45:48 +09:00
parent 2d1db6a22d
commit 9f333ac58a
5 changed files with 48 additions and 9 deletions

View File

@ -18,6 +18,7 @@
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osuTK;
using osuTK.Input; using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
@ -105,7 +106,7 @@ public bool OnPressed(PlatformAction action)
switch (action.ActionMethod) switch (action.ActionMethod)
{ {
case PlatformActionMethod.Delete: case PlatformActionMethod.Delete:
return deleteSelected(); return DeleteSelected();
} }
return false; return false;
@ -115,6 +116,9 @@ public void OnReleased(PlatformAction action)
{ {
} }
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
Pieces.Any(p => p.ReceivePositionalInputAt(screenSpacePos));
private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e) private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
{ {
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed) if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
@ -126,7 +130,7 @@ private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
} }
} }
private bool deleteSelected() public bool DeleteSelected()
{ {
List<PathControlPoint> toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.ControlPoint).ToList(); List<PathControlPoint> toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.ControlPoint).ToList();
@ -169,7 +173,7 @@ public MenuItem[] ContextMenuItems
return new MenuItem[] return new MenuItem[]
{ {
new OsuMenuItem($"Delete {"control point".ToQuantity(count, count > 1 ? ShowQuantityAs.Numeric : ShowQuantityAs.None)}", MenuItemType.Destructive, () => deleteSelected()), new OsuMenuItem($"Delete {"control point".ToQuantity(count, count > 1 ? ShowQuantityAs.Numeric : ShowQuantityAs.None)}", MenuItemType.Destructive, () => DeleteSelected()),
new OsuMenuItem("Curve type") new OsuMenuItem("Curve type")
{ {
Items = items Items = items

View File

@ -3,6 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -72,6 +73,18 @@ protected override void LoadComplete()
BodyPiece.UpdateFrom(HitObject); BodyPiece.UpdateFrom(HitObject);
} }
public override bool HandleQuickDeletion()
{
var hoveredControlPoint = ControlPointVisualiser.Pieces.FirstOrDefault(p => p.IsHovered);
if (hoveredControlPoint == null)
return false;
hoveredControlPoint.IsSelected.Value = true;
ControlPointVisualiser.DeleteSelected();
return true;
}
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
@ -216,7 +229,8 @@ private void updatePath()
public override Vector2 ScreenSpaceSelectionPoint => BodyPiece.ToScreenSpace(BodyPiece.PathStartLocation); public override Vector2 ScreenSpaceSelectionPoint => BodyPiece.ToScreenSpace(BodyPiece.PathStartLocation);
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
BodyPiece.ReceivePositionalInputAt(screenSpacePos) || ControlPointVisualiser?.ReceivePositionalInputAt(screenSpacePos) == true;
protected virtual SliderCircleSelectionBlueprint CreateCircleSelectionBlueprint(DrawableSlider slider, SliderPosition position) => new SliderCircleSelectionBlueprint(slider, position); protected virtual SliderCircleSelectionBlueprint CreateCircleSelectionBlueprint(DrawableSlider slider, SliderPosition position) => new SliderCircleSelectionBlueprint(slider, position);
} }

View File

@ -143,5 +143,11 @@ protected virtual void OnSelected()
public virtual Quad SelectionQuad => ScreenSpaceDrawQuad; public virtual Quad SelectionQuad => ScreenSpaceDrawQuad;
public virtual Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Parent.ToLocalSpace(screenSpacePosition) - Position; public virtual Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Parent.ToLocalSpace(screenSpacePosition) - Position;
/// <summary>
/// Handle to perform a partial deletion when the user requests a quick delete (Shift+Right Click).
/// </summary>
/// <returns>True if the deletion operation was handled by this blueprint. Returning false will delete the full blueprint.</returns>
public virtual bool HandleQuickDeletion() => false;
} }
} }

View File

@ -116,7 +116,8 @@ protected virtual Container<SelectionBlueprint> CreateSelectionBlueprintContaine
protected override bool OnMouseDown(MouseDownEvent e) protected override bool OnMouseDown(MouseDownEvent e)
{ {
beginClickSelection(e); if (beginClickSelection(e)) return true;
prepareSelectionMovement(); prepareSelectionMovement();
return e.Button == MouseButton.Left; return e.Button == MouseButton.Left;
@ -291,19 +292,23 @@ protected virtual void AddBlueprintFor(HitObject hitObject)
/// Attempts to select any hovered blueprints. /// Attempts to select any hovered blueprints.
/// </summary> /// </summary>
/// <param name="e">The input event that triggered this selection.</param> /// <param name="e">The input event that triggered this selection.</param>
private void beginClickSelection(MouseButtonEvent e) private bool beginClickSelection(MouseButtonEvent e)
{ {
Debug.Assert(!clickSelectionBegan); Debug.Assert(!clickSelectionBegan);
bool rightClickHandled = false;
foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren) foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren)
{ {
if (blueprint.IsHovered) if (blueprint.IsHovered)
{ {
SelectionHandler.HandleSelectionRequested(blueprint, e.CurrentState); rightClickHandled |= SelectionHandler.HandleSelectionRequested(blueprint, e.CurrentState);
clickSelectionBegan = true; clickSelectionBegan = true;
break; break;
} }
} }
return rightClickHandled;
} }
/// <summary> /// <summary>

View File

@ -219,18 +219,28 @@ internal void HandleDeselected(SelectionBlueprint blueprint)
/// </summary> /// </summary>
/// <param name="blueprint">The blueprint.</param> /// <param name="blueprint">The blueprint.</param>
/// <param name="state">The input state at the point of selection.</param> /// <param name="state">The input state at the point of selection.</param>
internal void HandleSelectionRequested(SelectionBlueprint blueprint, InputState state) /// <returns>Whether right click was handled.</returns>
internal bool HandleSelectionRequested(SelectionBlueprint blueprint, InputState state)
{ {
if (state.Keyboard.ShiftPressed && state.Mouse.IsPressed(MouseButton.Right)) if (state.Keyboard.ShiftPressed && state.Mouse.IsPressed(MouseButton.Right))
{
handleQuickDeletion(blueprint); handleQuickDeletion(blueprint);
else if (state.Keyboard.ControlPressed && state.Mouse.IsPressed(MouseButton.Left)) return true;
}
if (state.Keyboard.ControlPressed && state.Mouse.IsPressed(MouseButton.Left))
blueprint.ToggleSelection(); blueprint.ToggleSelection();
else else
ensureSelected(blueprint); ensureSelected(blueprint);
return false;
} }
private void handleQuickDeletion(SelectionBlueprint blueprint) private void handleQuickDeletion(SelectionBlueprint blueprint)
{ {
if (blueprint.HandleQuickDeletion())
return;
if (!blueprint.IsSelected) if (!blueprint.IsSelected)
EditorBeatmap.Remove(blueprint.HitObject); EditorBeatmap.Remove(blueprint.HitObject);
else else