Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to change slider length by dragging slider tail #25953

Merged
merged 31 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
22a0bc7
Add basic slider distance control
OliBomby Dec 19, 2023
1258a9d
Find closest distance value to mouse
OliBomby Dec 20, 2023
1365a1b
fix tests
OliBomby Dec 20, 2023
3aaf0b3
Add slider tail dragging test
OliBomby Dec 20, 2023
66f4dcc
fix repeat sliders half
OliBomby Dec 20, 2023
f7cb6b9
Fix all repeat sliders being draggable
OliBomby Dec 20, 2023
d000da7
fix code quality
OliBomby Dec 20, 2023
2261d65
Merge remote-tracking branch 'upstream/master' into free-sliders
OliBomby May 28, 2024
203e928
End circle only gets brighter once shift is held
OliBomby May 28, 2024
7254096
fix isDragging being always true
OliBomby Jun 1, 2024
ca41c84
trim excess control points on drag end
OliBomby Jun 1, 2024
bb38cb4
simplify tracking changes in shift key status
OliBomby Jun 3, 2024
77b47ad
simplify nullability annotation
OliBomby Jun 3, 2024
484e04a
Merge branch 'free-sliders' of https://github.com/OliBomby/osu into f…
OliBomby Jun 3, 2024
34c4ee7
add CanBeNull attribute to LastRepeat
OliBomby Jun 3, 2024
8291999
dont light up tail piece when hovering anchor
OliBomby Jun 4, 2024
efc8e14
activate length change with context menu
OliBomby Jun 19, 2024
d7fee53
Merge remote-tracking branch 'upstream/master' into free-sliders
OliBomby Jun 19, 2024
b24bfa2
click to choose length instead of drag
OliBomby Jun 19, 2024
956bdbc
fix tests
OliBomby Jun 19, 2024
3926af1
Use draggable handle for length adjust
OliBomby Jul 3, 2024
5697c82
add a small bias towards longer distances to prevent jittery behaviou…
OliBomby Jul 3, 2024
89f106d
Merge remote-tracking branch 'upstream/master' into free-sliders
OliBomby Jul 3, 2024
c1414f3
Merge branch 'master' into free-sliders
peppy Jul 18, 2024
9fb9a54
hold shift to adjust velocity instead of duration
OliBomby Jul 22, 2024
c57232c
enforce minimum duration based on snap
OliBomby Jul 22, 2024
0c6bee4
Merge branch 'master' into free-sliders
peppy Aug 19, 2024
094b184
snap the slider duration in normal drag
OliBomby Aug 21, 2024
4fc96eb
Tidy some thing up
peppy Aug 27, 2024
50a8348
Apply NRT to remaining classes in slider blueprint namespace
peppy Aug 27, 2024
1b26e1c
Merge branch 'master' into free-sliders
peppy Aug 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,44 @@ public void TestMultipleControlPointDeselectionViaClickOutside()
checkControlPointSelected(1, false);
}

[Test]
public void TestAdjustLength()
{
AddStep("move mouse to drag marker", () =>
{
Vector2 position = slider.Position + slider.Path.PositionAt(1) + new Vector2(60, 0);
InputManager.MoveMouseTo(drawableObject.Parent!.ToScreenSpace(position));
});
AddStep("start drag", () => InputManager.PressButton(MouseButton.Left));
AddStep("move mouse to control point 1", () =>
{
Vector2 position = slider.Position + slider.Path.ControlPoints[1].Position + new Vector2(60, 0);
InputManager.MoveMouseTo(drawableObject.Parent!.ToScreenSpace(position));
});
AddStep("end adjust length", () => InputManager.ReleaseButton(MouseButton.Left));
AddAssert("expected distance halved",
() => Precision.AlmostEquals(slider.Path.Distance, 172.2, 0.1));

AddStep("move mouse to drag marker", () =>
{
Vector2 position = slider.Position + slider.Path.PositionAt(1) + new Vector2(60, 0);
InputManager.MoveMouseTo(drawableObject.Parent!.ToScreenSpace(position));
});
AddStep("start drag", () => InputManager.PressButton(MouseButton.Left));
AddStep("move mouse beyond last control point", () =>
{
Vector2 position = slider.Position + slider.Path.ControlPoints[2].Position + new Vector2(100, 0);
InputManager.MoveMouseTo(drawableObject.Parent!.ToScreenSpace(position));
});
AddStep("end adjust length", () => InputManager.ReleaseButton(MouseButton.Left));
AddAssert("expected distance is calculated distance",
() => Precision.AlmostEquals(slider.Path.Distance, slider.Path.CalculatedDistance, 0.1));

moveMouseToControlPoint(1);
AddAssert("expected distance is unchanged",
() => Precision.AlmostEquals(slider.Path.Distance, slider.Path.CalculatedDistance, 0.1));
}

