mirror of
https://github.com/ppy/osu.git
synced 2025-02-09 18:53:20 +08:00
Merge pull request #31655 from bdach/deabstractify-editor-snap
Refactor editor object snapping to remove bad abstractions
This commit is contained in:
commit
48b1c7398e
@ -12,7 +12,6 @@ using osu.Framework.Testing;
|
|||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -71,11 +70,11 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
contentContainer.Playfield.HitObjectContainer.Add(hitObject);
|
contentContainer.Playfield.HitObjectContainer.Add(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override SnapResult SnapForBlueprint(HitObjectPlacementBlueprint blueprint)
|
protected override void UpdatePlacementTimeAndPosition()
|
||||||
{
|
{
|
||||||
var result = base.SnapForBlueprint(blueprint);
|
var position = InputManager.CurrentState.Mouse.Position;
|
||||||
result.Time = Math.Round(HitObjectContainer.TimeAtScreenSpacePosition(result.ScreenSpacePosition) / TIME_SNAP) * TIME_SNAP;
|
double time = Math.Round(HitObjectContainer.TimeAtScreenSpacePosition(position) / TIME_SNAP) * TIME_SNAP;
|
||||||
return result;
|
CurrentBlueprint.UpdateTimeAndPosition(position, time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Utils;
|
|||||||
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||||
@ -59,11 +60,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
return base.OnMouseDown(e);
|
return base.OnMouseDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
{
|
{
|
||||||
base.UpdateTimeAndPosition(result);
|
var result = Composer?.FindSnappedPositionAndTime(screenSpacePosition) ?? new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
|
|
||||||
if (!(result.Time is double time)) return;
|
base.UpdateTimeAndPosition(result.ScreenSpacePosition, result.Time ?? fallbackTime);
|
||||||
|
|
||||||
|
if (!(result.Time is double time)) return result;
|
||||||
|
|
||||||
switch (PlacementActive)
|
switch (PlacementActive)
|
||||||
{
|
{
|
||||||
@ -78,6 +81,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
|
|
||||||
HitObject.StartTime = Math.Min(placementStartTime, placementEndTime);
|
HitObject.StartTime = Math.Min(placementStartTime, placementEndTime);
|
||||||
HitObject.EndTime = Math.Max(placementStartTime, placementEndTime);
|
HitObject.EndTime = Math.Max(placementStartTime, placementEndTime);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ using osu.Game.Rulesets.UI.Scrolling;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||||
{
|
{
|
||||||
public partial class CatchPlacementBlueprint<THitObject> : HitObjectPlacementBlueprint
|
public abstract partial class CatchPlacementBlueprint<THitObject> : HitObjectPlacementBlueprint
|
||||||
where THitObject : CatchHitObject, new()
|
where THitObject : CatchHitObject, new()
|
||||||
{
|
{
|
||||||
protected new THitObject HitObject => (THitObject)base.HitObject;
|
protected new THitObject HitObject => (THitObject)base.HitObject;
|
||||||
@ -19,7 +19,10 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private Playfield playfield { get; set; } = null!;
|
private Playfield playfield { get; set; } = null!;
|
||||||
|
|
||||||
public CatchPlacementBlueprint()
|
[Resolved]
|
||||||
|
protected CatchHitObjectComposer? Composer { get; private set; }
|
||||||
|
|
||||||
|
protected CatchPlacementBlueprint()
|
||||||
: base(new THitObject())
|
: base(new THitObject())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||||
@ -41,11 +42,20 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
{
|
{
|
||||||
base.UpdateTimeAndPosition(result);
|
var gridSnapResult = Composer?.FindSnappedPositionAndTime(screenSpacePosition) ?? new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
|
gridSnapResult.ScreenSpacePosition.X = screenSpacePosition.X;
|
||||||
|
var distanceSnapResult = Composer?.TryDistanceSnap(gridSnapResult.ScreenSpacePosition);
|
||||||
|
|
||||||
|
var result = distanceSnapResult != null && Vector2.Distance(gridSnapResult.ScreenSpacePosition, distanceSnapResult.ScreenSpacePosition) < CatchHitObjectComposer.DISTANCE_SNAP_RADIUS
|
||||||
|
? distanceSnapResult
|
||||||
|
: gridSnapResult;
|
||||||
|
|
||||||
|
base.UpdateTimeAndPosition(result.ScreenSpacePosition, result.Time ?? fallbackTime);
|
||||||
|
|
||||||
HitObject.X = ToLocalSpace(result.ScreenSpacePosition).X;
|
HitObject.X = ToLocalSpace(result.ScreenSpacePosition).X;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,8 +83,16 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
return base.OnMouseDown(e);
|
return base.OnMouseDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
{
|
{
|
||||||
|
var gridSnapResult = Composer?.FindSnappedPositionAndTime(screenSpacePosition) ?? new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
|
gridSnapResult.ScreenSpacePosition.X = screenSpacePosition.X;
|
||||||
|
var distanceSnapResult = Composer?.TryDistanceSnap(gridSnapResult.ScreenSpacePosition);
|
||||||
|
|
||||||
|
var result = distanceSnapResult != null && Vector2.Distance(gridSnapResult.ScreenSpacePosition, distanceSnapResult.ScreenSpacePosition) < CatchHitObjectComposer.DISTANCE_SNAP_RADIUS
|
||||||
|
? distanceSnapResult
|
||||||
|
: gridSnapResult;
|
||||||
|
|
||||||
switch (PlacementActive)
|
switch (PlacementActive)
|
||||||
{
|
{
|
||||||
case PlacementState.Waiting:
|
case PlacementState.Waiting:
|
||||||
@ -99,7 +107,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the up-to-date position is used for outlines.
|
// Make sure the up-to-date position is used for outlines.
|
||||||
@ -113,6 +121,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
ApplyDefaultsToHitObject();
|
ApplyDefaultsToHitObject();
|
||||||
scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject);
|
scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject);
|
||||||
nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject);
|
nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double positionToTime(float relativeYPosition)
|
private double positionToTime(float relativeYPosition)
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Catch.Edit.Blueprints;
|
using osu.Game.Rulesets.Catch.Edit.Blueprints;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Edit
|
namespace osu.Game.Rulesets.Catch.Edit
|
||||||
{
|
{
|
||||||
public partial class CatchBlueprintContainer : ComposeBlueprintContainer
|
public partial class CatchBlueprintContainer : ComposeBlueprintContainer
|
||||||
{
|
{
|
||||||
|
public new CatchHitObjectComposer Composer => (CatchHitObjectComposer)base.Composer;
|
||||||
|
|
||||||
public CatchBlueprintContainer(CatchHitObjectComposer composer)
|
public CatchBlueprintContainer(CatchHitObjectComposer composer)
|
||||||
: base(composer)
|
: base(composer)
|
||||||
{
|
{
|
||||||
@ -36,5 +42,28 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override DragBox CreateDragBox() => new ScrollingDragBox(Composer.Playfield);
|
protected sealed override DragBox CreateDragBox() => new ScrollingDragBox(Composer.Playfield);
|
||||||
|
|
||||||
|
protected override bool TryMoveBlueprints(DragEvent e, IList<(SelectionBlueprint<HitObject> blueprint, Vector2[] originalSnapPositions)> blueprints)
|
||||||
|
{
|
||||||
|
Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
||||||
|
|
||||||
|
// The final movement position, relative to movementBlueprintOriginalPosition.
|
||||||
|
Vector2 movePosition = blueprints.First().originalSnapPositions.First() + distanceTravelled;
|
||||||
|
|
||||||
|
// Retrieve a snapped position.
|
||||||
|
var gridSnapResult = Composer.FindSnappedPositionAndTime(movePosition);
|
||||||
|
gridSnapResult.ScreenSpacePosition.X = movePosition.X;
|
||||||
|
var distanceSnapResult = Composer.TryDistanceSnap(gridSnapResult.ScreenSpacePosition);
|
||||||
|
|
||||||
|
var result = distanceSnapResult != null && Vector2.Distance(gridSnapResult.ScreenSpacePosition, distanceSnapResult.ScreenSpacePosition) < CatchHitObjectComposer.DISTANCE_SNAP_RADIUS
|
||||||
|
? distanceSnapResult
|
||||||
|
: gridSnapResult;
|
||||||
|
|
||||||
|
var referenceBlueprint = blueprints.First().blueprint;
|
||||||
|
bool moved = SelectionHandler.HandleMovement(new MoveSelectionEvent<HitObject>(referenceBlueprint, result.ScreenSpacePosition - referenceBlueprint.ScreenSpaceSelectionPoint));
|
||||||
|
if (moved)
|
||||||
|
ApplySnapResultTime(result, referenceBlueprint.Item.StartTime);
|
||||||
|
return moved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,10 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Edit
|
namespace osu.Game.Rulesets.Catch.Edit
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public partial class CatchHitObjectComposer : ScrollingHitObjectComposer<CatchHitObject>, IKeyBindingHandler<GlobalAction>
|
public partial class CatchHitObjectComposer : ScrollingHitObjectComposer<CatchHitObject>, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
private const float distance_snap_radius = 50;
|
public const float DISTANCE_SNAP_RADIUS = 50;
|
||||||
|
|
||||||
private CatchDistanceSnapGrid distanceSnapGrid = null!;
|
private CatchDistanceSnapGrid distanceSnapGrid = null!;
|
||||||
|
|
||||||
@ -135,22 +136,12 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
DistanceSnapProvider.HandleToggleViaKey(key);
|
DistanceSnapProvider.HandleToggleViaKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All)
|
public SnapResult? TryDistanceSnap(Vector2 screenSpacePosition)
|
||||||
{
|
{
|
||||||
var result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType);
|
if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(screenSpacePosition) is SnapResult snapResult)
|
||||||
|
return snapResult;
|
||||||
|
|
||||||
result.ScreenSpacePosition.X = screenSpacePosition.X;
|
return null;
|
||||||
|
|
||||||
if (snapType.HasFlag(SnapType.RelativeGrids))
|
|
||||||
{
|
|
||||||
if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult &&
|
|
||||||
Vector2.Distance(snapResult.ScreenSpacePosition, result.ScreenSpacePosition) < distance_snap_radius)
|
|
||||||
{
|
|
||||||
result = snapResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PalpableCatchHitObject? getLastSnappableHitObject(double time)
|
private PalpableCatchHitObject? getLastSnappableHitObject(double time)
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
@ -47,12 +46,11 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override SnapResult SnapForBlueprint(HitObjectPlacementBlueprint blueprint)
|
protected override void UpdatePlacementTimeAndPosition()
|
||||||
{
|
{
|
||||||
double time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position);
|
double time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position);
|
||||||
var pos = column.ScreenSpacePositionAtTime(time);
|
var pos = column.ScreenSpacePositionAtTime(time);
|
||||||
|
CurrentBlueprint.UpdateTimeAndPosition(pos, time);
|
||||||
return new SnapResult(pos, time, column);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };
|
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };
|
||||||
|
@ -20,7 +20,6 @@ using osu.Game.Rulesets.UI.Scrolling;
|
|||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
@ -100,10 +99,5 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
{
|
{
|
||||||
set => InternalChild = value;
|
set => InternalChild = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
|
|
||||||
private double originalStartTime;
|
private double originalStartTime;
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
{
|
{
|
||||||
base.UpdateTimeAndPosition(result);
|
var result = base.UpdateTimeAndPosition(screenSpacePosition, fallbackTime);
|
||||||
|
|
||||||
if (PlacementActive == PlacementState.Active)
|
if (PlacementActive == PlacementState.Active)
|
||||||
{
|
{
|
||||||
@ -121,6 +121,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
if (result.Time is double startTime)
|
if (result.Time is double startTime)
|
||||||
originalStartTime = HitObject.StartTime = startTime;
|
originalStartTime = HitObject.StartTime = startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
private EditorBeatmap? editorBeatmap { get; set; }
|
private EditorBeatmap? editorBeatmap { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IPositionSnapProvider? positionSnapProvider { get; set; }
|
private ManiaHitObjectComposer? positionSnapProvider { get; set; }
|
||||||
|
|
||||||
private EditBodyPiece body = null!;
|
private EditBodyPiece body = null!;
|
||||||
private EditHoldNoteEndPiece head = null!;
|
private EditHoldNoteEndPiece head = null!;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
@ -20,13 +20,18 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
{
|
{
|
||||||
protected new T HitObject => (T)base.HitObject;
|
protected new T HitObject => (T)base.HitObject;
|
||||||
|
|
||||||
private Column column;
|
[Resolved]
|
||||||
|
private ManiaHitObjectComposer? composer { get; set; }
|
||||||
|
|
||||||
public Column Column
|
private Column? column;
|
||||||
|
|
||||||
|
public Column? Column
|
||||||
{
|
{
|
||||||
get => column;
|
get => column;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(value);
|
||||||
|
|
||||||
if (value == column)
|
if (value == column)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -53,9 +58,11 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
{
|
{
|
||||||
base.UpdateTimeAndPosition(result);
|
var result = composer?.FindSnappedPositionAndTime(screenSpacePosition) ?? new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
|
|
||||||
|
base.UpdateTimeAndPosition(result.ScreenSpacePosition, result.Time ?? fallbackTime);
|
||||||
|
|
||||||
if (result.Playfield is Column col)
|
if (result.Playfield is Column col)
|
||||||
{
|
{
|
||||||
@ -76,6 +83,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
if (PlacementActive == PlacementState.Waiting)
|
if (PlacementActive == PlacementState.Waiting)
|
||||||
Column = col;
|
Column = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float getNoteHeight(Column resultPlayfield) =>
|
private float getNoteHeight(Column resultPlayfield) =>
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||||
@ -35,15 +36,17 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double referenceTime)
|
||||||
{
|
{
|
||||||
base.UpdateTimeAndPosition(result);
|
var result = base.UpdateTimeAndPosition(screenSpacePosition, referenceTime);
|
||||||
|
|
||||||
if (result.Playfield != null)
|
if (result.Playfield != null)
|
||||||
{
|
{
|
||||||
piece.Width = result.Playfield.DrawWidth;
|
piece.Width = result.Playfield.DrawWidth;
|
||||||
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
@ -1,17 +1,23 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit
|
namespace osu.Game.Rulesets.Mania.Edit
|
||||||
{
|
{
|
||||||
public partial class ManiaBlueprintContainer : ComposeBlueprintContainer
|
public partial class ManiaBlueprintContainer : ComposeBlueprintContainer
|
||||||
{
|
{
|
||||||
public ManiaBlueprintContainer(HitObjectComposer composer)
|
public new ManiaHitObjectComposer Composer => (ManiaHitObjectComposer)base.Composer;
|
||||||
|
|
||||||
|
public ManiaBlueprintContainer(ManiaHitObjectComposer composer)
|
||||||
: base(composer)
|
: base(composer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -33,5 +39,22 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
protected override SelectionHandler<HitObject> CreateSelectionHandler() => new ManiaSelectionHandler();
|
protected override SelectionHandler<HitObject> CreateSelectionHandler() => new ManiaSelectionHandler();
|
||||||
|
|
||||||
protected sealed override DragBox CreateDragBox() => new ScrollingDragBox(Composer.Playfield);
|
protected sealed override DragBox CreateDragBox() => new ScrollingDragBox(Composer.Playfield);
|
||||||
|
|
||||||
|
protected override bool TryMoveBlueprints(DragEvent e, IList<(SelectionBlueprint<HitObject> blueprint, Vector2[] originalSnapPositions)> blueprints)
|
||||||
|
{
|
||||||
|
Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
||||||
|
|
||||||
|
// The final movement position, relative to movementBlueprintOriginalPosition.
|
||||||
|
Vector2 movePosition = blueprints.First().originalSnapPositions.First() + distanceTravelled;
|
||||||
|
|
||||||
|
// Retrieve a snapped position.
|
||||||
|
var result = Composer.FindSnappedPositionAndTime(movePosition);
|
||||||
|
|
||||||
|
var referenceBlueprint = blueprints.First().blueprint;
|
||||||
|
bool moved = SelectionHandler.HandleMovement(new MoveSelectionEvent<HitObject>(referenceBlueprint, result.ScreenSpacePosition - referenceBlueprint.ScreenSpaceSelectionPoint));
|
||||||
|
if (moved)
|
||||||
|
ApplySnapResultTime(result, referenceBlueprint.Item.StartTime);
|
||||||
|
return moved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit
|
namespace osu.Game.Rulesets.Mania.Edit
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public partial class ManiaHitObjectComposer : ScrollingHitObjectComposer<ManiaHitObject>
|
public partial class ManiaHitObjectComposer : ScrollingHitObjectComposer<ManiaHitObject>
|
||||||
{
|
{
|
||||||
private DrawableManiaEditorRuleset drawableRuleset = null!;
|
private DrawableManiaEditorRuleset drawableRuleset = null!;
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
|||||||
public partial class GridPlacementBlueprint : PlacementBlueprint
|
public partial class GridPlacementBlueprint : PlacementBlueprint
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private HitObjectComposer? hitObjectComposer { get; set; }
|
private OsuHitObjectComposer? hitObjectComposer { get; set; }
|
||||||
|
|
||||||
private OsuGridToolboxGroup gridToolboxGroup = null!;
|
private OsuGridToolboxGroup gridToolboxGroup = null!;
|
||||||
private Vector2 originalOrigin;
|
private Vector2 originalOrigin;
|
||||||
@ -95,12 +95,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
|||||||
base.OnDragEnd(e);
|
base.OnDragEnd(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SnapType SnapType => ~SnapType.GlobalGrids;
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
|
||||||
{
|
{
|
||||||
if (State.Value == Visibility.Hidden)
|
if (State.Value == Visibility.Hidden)
|
||||||
return;
|
return new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
|
|
||||||
|
var result = hitObjectComposer?.TrySnapToNearbyObjects(screenSpacePosition) ?? new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
|
|
||||||
var pos = ToLocalSpace(result.ScreenSpacePosition);
|
var pos = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
|
|
||||||
@ -120,6 +120,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
|||||||
gridToolboxGroup.SetGridFromPoints(gridToolboxGroup.StartPosition.Value, pos);
|
gridToolboxGroup.SetGridFromPoints(gridToolboxGroup.StartPosition.Value, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||||
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.HitCircles
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
||||||
@ -15,6 +17,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
|||||||
|
|
||||||
private readonly HitCirclePiece circlePiece;
|
private readonly HitCirclePiece circlePiece;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuHitObjectComposer? composer { get; set; }
|
||||||
|
|
||||||
public HitCirclePlacementBlueprint()
|
public HitCirclePlacementBlueprint()
|
||||||
: base(new HitCircle())
|
: base(new HitCircle())
|
||||||
{
|
{
|
||||||
@ -45,10 +50,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
|||||||
return base.OnMouseDown(e);
|
return base.OnMouseDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
{
|
{
|
||||||
base.UpdateTimeAndPosition(result);
|
var result = composer?.TrySnapToNearbyObjects(screenSpacePosition, fallbackTime);
|
||||||
|
result ??= composer?.TrySnapToDistanceGrid(screenSpacePosition);
|
||||||
|
if (composer?.TrySnapToPositionGrid(result?.ScreenSpacePosition ?? screenSpacePosition, result?.Time ?? fallbackTime) is SnapResult gridSnapResult)
|
||||||
|
result = gridSnapResult;
|
||||||
|
result ??= new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
|
|
||||||
|
base.UpdateTimeAndPosition(result.ScreenSpacePosition, result.Time ?? fallbackTime);
|
||||||
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using System.Collections.Specialized;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -48,7 +49,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
public Action<List<PathControlPoint>> SplitControlPointsRequested;
|
public Action<List<PathControlPoint>> SplitControlPointsRequested;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IPositionSnapProvider positionSnapProvider { get; set; }
|
[CanBeNull]
|
||||||
|
private OsuHitObjectComposer positionSnapProvider { get; set; }
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IDistanceSnapProvider distanceSnapProvider { get; set; }
|
private IDistanceSnapProvider distanceSnapProvider { get; set; }
|
||||||
@ -433,12 +435,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
// Special handling for selections containing head control point - the position of the hit object changes which means the snapped position and time have to be taken into account
|
// Special handling for selections containing head control point - the position of the hit object changes which means the snapped position and time have to be taken into account
|
||||||
Vector2 newHeadPosition = Parent!.ToScreenSpace(e.MousePosition + (dragStartPositions[0] - dragStartPositions[draggedControlPointIndex]));
|
Vector2 newHeadPosition = Parent!.ToScreenSpace(e.MousePosition + (dragStartPositions[0] - dragStartPositions[draggedControlPointIndex]));
|
||||||
SnapResult result = positionSnapProvider?.FindSnappedPositionAndTime(newHeadPosition);
|
|
||||||
|
|
||||||
Vector2 movementDelta = Parent!.ToLocalSpace(result?.ScreenSpacePosition ?? newHeadPosition) - hitObject.Position;
|
var result = positionSnapProvider?.TrySnapToNearbyObjects(newHeadPosition, oldStartTime);
|
||||||
|
result ??= positionSnapProvider?.TrySnapToDistanceGrid(newHeadPosition);
|
||||||
|
if (positionSnapProvider?.TrySnapToPositionGrid(result?.ScreenSpacePosition ?? newHeadPosition, result?.Time ?? oldStartTime) is SnapResult gridSnapResult)
|
||||||
|
result = gridSnapResult;
|
||||||
|
result ??= new SnapResult(newHeadPosition, oldStartTime);
|
||||||
|
|
||||||
|
Vector2 movementDelta = Parent!.ToLocalSpace(result.ScreenSpacePosition) - hitObject.Position;
|
||||||
|
|
||||||
hitObject.Position += movementDelta;
|
hitObject.Position += movementDelta;
|
||||||
hitObject.StartTime = result?.Time ?? hitObject.StartTime;
|
hitObject.StartTime = result.Time ?? hitObject.StartTime;
|
||||||
|
|
||||||
for (int i = 1; i < hitObject.Path.ControlPoints.Count; i++)
|
for (int i = 1; i < hitObject.Path.ControlPoints.Count; i++)
|
||||||
{
|
{
|
||||||
@ -453,7 +460,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SnapResult result = positionSnapProvider?.FindSnappedPositionAndTime(Parent!.ToScreenSpace(e.MousePosition), SnapType.GlobalGrids);
|
SnapResult result = positionSnapProvider?.TrySnapToPositionGrid(Parent!.ToScreenSpace(e.MousePosition));
|
||||||
|
|
||||||
Vector2 movementDelta = Parent!.ToLocalSpace(result?.ScreenSpacePosition ?? Parent!.ToScreenSpace(e.MousePosition)) - dragStartPositions[draggedControlPointIndex] - hitObject.Position;
|
Vector2 movementDelta = Parent!.ToLocalSpace(result?.ScreenSpacePosition ?? Parent!.ToScreenSpace(e.MousePosition)) - dragStartPositions[draggedControlPointIndex] - hitObject.Position;
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
{
|
{
|
||||||
public new Slider HitObject => (Slider)base.HitObject;
|
public new Slider HitObject => (Slider)base.HitObject;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuHitObjectComposer? composer { get; set; }
|
||||||
|
|
||||||
private SliderBodyPiece bodyPiece = null!;
|
private SliderBodyPiece bodyPiece = null!;
|
||||||
private HitCirclePiece headCirclePiece = null!;
|
private HitCirclePiece headCirclePiece = null!;
|
||||||
private HitCirclePiece tailCirclePiece = null!;
|
private HitCirclePiece tailCirclePiece = null!;
|
||||||
@ -40,9 +43,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
private int currentSegmentLength;
|
private int currentSegmentLength;
|
||||||
private bool usingCustomSegmentType;
|
private bool usingCustomSegmentType;
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IPositionSnapProvider? positionSnapProvider { get; set; }
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IDistanceSnapProvider? distanceSnapProvider { get; set; }
|
private IDistanceSnapProvider? distanceSnapProvider { get; set; }
|
||||||
|
|
||||||
@ -106,9 +106,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorBeatmap editorBeatmap { get; set; } = null!;
|
private EditorBeatmap editorBeatmap { get; set; } = null!;
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
{
|
{
|
||||||
base.UpdateTimeAndPosition(result);
|
var result = composer?.TrySnapToNearbyObjects(screenSpacePosition, fallbackTime);
|
||||||
|
result ??= composer?.TrySnapToDistanceGrid(screenSpacePosition);
|
||||||
|
if (composer?.TrySnapToPositionGrid(result?.ScreenSpacePosition ?? screenSpacePosition, result?.Time ?? fallbackTime) is SnapResult gridSnapResult)
|
||||||
|
result = gridSnapResult;
|
||||||
|
result ??= new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
|
|
||||||
|
base.UpdateTimeAndPosition(result.ScreenSpacePosition, result.Time ?? fallbackTime);
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
@ -131,6 +137,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
updateCursor();
|
updateCursor();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
@ -375,7 +383,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
private Vector2 getCursorPosition()
|
private Vector2 getCursorPosition()
|
||||||
{
|
{
|
||||||
var result = positionSnapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.ControlPoints ? SnapType.GlobalGrids : SnapType.All);
|
SnapResult? result = null;
|
||||||
|
var mousePosition = inputManager.CurrentState.Mouse.Position;
|
||||||
|
|
||||||
|
if (state != SliderPlacementState.ControlPoints)
|
||||||
|
{
|
||||||
|
result ??= composer?.TrySnapToNearbyObjects(mousePosition);
|
||||||
|
result ??= composer?.TrySnapToDistanceGrid(mousePosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
result ??= composer?.TrySnapToPositionGrid(mousePosition);
|
||||||
|
|
||||||
return ToLocalSpace(result?.ScreenSpacePosition ?? inputManager.CurrentState.Mouse.Position) - HitObject.Position;
|
return ToLocalSpace(result?.ScreenSpacePosition ?? inputManager.CurrentState.Mouse.Position) - HitObject.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
|
||||||
@ -8,12 +11,15 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
|
|||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit
|
namespace osu.Game.Rulesets.Osu.Edit
|
||||||
{
|
{
|
||||||
public partial class OsuBlueprintContainer : ComposeBlueprintContainer
|
public partial class OsuBlueprintContainer : ComposeBlueprintContainer
|
||||||
{
|
{
|
||||||
public OsuBlueprintContainer(HitObjectComposer composer)
|
public new OsuHitObjectComposer Composer => (OsuHitObjectComposer)base.Composer;
|
||||||
|
|
||||||
|
public OsuBlueprintContainer(OsuHitObjectComposer composer)
|
||||||
: base(composer)
|
: base(composer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -36,5 +42,68 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
return base.CreateHitObjectBlueprintFor(hitObject);
|
return base.CreateHitObjectBlueprintFor(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool TryMoveBlueprints(DragEvent e, IList<(SelectionBlueprint<HitObject> blueprint, Vector2[] originalSnapPositions)> blueprints)
|
||||||
|
{
|
||||||
|
Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
||||||
|
|
||||||
|
for (int i = 0; i < blueprints.Count; i++)
|
||||||
|
{
|
||||||
|
if (checkSnappingBlueprintToNearbyObjects(blueprints[i].blueprint, distanceTravelled, blueprints[i].originalSnapPositions))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no positional snapping could be performed, try unrestricted snapping from the earliest
|
||||||
|
// item in the selection.
|
||||||
|
|
||||||
|
// The final movement position, relative to movementBlueprintOriginalPosition.
|
||||||
|
Vector2 movePosition = blueprints.First().originalSnapPositions.First() + distanceTravelled;
|
||||||
|
|
||||||
|
// Retrieve a snapped position.
|
||||||
|
var result = Composer.TrySnapToNearbyObjects(movePosition);
|
||||||
|
result ??= Composer.TrySnapToDistanceGrid(movePosition);
|
||||||
|
if (Composer.TrySnapToPositionGrid(result?.ScreenSpacePosition ?? movePosition, result?.Time) is SnapResult gridSnapResult)
|
||||||
|
result = gridSnapResult;
|
||||||
|
result ??= new SnapResult(movePosition, null);
|
||||||
|
|
||||||
|
var referenceBlueprint = blueprints.First().blueprint;
|
||||||
|
bool moved = SelectionHandler.HandleMovement(new MoveSelectionEvent<HitObject>(referenceBlueprint, result.ScreenSpacePosition - referenceBlueprint.ScreenSpaceSelectionPoint));
|
||||||
|
if (moved)
|
||||||
|
ApplySnapResultTime(result, referenceBlueprint.Item.StartTime);
|
||||||
|
return moved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check for positional snap for given blueprint.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="blueprint">The blueprint to check for snapping.</param>
|
||||||
|
/// <param name="distanceTravelled">Distance travelled since start of dragging action.</param>
|
||||||
|
/// <param name="originalPositions">The snap positions of blueprint before start of dragging action.</param>
|
||||||
|
/// <returns>Whether an object to snap to was found.</returns>
|
||||||
|
private bool checkSnappingBlueprintToNearbyObjects(SelectionBlueprint<HitObject> blueprint, Vector2 distanceTravelled, Vector2[] originalPositions)
|
||||||
|
{
|
||||||
|
var currentPositions = blueprint.ScreenSpaceSnapPoints;
|
||||||
|
|
||||||
|
for (int i = 0; i < originalPositions.Length; i++)
|
||||||
|
{
|
||||||
|
Vector2 originalPosition = originalPositions[i];
|
||||||
|
var testPosition = originalPosition + distanceTravelled;
|
||||||
|
|
||||||
|
var positionalResult = Composer.TrySnapToNearbyObjects(testPosition);
|
||||||
|
|
||||||
|
if (positionalResult == null || positionalResult.ScreenSpacePosition == testPosition) continue;
|
||||||
|
|
||||||
|
var delta = positionalResult.ScreenSpacePosition - currentPositions[i];
|
||||||
|
|
||||||
|
// attempt to move the objects, and apply any time based snapping if we can.
|
||||||
|
if (SelectionHandler.HandleMovement(new MoveSelectionEvent<HitObject>(blueprint, delta)))
|
||||||
|
{
|
||||||
|
ApplySnapResultTime(positionalResult, blueprint.Item.StartTime);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
@ -31,6 +32,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit
|
namespace osu.Game.Rulesets.Osu.Edit
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public partial class OsuHitObjectComposer : HitObjectComposer<OsuHitObject>
|
public partial class OsuHitObjectComposer : HitObjectComposer<OsuHitObject>
|
||||||
{
|
{
|
||||||
public OsuHitObjectComposer(Ruleset ruleset)
|
public OsuHitObjectComposer(Ruleset ruleset)
|
||||||
@ -222,56 +224,56 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All)
|
[CanBeNull]
|
||||||
|
public SnapResult TrySnapToNearbyObjects(Vector2 screenSpacePosition, double? fallbackTime = null)
|
||||||
{
|
{
|
||||||
if (snapType.HasFlag(SnapType.NearbyObjects) && snapToVisibleBlueprints(screenSpacePosition, out var snapResult))
|
if (!snapToVisibleBlueprints(screenSpacePosition, out var snapResult))
|
||||||
{
|
return null;
|
||||||
// In the case of snapping to nearby objects, a time value is not provided.
|
|
||||||
// This matches the stable editor (which also uses current time), but with the introduction of time-snapping distance snap
|
|
||||||
// this could result in unexpected behaviour when distance snapping is turned on and a user attempts to place an object that is
|
|
||||||
// BOTH on a valid distance snap ring, and also at the same position as a previous object.
|
|
||||||
//
|
|
||||||
// We want to ensure that in this particular case, the time-snapping component of distance snap is still applied.
|
|
||||||
// The easiest way to ensure this is to attempt application of distance snap after a nearby object is found, and copy over
|
|
||||||
// the time value if the proposed positions are roughly the same.
|
|
||||||
if (snapType.HasFlag(SnapType.RelativeGrids) && DistanceSnapProvider.DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
|
|
||||||
{
|
|
||||||
(Vector2 distanceSnappedPosition, double distanceSnappedTime) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(snapResult.ScreenSpacePosition));
|
|
||||||
if (Precision.AlmostEquals(distanceSnapGrid.ToScreenSpace(distanceSnappedPosition), snapResult.ScreenSpacePosition, 1))
|
|
||||||
snapResult.Time = distanceSnappedTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (DistanceSnapProvider.DistanceSnapToggle.Value != TernaryState.True || distanceSnapGrid == null)
|
||||||
return snapResult;
|
return snapResult;
|
||||||
}
|
|
||||||
|
|
||||||
SnapResult result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType);
|
// In the case of snapping to nearby objects, a time value is not provided.
|
||||||
|
// This matches the stable editor (which also uses current time), but with the introduction of time-snapping distance snap
|
||||||
|
// this could result in unexpected behaviour when distance snapping is turned on and a user attempts to place an object that is
|
||||||
|
// BOTH on a valid distance snap ring, and also at the same position as a previous object.
|
||||||
|
//
|
||||||
|
// We want to ensure that in this particular case, the time-snapping component of distance snap is still applied.
|
||||||
|
// The easiest way to ensure this is to attempt application of distance snap after a nearby object is found, and copy over
|
||||||
|
// the time value if the proposed positions are roughly the same.
|
||||||
|
(Vector2 distanceSnappedPosition, double distanceSnappedTime) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(snapResult.ScreenSpacePosition));
|
||||||
|
snapResult.Time = Precision.AlmostEquals(distanceSnapGrid.ToScreenSpace(distanceSnappedPosition), snapResult.ScreenSpacePosition, 1)
|
||||||
|
? distanceSnappedTime
|
||||||
|
: fallbackTime;
|
||||||
|
|
||||||
if (snapType.HasFlag(SnapType.RelativeGrids))
|
return snapResult;
|
||||||
{
|
}
|
||||||
if (DistanceSnapProvider.DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
|
|
||||||
{
|
|
||||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
|
||||||
|
|
||||||
result.ScreenSpacePosition = distanceSnapGrid.ToScreenSpace(pos);
|
[CanBeNull]
|
||||||
result.Time = time;
|
public SnapResult TrySnapToDistanceGrid(Vector2 screenSpacePosition)
|
||||||
}
|
{
|
||||||
}
|
if (DistanceSnapProvider.DistanceSnapToggle.Value != TernaryState.True || distanceSnapGrid == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
if (snapType.HasFlag(SnapType.GlobalGrids))
|
var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition);
|
||||||
{
|
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||||
if (rectangularGridSnapToggle.Value == TernaryState.True)
|
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, playfield);
|
||||||
{
|
}
|
||||||
Vector2 pos = positionSnapGrid.GetSnappedPosition(positionSnapGrid.ToLocalSpace(result.ScreenSpacePosition));
|
|
||||||
|
|
||||||
// A grid which doesn't perfectly fit the playfield can produce a position that is outside of the playfield.
|
[CanBeNull]
|
||||||
// We need to clamp the position to the playfield bounds to ensure that the snapped position is always in bounds.
|
public SnapResult TrySnapToPositionGrid(Vector2 screenSpacePosition, double? fallbackTime = null)
|
||||||
pos = Vector2.Clamp(pos, Vector2.Zero, OsuPlayfield.BASE_SIZE);
|
{
|
||||||
|
if (rectangularGridSnapToggle.Value != TernaryState.True)
|
||||||
|
return null;
|
||||||
|
|
||||||
result.ScreenSpacePosition = positionSnapGrid.ToScreenSpace(pos);
|
Vector2 pos = positionSnapGrid.GetSnappedPosition(positionSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
// A grid which doesn't perfectly fit the playfield can produce a position that is outside of the playfield.
|
||||||
|
// We need to clamp the position to the playfield bounds to ensure that the snapped position is always in bounds.
|
||||||
|
pos = Vector2.Clamp(pos, Vector2.Zero, OsuPlayfield.BASE_SIZE);
|
||||||
|
|
||||||
|
var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition);
|
||||||
|
return new SnapResult(positionSnapGrid.ToScreenSpace(pos), null, playfield);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool snapToVisibleBlueprints(Vector2 screenSpacePosition, out SnapResult snapResult)
|
private bool snapToVisibleBlueprints(Vector2 screenSpacePosition, out SnapResult snapResult)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -16,6 +17,9 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
|
|
||||||
public new Hit HitObject => (Hit)base.HitObject;
|
public new Hit HitObject => (Hit)base.HitObject;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private TaikoHitObjectComposer? composer { get; set; }
|
||||||
|
|
||||||
public HitPlacementBlueprint()
|
public HitPlacementBlueprint()
|
||||||
: base(new Hit())
|
: base(new Hit())
|
||||||
{
|
{
|
||||||
@ -40,10 +44,12 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
{
|
{
|
||||||
|
var result = composer?.FindSnappedPositionAndTime(screenSpacePosition) ?? new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
base.UpdateTimeAndPosition(result);
|
base.UpdateTimeAndPosition(result.ScreenSpacePosition, result.Time ?? fallbackTime);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -26,12 +25,15 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
|
|
||||||
private readonly IHasDuration spanPlacementObject;
|
private readonly IHasDuration spanPlacementObject;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private TaikoHitObjectComposer? composer { get; set; }
|
||||||
|
|
||||||
protected override bool IsValidForPlacement => Precision.DefinitelyBigger(spanPlacementObject.Duration, 0);
|
protected override bool IsValidForPlacement => Precision.DefinitelyBigger(spanPlacementObject.Duration, 0);
|
||||||
|
|
||||||
public TaikoSpanPlacementBlueprint(HitObject hitObject)
|
public TaikoSpanPlacementBlueprint(HitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
spanPlacementObject = hitObject as IHasDuration;
|
spanPlacementObject = (hitObject as IHasDuration)!;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
@ -79,9 +81,11 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
EndPlacement(true);
|
EndPlacement(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||||
{
|
{
|
||||||
base.UpdateTimeAndPosition(result);
|
var result = composer?.FindSnappedPositionAndTime(screenSpacePosition) ?? new SnapResult(screenSpacePosition, fallbackTime);
|
||||||
|
|
||||||
|
base.UpdateTimeAndPosition(result.ScreenSpacePosition, result.Time ?? fallbackTime);
|
||||||
|
|
||||||
if (PlacementActive == PlacementState.Active)
|
if (PlacementActive == PlacementState.Active)
|
||||||
{
|
{
|
||||||
@ -116,6 +120,8 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
originalPosition = ToLocalSpace(result.ScreenSpacePosition);
|
originalPosition = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Taiko.Edit.Blueprints;
|
using osu.Game.Rulesets.Taiko.Edit.Blueprints;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Edit
|
namespace osu.Game.Rulesets.Taiko.Edit
|
||||||
{
|
{
|
||||||
public partial class TaikoBlueprintContainer : ComposeBlueprintContainer
|
public partial class TaikoBlueprintContainer : ComposeBlueprintContainer
|
||||||
{
|
{
|
||||||
public TaikoBlueprintContainer(HitObjectComposer composer)
|
public new TaikoHitObjectComposer Composer => (TaikoHitObjectComposer)base.Composer;
|
||||||
|
|
||||||
|
public TaikoBlueprintContainer(TaikoHitObjectComposer composer)
|
||||||
: base(composer)
|
: base(composer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -19,5 +25,22 @@ namespace osu.Game.Rulesets.Taiko.Edit
|
|||||||
|
|
||||||
public override HitObjectSelectionBlueprint CreateHitObjectBlueprintFor(HitObject hitObject) =>
|
public override HitObjectSelectionBlueprint CreateHitObjectBlueprintFor(HitObject hitObject) =>
|
||||||
new TaikoSelectionBlueprint(hitObject);
|
new TaikoSelectionBlueprint(hitObject);
|
||||||
|
|
||||||
|
protected override bool TryMoveBlueprints(DragEvent e, IList<(SelectionBlueprint<HitObject> blueprint, Vector2[] originalSnapPositions)> blueprints)
|
||||||
|
{
|
||||||
|
Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
||||||
|
|
||||||
|
// The final movement position, relative to movementBlueprintOriginalPosition.
|
||||||
|
Vector2 movePosition = blueprints.First().originalSnapPositions.First() + distanceTravelled;
|
||||||
|
|
||||||
|
// Retrieve a snapped position.
|
||||||
|
var result = Composer.FindSnappedPositionAndTime(movePosition);
|
||||||
|
|
||||||
|
var referenceBlueprint = blueprints.First().blueprint;
|
||||||
|
bool moved = SelectionHandler.HandleMovement(new MoveSelectionEvent<HitObject>(referenceBlueprint, result.ScreenSpacePosition - referenceBlueprint.ScreenSpaceSelectionPoint));
|
||||||
|
if (moved)
|
||||||
|
ApplySnapResultTime(result, referenceBlueprint.Item.StartTime);
|
||||||
|
return moved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
@ -12,6 +13,7 @@ using osu.Game.Screens.Edit.Compose.Components;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Edit
|
namespace osu.Game.Rulesets.Taiko.Edit
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public partial class TaikoHitObjectComposer : ScrollingHitObjectComposer<TaikoHitObject>
|
public partial class TaikoHitObjectComposer : ScrollingHitObjectComposer<TaikoHitObject>
|
||||||
{
|
{
|
||||||
protected override bool ApplyHorizontalCentering => false;
|
protected override bool ApplyHorizontalCentering => false;
|
||||||
|
@ -111,6 +111,16 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
SelectedItems.AddRange(targetComponents.SelectMany(list => list).Except(SelectedItems).ToArray());
|
SelectedItems.AddRange(targetComponents.SelectMany(list => list).Except(SelectedItems).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool TryMoveBlueprints(DragEvent e, IList<(SelectionBlueprint<ISerialisableDrawable> blueprint, Vector2[] originalSnapPositions)> blueprints)
|
||||||
|
{
|
||||||
|
Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
||||||
|
|
||||||
|
// The final movement position, relative to movementBlueprintOriginalPosition.
|
||||||
|
var referenceBlueprint = blueprints.First().blueprint;
|
||||||
|
Vector2 movePosition = blueprints.First().originalSnapPositions.First() + distanceTravelled;
|
||||||
|
return SelectionHandler.HandleMovement(new MoveSelectionEvent<ISerialisableDrawable>(referenceBlueprint, movePosition - referenceBlueprint.ScreenSpaceSelectionPoint));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Move the current selection spatially by the specified delta, in screen coordinates (ie. the same coordinates as the blueprints).
|
/// Move the current selection spatially by the specified delta, in screen coordinates (ie. the same coordinates as the blueprints).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -376,7 +376,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic.
|
/// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual ComposeBlueprintContainer CreateBlueprintContainer() => new ComposeBlueprintContainer(this);
|
protected abstract ComposeBlueprintContainer CreateBlueprintContainer();
|
||||||
|
|
||||||
protected virtual Drawable CreateHitObjectInspector() => new HitObjectInspector();
|
protected virtual Drawable CreateHitObjectInspector() => new HitObjectInspector();
|
||||||
|
|
||||||
@ -566,28 +566,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// <returns>The most relevant <see cref="Playfield"/>.</returns>
|
/// <returns>The most relevant <see cref="Playfield"/>.</returns>
|
||||||
protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield;
|
protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield;
|
||||||
|
|
||||||
public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All)
|
|
||||||
{
|
|
||||||
var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition);
|
|
||||||
double? targetTime = null;
|
|
||||||
|
|
||||||
if (snapType.HasFlag(SnapType.GlobalGrids))
|
|
||||||
{
|
|
||||||
if (playfield is ScrollingPlayfield scrollingPlayfield)
|
|
||||||
{
|
|
||||||
targetTime = scrollingPlayfield.TimeAtScreenSpacePosition(screenSpacePosition);
|
|
||||||
|
|
||||||
// apply beat snapping
|
|
||||||
targetTime = BeatSnapProvider.SnapTime(targetTime.Value);
|
|
||||||
|
|
||||||
// convert back to screen space
|
|
||||||
screenSpacePosition = scrollingPlayfield.ScreenSpacePositionAtTime(targetTime.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SnapResult(screenSpacePosition, targetTime, playfield);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,7 +574,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// Generally used to access certain methods without requiring a generic type for <see cref="HitObjectComposer{T}" />.
|
/// Generally used to access certain methods without requiring a generic type for <see cref="HitObjectComposer{T}" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Cached]
|
[Cached]
|
||||||
public abstract partial class HitObjectComposer : CompositeDrawable, IPositionSnapProvider
|
public abstract partial class HitObjectComposer : CompositeDrawable
|
||||||
{
|
{
|
||||||
public const float TOOLBOX_CONTRACTED_SIZE_LEFT = 60;
|
public const float TOOLBOX_CONTRACTED_SIZE_LEFT = 60;
|
||||||
public const float TOOLBOX_CONTRACTED_SIZE_RIGHT = 120;
|
public const float TOOLBOX_CONTRACTED_SIZE_RIGHT = 120;
|
||||||
@ -639,11 +617,5 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// <param name="timestamp">The time instant to seek to, in milliseconds.</param>
|
/// <param name="timestamp">The time instant to seek to, in milliseconds.</param>
|
||||||
/// <param name="objectDescription">The ruleset-specific description of objects to select at the given timestamp.</param>
|
/// <param name="objectDescription">The ruleset-specific description of objects to select at the given timestamp.</param>
|
||||||
public virtual void SelectFromTimestamp(double timestamp, string objectDescription) { }
|
public virtual void SelectFromTimestamp(double timestamp, string objectDescription) { }
|
||||||
|
|
||||||
#region IPositionSnapProvider
|
|
||||||
|
|
||||||
public abstract SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose;
|
using osu.Game.Screens.Edit.Compose;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit
|
namespace osu.Game.Rulesets.Edit
|
||||||
{
|
{
|
||||||
@ -87,14 +88,13 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the time and position of this <see cref="PlacementBlueprint"/> based on the provided snap information.
|
/// Updates the time and position of this <see cref="PlacementBlueprint"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="result">The snap result information.</param>
|
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double time)
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
|
||||||
{
|
{
|
||||||
if (PlacementActive == PlacementState.Waiting)
|
if (PlacementActive == PlacementState.Waiting)
|
||||||
{
|
{
|
||||||
HitObject.StartTime = result.Time ?? EditorClock.CurrentTime;
|
HitObject.StartTime = time;
|
||||||
|
|
||||||
if (HitObject is IHasComboInformation comboInformation)
|
if (HitObject is IHasComboInformation comboInformation)
|
||||||
comboInformation.UpdateComboInformation(getPreviousHitObject() as IHasComboInformation);
|
comboInformation.UpdateComboInformation(getPreviousHitObject() as IHasComboInformation);
|
||||||
@ -129,6 +129,8 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
for (int i = 0; i < hasRepeats.NodeSamples.Count; i++)
|
for (int i = 0; i < hasRepeats.NodeSamples.Count; i++)
|
||||||
hasRepeats.NodeSamples[i] = HitObject.Samples.Select(o => o.With()).ToList();
|
hasRepeats.NodeSamples[i] = HitObject.Samples.Select(o => o.With()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new SnapResult(screenSpacePosition, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A snap provider which given a proposed position for a hit object, potentially offers a more correct position and time value inferred from the context of the beatmap.
|
|
||||||
/// </summary>
|
|
||||||
[Cached]
|
|
||||||
public interface IPositionSnapProvider
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Given a position, find a valid time and position snap.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="screenSpacePosition">The screen-space position to be snapped.</param>
|
|
||||||
/// <param name="snapType">The type of snapping to apply.</param>
|
|
||||||
/// <returns>The time and position post-snapping.</returns>
|
|
||||||
SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All);
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,6 +7,7 @@ using osu.Framework.Input.Bindings;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit
|
namespace osu.Game.Rulesets.Edit
|
||||||
@ -75,18 +76,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
PlacementActive = PlacementState.Finished;
|
PlacementActive = PlacementState.Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public abstract SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime);
|
||||||
/// Determines which objects to snap to for the snap result in <see cref="UpdateTimeAndPosition"/>.
|
|
||||||
/// </summary>
|
|
||||||
public virtual SnapType SnapType => SnapType.All;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the time and position of this <see cref="PlacementBlueprint"/> based on the provided snap information.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="result">The snap result information.</param>
|
|
||||||
public virtual void UpdateTimeAndPosition(SnapResult result)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
|
@ -117,6 +117,23 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition)
|
||||||
|
{
|
||||||
|
var scrollingPlayfield = PlayfieldAtScreenSpacePosition(screenSpacePosition) as ScrollingPlayfield;
|
||||||
|
if (scrollingPlayfield == null)
|
||||||
|
return new SnapResult(screenSpacePosition, null);
|
||||||
|
|
||||||
|
double? targetTime = scrollingPlayfield.TimeAtScreenSpacePosition(screenSpacePosition);
|
||||||
|
|
||||||
|
// apply beat snapping
|
||||||
|
targetTime = BeatSnapProvider.SnapTime(targetTime.Value);
|
||||||
|
|
||||||
|
// convert back to screen space
|
||||||
|
screenSpacePosition = scrollingPlayfield.ScreenSpacePositionAtTime(targetTime.Value);
|
||||||
|
|
||||||
|
return new SnapResult(screenSpacePosition, targetTime, scrollingPlayfield);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum SnapType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Snapping to visible nearby objects.
|
|
||||||
/// </summary>
|
|
||||||
NearbyObjects = 1 << 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Grids which are global to the playfield.
|
|
||||||
/// </summary>
|
|
||||||
GlobalGrids = 1 << 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Grids which are relative to other nearby hit objects.
|
|
||||||
/// </summary>
|
|
||||||
RelativeGrids = 1 << 2,
|
|
||||||
|
|
||||||
AllGrids = RelativeGrids | GlobalGrids,
|
|
||||||
|
|
||||||
All = NearbyObjects | GlobalGrids | RelativeGrids,
|
|
||||||
}
|
|
||||||
}
|
|
@ -43,9 +43,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private readonly Dictionary<T, SelectionBlueprint<T>> blueprintMap = new Dictionary<T, SelectionBlueprint<T>>();
|
private readonly Dictionary<T, SelectionBlueprint<T>> blueprintMap = new Dictionary<T, SelectionBlueprint<T>>();
|
||||||
|
|
||||||
[Resolved(canBeNull: true)]
|
|
||||||
private IPositionSnapProvider snapProvider { get; set; }
|
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IEditorChangeHandler changeHandler { get; set; }
|
private IEditorChangeHandler changeHandler { get; set; }
|
||||||
|
|
||||||
@ -333,19 +330,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
protected void RemoveBlueprintFor(T item)
|
protected void RemoveBlueprintFor(T item)
|
||||||
{
|
{
|
||||||
if (!blueprintMap.Remove(item, out var blueprint))
|
if (!blueprintMap.Remove(item, out var blueprintToRemove))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
blueprint.Deselect();
|
blueprintToRemove.Deselect();
|
||||||
blueprint.Selected -= OnBlueprintSelected;
|
blueprintToRemove.Selected -= OnBlueprintSelected;
|
||||||
blueprint.Deselected -= OnBlueprintDeselected;
|
blueprintToRemove.Deselected -= OnBlueprintDeselected;
|
||||||
|
|
||||||
SelectionBlueprints.Remove(blueprint, true);
|
SelectionBlueprints.Remove(blueprintToRemove, true);
|
||||||
|
|
||||||
if (movementBlueprints?.Contains(blueprint) == true)
|
if (movementBlueprints?.Any(m => m.blueprint == blueprintToRemove) == true)
|
||||||
finishSelectionMovement();
|
finishSelectionMovement();
|
||||||
|
|
||||||
OnBlueprintRemoved(blueprint.Item);
|
OnBlueprintRemoved(blueprintToRemove.Item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -538,8 +535,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
#region Selection Movement
|
#region Selection Movement
|
||||||
|
|
||||||
private Vector2[][] movementBlueprintsOriginalPositions;
|
private (SelectionBlueprint<T> blueprint, Vector2[] originalSnapPositions)[] movementBlueprints;
|
||||||
private SelectionBlueprint<T>[] movementBlueprints;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a blueprint is currently being dragged.
|
/// Whether a blueprint is currently being dragged.
|
||||||
@ -572,8 +568,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Movement is tracked from the blueprint of the earliest item, since it only makes sense to distance snap from that item
|
// Movement is tracked from the blueprint of the earliest item, since it only makes sense to distance snap from that item
|
||||||
movementBlueprints = SortForMovement(SelectionHandler.SelectedBlueprints).ToArray();
|
movementBlueprints = SortForMovement(SelectionHandler.SelectedBlueprints).Select(b => (b, b.ScreenSpaceSnapPoints)).ToArray();
|
||||||
movementBlueprintsOriginalPositions = movementBlueprints.Select(m => m.ScreenSpaceSnapPoints).ToArray();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,68 +589,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (movementBlueprints == null)
|
if (movementBlueprints == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Debug.Assert(movementBlueprintsOriginalPositions != null);
|
return TryMoveBlueprints(e, movementBlueprints);
|
||||||
|
|
||||||
Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
|
||||||
|
|
||||||
if (snapProvider != null)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < movementBlueprints.Length; i++)
|
|
||||||
{
|
|
||||||
if (checkSnappingBlueprintToNearbyObjects(movementBlueprints[i], distanceTravelled, movementBlueprintsOriginalPositions[i]))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no positional snapping could be performed, try unrestricted snapping from the earliest
|
|
||||||
// item in the selection.
|
|
||||||
|
|
||||||
// The final movement position, relative to movementBlueprintOriginalPosition.
|
|
||||||
Vector2 movePosition = movementBlueprintsOriginalPositions.First().First() + distanceTravelled;
|
|
||||||
|
|
||||||
// Retrieve a snapped position.
|
|
||||||
var result = snapProvider?.FindSnappedPositionAndTime(movePosition, ~SnapType.NearbyObjects);
|
|
||||||
|
|
||||||
if (result == null)
|
|
||||||
{
|
|
||||||
return SelectionHandler.HandleMovement(new MoveSelectionEvent<T>(movementBlueprints.First(), movePosition - movementBlueprints.First().ScreenSpaceSelectionPoint));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApplySnapResult(movementBlueprints, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
protected abstract bool TryMoveBlueprints(DragEvent e, IList<(SelectionBlueprint<T> blueprint, Vector2[] originalSnapPositions)> blueprints);
|
||||||
/// Check for positional snap for given blueprint.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="blueprint">The blueprint to check for snapping.</param>
|
|
||||||
/// <param name="distanceTravelled">Distance travelled since start of dragging action.</param>
|
|
||||||
/// <param name="originalPositions">The snap positions of blueprint before start of dragging action.</param>
|
|
||||||
/// <returns>Whether an object to snap to was found.</returns>
|
|
||||||
private bool checkSnappingBlueprintToNearbyObjects(SelectionBlueprint<T> blueprint, Vector2 distanceTravelled, Vector2[] originalPositions)
|
|
||||||
{
|
|
||||||
var currentPositions = blueprint.ScreenSpaceSnapPoints;
|
|
||||||
|
|
||||||
for (int i = 0; i < originalPositions.Length; i++)
|
|
||||||
{
|
|
||||||
Vector2 originalPosition = originalPositions[i];
|
|
||||||
var testPosition = originalPosition + distanceTravelled;
|
|
||||||
|
|
||||||
var positionalResult = snapProvider.FindSnappedPositionAndTime(testPosition, SnapType.NearbyObjects);
|
|
||||||
|
|
||||||
if (positionalResult.ScreenSpacePosition == testPosition) continue;
|
|
||||||
|
|
||||||
var delta = positionalResult.ScreenSpacePosition - currentPositions[i];
|
|
||||||
|
|
||||||
// attempt to move the objects, and abort any time based snapping if we can.
|
|
||||||
if (SelectionHandler.HandleMovement(new MoveSelectionEvent<T>(blueprint, delta)))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool ApplySnapResult(SelectionBlueprint<T>[] blueprints, SnapResult result) =>
|
|
||||||
SelectionHandler.HandleMovement(new MoveSelectionEvent<T>(blueprints.First(), result.ScreenSpacePosition - blueprints.First().ScreenSpaceSelectionPoint));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finishes the current movement of selected blueprints.
|
/// Finishes the current movement of selected blueprints.
|
||||||
@ -666,7 +603,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (movementBlueprints == null)
|
if (movementBlueprints == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
movementBlueprintsOriginalPositions = null;
|
|
||||||
movementBlueprints = null;
|
movementBlueprints = null;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A blueprint container generally displayed as an overlay to a ruleset's playfield.
|
/// A blueprint container generally displayed as an overlay to a ruleset's playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ComposeBlueprintContainer : EditorBlueprintContainer
|
public abstract partial class ComposeBlueprintContainer : EditorBlueprintContainer
|
||||||
{
|
{
|
||||||
private readonly Container<PlacementBlueprint> placementBlueprintContainer;
|
private readonly Container<PlacementBlueprint> placementBlueprintContainer;
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => editorScreen?.MainContent.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => editorScreen?.MainContent.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
public ComposeBlueprintContainer(HitObjectComposer composer)
|
protected ComposeBlueprintContainer(HitObjectComposer composer)
|
||||||
: base(composer)
|
: base(composer)
|
||||||
{
|
{
|
||||||
placementBlueprintContainer = new Container<PlacementBlueprint>
|
placementBlueprintContainer = new Container<PlacementBlueprint>
|
||||||
@ -340,12 +340,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private void updatePlacementTimeAndPosition()
|
private void updatePlacementTimeAndPosition()
|
||||||
{
|
{
|
||||||
var snapResult = Composer.FindSnappedPositionAndTime(InputManager.CurrentState.Mouse.Position, CurrentPlacement.SnapType);
|
CurrentPlacement.UpdateTimeAndPosition(InputManager.CurrentState.Mouse.Position, Beatmap.SnapTime(EditorClock.CurrentTime, null));
|
||||||
|
|
||||||
// if no time was found from positional snapping, we should still quantize to the beat.
|
|
||||||
snapResult.Time ??= Beatmap.SnapTime(EditorClock.CurrentTime, null);
|
|
||||||
|
|
||||||
CurrentPlacement.UpdateTimeAndPosition(snapResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -17,7 +17,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components
|
namespace osu.Game.Screens.Edit.Compose.Components
|
||||||
{
|
{
|
||||||
public partial class EditorBlueprintContainer : BlueprintContainer<HitObject>
|
public abstract partial class EditorBlueprintContainer : BlueprintContainer<HitObject>
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
protected EditorClock EditorClock { get; private set; }
|
protected EditorClock EditorClock { get; private set; }
|
||||||
@ -73,27 +73,22 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
protected override IEnumerable<SelectionBlueprint<HitObject>> SortForMovement(IReadOnlyList<SelectionBlueprint<HitObject>> blueprints)
|
protected override IEnumerable<SelectionBlueprint<HitObject>> SortForMovement(IReadOnlyList<SelectionBlueprint<HitObject>> blueprints)
|
||||||
=> blueprints.OrderBy(b => b.Item.StartTime);
|
=> blueprints.OrderBy(b => b.Item.StartTime);
|
||||||
|
|
||||||
protected override bool ApplySnapResult(SelectionBlueprint<HitObject>[] blueprints, SnapResult result)
|
protected void ApplySnapResultTime(SnapResult result, double referenceTime)
|
||||||
{
|
{
|
||||||
if (!base.ApplySnapResult(blueprints, result))
|
if (!result.Time.HasValue)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
if (result.Time.HasValue)
|
// Apply the start time at the newly snapped-to position
|
||||||
|
double offset = result.Time.Value - referenceTime;
|
||||||
|
|
||||||
|
if (offset != 0)
|
||||||
{
|
{
|
||||||
// Apply the start time at the newly snapped-to position
|
Beatmap.PerformOnSelection(obj =>
|
||||||
double offset = result.Time.Value - blueprints.First().Item.StartTime;
|
|
||||||
|
|
||||||
if (offset != 0)
|
|
||||||
{
|
{
|
||||||
Beatmap.PerformOnSelection(obj =>
|
obj.StartTime += offset;
|
||||||
{
|
Beatmap.Update(obj);
|
||||||
obj.StartTime += offset;
|
});
|
||||||
Beatmap.Update(obj);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddBlueprintFor(HitObject item)
|
protected override void AddBlueprintFor(HitObject item)
|
||||||
|
@ -22,7 +22,7 @@ using osuTK.Input;
|
|||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
{
|
{
|
||||||
[Cached]
|
[Cached]
|
||||||
public partial class Timeline : ZoomableScrollContainer, IPositionSnapProvider
|
public partial class Timeline : ZoomableScrollContainer
|
||||||
{
|
{
|
||||||
private const float timeline_height = 80;
|
private const float timeline_height = 80;
|
||||||
|
|
||||||
@ -332,7 +332,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
return (float)(time / editorClock.TrackLength * Content.DrawWidth);
|
return (float)(time / editorClock.TrackLength * Content.DrawWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All)
|
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition)
|
||||||
{
|
{
|
||||||
double time = TimeAtPosition(Content.ToLocalSpace(screenSpacePosition).X);
|
double time = TimeAtPosition(Content.ToLocalSpace(screenSpacePosition).X);
|
||||||
return new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(time));
|
return new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(time));
|
||||||
|
@ -107,6 +107,23 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
return base.OnDragStart(e);
|
return base.OnDragStart(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool TryMoveBlueprints(DragEvent e, IList<(SelectionBlueprint<HitObject> blueprint, Vector2[] originalSnapPositions)> blueprints)
|
||||||
|
{
|
||||||
|
Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
||||||
|
|
||||||
|
// The final movement position, relative to movementBlueprintOriginalPosition.
|
||||||
|
Vector2 movePosition = blueprints.First().originalSnapPositions.First() + distanceTravelled;
|
||||||
|
|
||||||
|
// Retrieve a snapped position.
|
||||||
|
var result = timeline?.FindSnappedPositionAndTime(movePosition) ?? new SnapResult(movePosition, null);
|
||||||
|
|
||||||
|
var referenceBlueprint = blueprints.First().blueprint;
|
||||||
|
bool moved = SelectionHandler.HandleMovement(new MoveSelectionEvent<HitObject>(referenceBlueprint, result.ScreenSpacePosition - referenceBlueprint.ScreenSpaceSelectionPoint));
|
||||||
|
if (moved)
|
||||||
|
ApplySnapResultTime(result, referenceBlueprint.Item.StartTime);
|
||||||
|
return moved;
|
||||||
|
}
|
||||||
|
|
||||||
private float dragTimeAccumulated;
|
private float dragTimeAccumulated;
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
base.Content.Add(HitObjectContainer = CreateHitObjectContainer().With(c => c.Clock = new FramedClock(new StopwatchClock())));
|
base.Content.Add(HitObjectContainer = CreateHitObjectContainer().With(c => c.Clock = new FramedClock(new StopwatchClock())));
|
||||||
base.Content.Add(new MouseMovementInterceptor
|
base.Content.Add(new MouseMovementInterceptor
|
||||||
{
|
{
|
||||||
MouseMoved = updatePlacementTimeAndPosition,
|
MouseMoved = UpdatePlacementTimeAndPosition,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,13 +93,10 @@ namespace osu.Game.Tests.Visual
|
|||||||
if (CurrentBlueprint.PlacementActive == PlacementBlueprint.PlacementState.Finished)
|
if (CurrentBlueprint.PlacementActive == PlacementBlueprint.PlacementState.Finished)
|
||||||
ResetPlacement();
|
ResetPlacement();
|
||||||
|
|
||||||
updatePlacementTimeAndPosition();
|
UpdatePlacementTimeAndPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePlacementTimeAndPosition() => CurrentBlueprint.UpdateTimeAndPosition(SnapForBlueprint(CurrentBlueprint));
|
protected virtual void UpdatePlacementTimeAndPosition() => CurrentBlueprint.UpdateTimeAndPosition(InputManager.CurrentState.Mouse.Position, 0);
|
||||||
|
|
||||||
protected virtual SnapResult SnapForBlueprint(HitObjectPlacementBlueprint blueprint) =>
|
|
||||||
new SnapResult(InputManager.CurrentState.Mouse.Position, null);
|
|
||||||
|
|
||||||
public override void Add(Drawable drawable)
|
public override void Add(Drawable drawable)
|
||||||
{
|
{
|
||||||
@ -108,7 +105,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
if (drawable is HitObjectPlacementBlueprint blueprint)
|
if (drawable is HitObjectPlacementBlueprint blueprint)
|
||||||
{
|
{
|
||||||
blueprint.Show();
|
blueprint.Show();
|
||||||
blueprint.UpdateTimeAndPosition(SnapForBlueprint(blueprint));
|
UpdatePlacementTimeAndPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user