2019-01-24 16:43:03 +08:00
// 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.
2018-10-29 13:07:06 +08:00
2019-10-24 18:02:59 +08:00
using System ;
2018-10-29 13:07:06 +08:00
using osu.Framework.Allocation ;
2019-10-31 15:23:54 +08:00
using osu.Framework.Bindables ;
2018-10-29 13:07:06 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Lines ;
using osu.Framework.Graphics.Shapes ;
using osu.Framework.Input.Events ;
using osu.Game.Graphics ;
2019-11-07 13:00:12 +08:00
using osu.Game.Rulesets.Edit ;
2018-10-29 13:07:06 +08:00
using osu.Game.Rulesets.Osu.Objects ;
2018-11-20 15:51:59 +08:00
using osuTK ;
2019-10-31 15:23:54 +08:00
using osuTK.Graphics ;
2018-10-29 13:07:06 +08:00
2018-11-07 15:08:56 +08:00
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
2018-10-29 13:07:06 +08:00
{
2019-09-27 17:45:22 +08:00
public class PathControlPointPiece : BlueprintPiece < Slider >
2018-10-29 13:07:06 +08:00
{
2019-10-31 16:13:10 +08:00
public Action < int > RequestSelection ;
2019-10-24 18:02:59 +08:00
public Action < Vector2 [ ] > ControlPointsChanged ;
2019-11-03 17:41:29 +08:00
public readonly BindableBool IsSelected = new BindableBool ( ) ;
2019-10-31 15:23:54 +08:00
public readonly int Index ;
2018-10-29 13:07:06 +08:00
2019-10-31 15:23:54 +08:00
private readonly Slider slider ;
2018-10-29 13:07:06 +08:00
private readonly Path path ;
2019-10-31 15:23:54 +08:00
private readonly Container marker ;
private readonly Drawable markerRing ;
2018-10-29 13:07:06 +08:00
2019-11-07 13:00:12 +08:00
[Resolved(CanBeNull = true)]
private IDistanceSnapProvider snapProvider { get ; set ; }
2018-10-29 13:07:06 +08:00
[Resolved]
private OsuColour colours { get ; set ; }
2018-11-01 02:52:24 +08:00
public PathControlPointPiece ( Slider slider , int index )
2018-10-29 13:07:06 +08:00
{
this . slider = slider ;
2019-10-31 15:23:54 +08:00
Index = index ;
2018-10-29 13:07:06 +08:00
Origin = Anchor . Centre ;
2018-10-29 15:15:04 +08:00
AutoSizeAxes = Axes . Both ;
2018-10-29 13:07:06 +08:00
InternalChildren = new Drawable [ ]
{
path = new SmoothPath
{
Anchor = Anchor . Centre ,
2019-03-07 16:39:19 +08:00
PathRadius = 1
2018-10-29 13:07:06 +08:00
} ,
2019-10-31 15:23:54 +08:00
marker = new Container
2018-10-29 13:07:06 +08:00
{
2018-10-29 15:15:04 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2019-10-31 15:23:54 +08:00
AutoSizeAxes = Axes . Both ,
Children = new [ ]
{
new Circle
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Size = new Vector2 ( 10 ) ,
} ,
markerRing = new CircularContainer
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Size = new Vector2 ( 14 ) ,
Masking = true ,
BorderThickness = 2 ,
BorderColour = Color4 . White ,
Alpha = 0 ,
Child = new Box
{
RelativeSizeAxes = Axes . Both ,
Alpha = 0 ,
AlwaysPresent = true
}
}
}
2018-10-29 13:07:06 +08:00
}
} ;
}
protected override void Update ( )
{
base . Update ( ) ;
2019-10-31 15:23:54 +08:00
Position = slider . StackedPosition + slider . Path . ControlPoints [ Index ] ;
updateMarkerDisplay ( ) ;
updateConnectingPath ( ) ;
}
/// <summary>
/// Updates the state of the circular control point marker.
/// </summary>
private void updateMarkerDisplay ( )
{
markerRing . Alpha = IsSelected . Value ? 1 : 0 ;
2018-10-29 13:07:06 +08:00
2019-10-31 15:23:54 +08:00
Color4 colour = isSegmentSeparator ? colours . Red : colours . Yellow ;
2019-11-05 01:21:50 +08:00
if ( IsHovered | | IsSelected . Value )
2019-10-31 15:23:54 +08:00
colour = Color4 . White ;
marker . Colour = colour ;
}
2018-10-29 13:07:06 +08:00
2019-10-31 15:23:54 +08:00
/// <summary>
/// Updates the path connecting this control point to the previous one.
/// </summary>
private void updateConnectingPath ( )
{
2018-10-29 13:07:06 +08:00
path . ClearVertices ( ) ;
2019-10-31 15:23:54 +08:00
if ( Index ! = slider . Path . ControlPoints . Length - 1 )
2018-10-29 13:07:06 +08:00
{
path . AddVertex ( Vector2 . Zero ) ;
2019-10-31 15:23:54 +08:00
path . AddVertex ( slider . Path . ControlPoints [ Index + 1 ] - slider . Path . ControlPoints [ Index ] ) ;
2018-10-29 13:07:06 +08:00
}
path . OriginPosition = path . PositionInBoundingBox ( Vector2 . Zero ) ;
}
2019-10-31 15:23:54 +08:00
// The connecting path is excluded from positional input
2018-10-29 15:15:04 +08:00
public override bool ReceivePositionalInputAt ( Vector2 screenSpacePos ) = > marker . ReceivePositionalInputAt ( screenSpacePos ) ;
2019-10-31 16:13:10 +08:00
protected override bool OnMouseDown ( MouseDownEvent e )
{
2019-11-03 18:59:37 +08:00
if ( RequestSelection ! = null )
{
RequestSelection . Invoke ( Index ) ;
return true ;
}
return false ;
2019-10-31 16:13:10 +08:00
}
2019-11-05 01:21:50 +08:00
protected override bool OnMouseUp ( MouseUpEvent e ) = > RequestSelection ! = null ;
protected override bool OnClick ( ClickEvent e ) = > RequestSelection ! = null ;
2018-10-29 13:07:06 +08:00
protected override bool OnDragStart ( DragStartEvent e ) = > true ;
protected override bool OnDrag ( DragEvent e )
{
2018-11-01 14:38:19 +08:00
var newControlPoints = slider . Path . ControlPoints . ToArray ( ) ;
2018-10-31 17:02:08 +08:00
2019-10-31 15:23:54 +08:00
if ( Index = = 0 )
2018-10-29 14:36:43 +08:00
{
2019-11-07 13:00:12 +08:00
// Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account
( Vector2 snappedPosition , double snappedTime ) = snapProvider ? . GetSnappedPosition ( e . MousePosition , slider . StartTime ) ? ? ( e . MousePosition , slider . StartTime ) ;
Vector2 movementDelta = snappedPosition - slider . Position ;
slider . Position + = movementDelta ;
slider . StartTime = snappedTime ;
2018-10-29 14:36:43 +08:00
// Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
for ( int i = 1 ; i < newControlPoints . Length ; i + + )
2019-11-07 13:00:12 +08:00
newControlPoints [ i ] - = movementDelta ;
2018-10-29 14:36:43 +08:00
}
else
2019-10-31 15:23:54 +08:00
newControlPoints [ Index ] + = e . Delta ;
2018-10-29 13:07:06 +08:00
2018-10-31 17:02:08 +08:00
if ( isSegmentSeparatorWithNext )
2019-10-31 15:23:54 +08:00
newControlPoints [ Index + 1 ] = newControlPoints [ Index ] ;
2018-10-31 17:02:08 +08:00
if ( isSegmentSeparatorWithPrevious )
2019-10-31 15:23:54 +08:00
newControlPoints [ Index - 1 ] = newControlPoints [ Index ] ;
2018-10-31 17:02:08 +08:00
2019-10-24 18:02:59 +08:00
ControlPointsChanged ? . Invoke ( newControlPoints ) ;
2018-10-29 13:07:06 +08:00
return true ;
}
protected override bool OnDragEnd ( DragEndEvent e ) = > true ;
2018-10-31 17:02:08 +08:00
private bool isSegmentSeparator = > isSegmentSeparatorWithNext | | isSegmentSeparatorWithPrevious ;
2018-10-29 14:52:28 +08:00
2019-10-31 15:23:54 +08:00
private bool isSegmentSeparatorWithNext = > Index < slider . Path . ControlPoints . Length - 1 & & slider . Path . ControlPoints [ Index + 1 ] = = slider . Path . ControlPoints [ Index ] ;
2018-10-29 14:52:28 +08:00
2019-10-31 15:23:54 +08:00
private bool isSegmentSeparatorWithPrevious = > Index > 0 & & slider . Path . ControlPoints [ Index - 1 ] = = slider . Path . ControlPoints [ Index ] ;
2018-10-29 13:07:06 +08:00
}
}