1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-23 21:00:27 +08:00

Merge pull request #31917 from bdach/nudge-fix

Fix nudge operations incurring FP error from coordinate space conversions
This commit is contained in:
Dan Balasescu
2025-02-18 12:19:14 +09:00
committed by GitHub
Unverified
3 changed files with 124 additions and 66 deletions
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects;
@@ -12,6 +13,7 @@ using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
using osuTK.Input;
using Direction = osu.Framework.Graphics.Direction;
namespace osu.Game.Rulesets.Catch.Edit
@@ -38,6 +40,13 @@ namespace osu.Game.Rulesets.Catch.Edit
return true;
}
moveSelection(deltaX);
return true;
}
private void moveSelection(float deltaX)
{
EditorBeatmap.PerformOnSelection(h =>
{
if (!(h is CatchHitObject catchObject)) return;
@@ -48,7 +57,60 @@ namespace osu.Game.Rulesets.Catch.Edit
foreach (var nested in catchObject.NestedHitObjects.OfType<CatchHitObject>())
nested.OriginalX += deltaX;
});
}
private bool nudgeMovementActive;
protected override bool OnKeyDown(KeyDownEvent e)
{
// Until the keys below are global actions, this will prevent conflicts with "seek between sample points"
// which has a default of ctrl+shift+arrows.
if (e.ShiftPressed)
return false;
if (e.ControlPressed)
{
switch (e.Key)
{
case Key.Left:
return nudgeSelection(-1);
case Key.Right:
return nudgeSelection(1);
}
}
return false;
}
protected override void OnKeyUp(KeyUpEvent e)
{
base.OnKeyUp(e);
if (nudgeMovementActive && !e.ControlPressed)
{
EditorBeatmap.EndChange();
nudgeMovementActive = false;
}
}
/// <summary>
/// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints).
/// </summary>
private bool nudgeSelection(float deltaX)
{
if (!nudgeMovementActive)
{
nudgeMovementActive = true;
EditorBeatmap.BeginChange();
}
var firstBlueprint = SelectedBlueprints.FirstOrDefault();
if (firstBlueprint == null)
return false;
moveSelection(deltaX);
return true;
}
@@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.Edit
SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider);
}
private bool nudgeMovementActive;
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.Key == Key.M && e.ControlPressed && e.ShiftPressed)
@@ -48,9 +50,43 @@ namespace osu.Game.Rulesets.Osu.Edit
return true;
}
// Until the keys below are global actions, this will prevent conflicts with "seek between sample points"
// which has a default of ctrl+shift+arrows.
if (e.ShiftPressed)
return false;
if (e.ControlPressed)
{
switch (e.Key)
{
case Key.Left:
return nudgeSelection(new Vector2(-1, 0));
case Key.Right:
return nudgeSelection(new Vector2(1, 0));
case Key.Up:
return nudgeSelection(new Vector2(0, -1));
case Key.Down:
return nudgeSelection(new Vector2(0, 1));
}
}
return false;
}
protected override void OnKeyUp(KeyUpEvent e)
{
base.OnKeyUp(e);
if (nudgeMovementActive && !e.ControlPressed)
{
EditorBeatmap.EndChange();
nudgeMovementActive = false;
}
}
public override bool HandleMovement(MoveSelectionEvent<HitObject> moveEvent)
{
var hitObjects = selectedMovableObjects;
@@ -70,6 +106,13 @@ namespace osu.Game.Rulesets.Osu.Edit
if (hitObjects.Any(h => Precision.AlmostEquals(localDelta, -h.StackOffset)))
return true;
moveObjects(hitObjects, localDelta);
return true;
}
private void moveObjects(OsuHitObject[] hitObjects, Vector2 localDelta)
{
// this will potentially move the selection out of bounds...
foreach (var h in hitObjects)
h.Position += localDelta;
@@ -81,7 +124,26 @@ namespace osu.Game.Rulesets.Osu.Edit
// this intentionally bypasses the editor `UpdateState()` / beatmap processor flow for performance reasons,
// as the entire flow is too expensive to run on every movement.
Scheduler.AddOnce(OsuBeatmapProcessor.ApplyStacking, EditorBeatmap);
}
/// <summary>
/// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints).
/// </summary>
/// <param name="delta"></param>
private bool nudgeSelection(Vector2 delta)
{
if (!nudgeMovementActive)
{
nudgeMovementActive = true;
EditorBeatmap.BeginChange();
}
var firstBlueprint = SelectedBlueprints.FirstOrDefault();
if (firstBlueprint == null)
return false;
moveObjects(selectedMovableObjects, delta);
return true;
}
@@ -27,7 +27,6 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Components.TernaryButtons;
using osuTK;
using osuTK.Input;
namespace osu.Game.Screens.Edit.Compose.Components
{
@@ -112,71 +111,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
blueprint.DrawableObject = drawableObject;
}
private bool nudgeMovementActive;
protected override bool OnKeyDown(KeyDownEvent e)
{
// Until the keys below are global actions, this will prevent conflicts with "seek between sample points"
// which has a default of ctrl+shift+arrows.
if (e.ShiftPressed)
return false;
if (e.ControlPressed)
{
switch (e.Key)
{
case Key.Left:
return nudgeSelection(new Vector2(-1, 0));
case Key.Right:
return nudgeSelection(new Vector2(1, 0));
case Key.Up:
return nudgeSelection(new Vector2(0, -1));
case Key.Down:
return nudgeSelection(new Vector2(0, 1));
}
}
return false;
}
protected override void OnKeyUp(KeyUpEvent e)
{
base.OnKeyUp(e);
if (nudgeMovementActive && !e.ControlPressed)
{
Beatmap.EndChange();
nudgeMovementActive = false;
}
}
/// <summary>
/// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints).
/// </summary>
/// <param name="delta"></param>
private bool nudgeSelection(Vector2 delta)
{
if (!nudgeMovementActive)
{
nudgeMovementActive = true;
Beatmap.BeginChange();
}
var firstBlueprint = SelectionHandler.SelectedBlueprints.FirstOrDefault();
if (firstBlueprint == null)
return false;
// convert to game space coordinates
delta = firstBlueprint.ToScreenSpace(delta) - firstBlueprint.ToScreenSpace(Vector2.Zero);
SelectionHandler.HandleMovement(new MoveSelectionEvent<HitObject>(firstBlueprint, delta));
return true;
}
private void updatePlacementNewCombo()
{
if (CurrentHitObjectPlacement?.HitObject is IHasComboInformation c)