private void moveHitObject()
{
AddStep("move hitobject", () =>
Expand Down
121 changes: 120 additions & 1 deletion osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleOverlay.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
// 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;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;

namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
{
public partial class SliderCircleOverlay : CompositeDrawable
{
public RectangleF VisibleQuad
{
get
{
var result = CirclePiece.ScreenSpaceDrawQuad.AABBFloat;

if (endDragMarkerContainer == null) return result;

var size = result.Size * 1.4f;
var location = result.TopLeft - result.Size * 0.2f;
Comment on lines +25 to +26
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit hand-wavey but I guess it's okay.

return new RectangleF(location, size);
}
}

protected readonly HitCirclePiece CirclePiece;

private readonly Slider slider;
private readonly SliderPosition position;
private readonly HitCircleOverlapMarker? marker;
private readonly Container? endDragMarkerContainer;

public SliderCircleOverlay(Slider slider, SliderPosition position)
{
Expand All @@ -24,26 +48,121 @@ public SliderCircleOverlay(Slider slider, SliderPosition position)
AddInternal(marker = new HitCircleOverlapMarker());

AddInternal(CirclePiece = new HitCirclePiece());

if (position == SliderPosition.End)
{
AddInternal(endDragMarkerContainer = new Container
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Padding = new MarginPadding(-2.5f),
Child = EndDragMarker = new SliderEndDragMarker()
});
}
}

public SliderEndDragMarker? EndDragMarker { get; }

protected override void Update()
{
base.Update();

var circle = position == SliderPosition.Start ? (HitCircle)slider.HeadCircle : slider.TailCircle;
var circle = position == SliderPosition.Start ? (HitCircle)slider.HeadCircle :
slider.RepeatCount % 2 == 0 ? slider.TailCircle : slider.LastRepeat!;

CirclePiece.UpdateFrom(circle);
marker?.UpdateFrom(circle);

if (endDragMarkerContainer != null)
{
endDragMarkerContainer.Position = circle.Position;
endDragMarkerContainer.Scale = CirclePiece.Scale * 1.2f;
var diff = slider.Path.PositionAt(1) - slider.Path.PositionAt(0.99f);
endDragMarkerContainer.Rotation = float.RadiansToDegrees(MathF.Atan2(diff.Y, diff.X));
}
}

public override void Hide()
{
CirclePiece.Hide();
endDragMarkerContainer?.Hide();
}

public override void Show()
{
CirclePiece.Show();
endDragMarkerContainer?.Show();
}

public partial class SliderEndDragMarker : SmoothPath
{
public Action<DragStartEvent>? StartDrag { get; set; }
public Action<DragEvent>? Drag { get; set; }
public Action? EndDrag { get; set; }

[Resolved]
private OsuColour colours { get; set; } = null!;

[BackgroundDependencyLoader]
private void load()
{
var path = PathApproximator.CircularArcToPiecewiseLinear([
new Vector2(0, OsuHitObject.OBJECT_RADIUS),
new Vector2(OsuHitObject.OBJECT_RADIUS, 0),
new Vector2(0, -OsuHitObject.OBJECT_RADIUS)
]);

Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
PathRadius = 5;
Vertices = path;
}

protected override void LoadComplete()
{
base.LoadComplete();

updateState();
}

protected override bool OnHover(HoverEvent e)
{
updateState();
return true;
}

protected override void OnHoverLost(HoverLostEvent e)
{
updateState();
base.OnHoverLost(e);
}

protected override bool OnDragStart(DragStartEvent e)
{
updateState();
StartDrag?.Invoke(e);
return true;
}

protected override void OnDrag(DragEvent e)
{
updateState();
base.OnDrag(e);
Drag?.Invoke(e);
}

protected override void OnDragEnd(DragEndEvent e)
{
updateState();
EndDrag?.Invoke();
base.OnDragEnd(e);
}

private void updateState()
{
Colour = IsHovered || IsDragged ? colours.Red : colours.Yellow;
}
}
}
}
Loading
Loading