2019-10-18 16:59:54 +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.
2021-04-19 17:25:30 +08:00
using System ;
2019-10-18 16:59:54 +08:00
using System.Collections.Generic ;
using System.Linq ;
using osu.Framework.Allocation ;
using osu.Framework.Bindables ;
using osu.Framework.Extensions ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Game.Beatmaps.ControlPoints ;
2020-09-30 11:45:43 +08:00
using osu.Game.Extensions ;
2019-10-18 16:59:54 +08:00
using osu.Game.Graphics ;
using osu.Game.Graphics.Sprites ;
2021-04-19 14:06:07 +08:00
using osu.Game.Screens.Edit.Timing.RowAttributes ;
2019-10-18 16:59:54 +08:00
using osuTK ;
namespace osu.Game.Screens.Edit.Timing
{
2022-11-24 13:32:20 +08:00
public partial class ControlPointTable : EditorTable
2019-10-18 16:59:54 +08:00
{
[Resolved]
2023-12-26 19:42:04 +08:00
private Bindable < ControlPointGroup ? > selectedGroup { get ; set ; } = null ! ;
2019-10-18 16:59:54 +08:00
2021-04-13 22:26:19 +08:00
[Resolved]
2022-11-27 10:45:56 +08:00
private EditorClock clock { get ; set ; } = null ! ;
2021-04-13 22:26:19 +08:00
2023-03-02 17:57:54 +08:00
public const float TIMING_COLUMN_WIDTH = 300 ;
2021-04-19 17:25:30 +08:00
2019-10-25 19:13:22 +08:00
public IEnumerable < ControlPointGroup > ControlGroups
2019-10-18 16:59:54 +08:00
{
set
{
2023-12-26 19:42:04 +08:00
int selectedIndex = GetIndexForObject ( selectedGroup . Value ) ;
2019-10-18 16:59:54 +08:00
Content = null ;
2021-04-13 22:05:58 +08:00
BackgroundFlow . Clear ( ) ;
2019-10-18 16:59:54 +08:00
2022-11-27 11:23:08 +08:00
if ( ! value . Any ( ) )
2019-10-18 16:59:54 +08:00
return ;
2019-10-25 19:13:22 +08:00
foreach ( var group in value )
2019-10-18 16:59:54 +08:00
{
2021-04-13 22:26:19 +08:00
BackgroundFlow . Add ( new RowBackground ( group )
{
2024-01-17 13:10:37 +08:00
// schedule to give time for any modified focused text box to lose focus and commit changes (e.g. BPM / time signature textboxes) before switching to new point.
Action = ( ) = > Schedule ( ( ) = >
2021-04-13 22:26:19 +08:00
{
2023-12-26 20:20:18 +08:00
SetSelectedRow ( group ) ;
2021-04-13 22:26:19 +08:00
clock . SeekSmoothlyTo ( group . Time ) ;
2024-01-17 13:10:37 +08:00
} )
2021-04-13 22:26:19 +08:00
} ) ;
2019-10-18 16:59:54 +08:00
}
Columns = createHeaders ( ) ;
2022-02-02 04:22:22 +08:00
Content = value . Select ( createContent ) . ToArray ( ) . ToRectangular ( ) ;
2022-06-21 11:53:06 +08:00
2023-12-26 20:20:18 +08:00
// Attempt to retain selection.
if ( SetSelectedRow ( selectedGroup . Value ) )
return ;
// Some operations completely obliterate references, so best-effort reselect based on index.
if ( SetSelectedRow ( GetObjectAtIndex ( selectedIndex ) ) )
return ;
// Selection could not be retained.
selectedGroup . Value = null ;
2019-10-18 16:59:54 +08:00
}
}
2021-04-13 22:26:19 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2023-12-26 20:20:18 +08:00
// Handle external selections.
selectedGroup . BindValueChanged ( g = > SetSelectedRow ( g . NewValue ) , true ) ;
}
protected override bool SetSelectedRow ( object? item )
{
if ( ! base . SetSelectedRow ( item ) )
return false ;
selectedGroup . Value = item as ControlPointGroup ;
return true ;
2021-04-13 22:26:19 +08:00
}
2019-10-18 16:59:54 +08:00
private TableColumn [ ] createHeaders ( )
{
var columns = new List < TableColumn >
{
2022-11-30 02:22:07 +08:00
new TableColumn ( "Time" , Anchor . CentreLeft , new Dimension ( GridSizeMode . Absolute , TIMING_COLUMN_WIDTH ) ) ,
new TableColumn ( "Attributes" , Anchor . CentreLeft ) ,
2019-10-18 16:59:54 +08:00
} ;
return columns . ToArray ( ) ;
}
2022-02-02 04:22:22 +08:00
private Drawable [ ] createContent ( ControlPointGroup group )
2019-10-18 16:59:54 +08:00
{
2021-04-19 17:25:30 +08:00
return new Drawable [ ]
2019-10-18 16:59:54 +08:00
{
2022-11-27 10:47:02 +08:00
new ControlGroupTiming ( group ) ,
new ControlGroupAttributes ( group , c = > c is not TimingControlPoint )
} ;
}
private partial class ControlGroupTiming : FillFlowContainer
{
public ControlGroupTiming ( ControlPointGroup group )
{
Name = @"ControlGroupTiming" ;
RelativeSizeAxes = Axes . Y ;
Width = TIMING_COLUMN_WIDTH ;
Spacing = new Vector2 ( 5 ) ;
Children = new Drawable [ ]
2021-04-19 17:25:30 +08:00
{
2022-11-27 10:47:02 +08:00
new OsuSpriteText
2021-04-19 17:25:30 +08:00
{
2022-11-27 10:47:02 +08:00
Text = group . Time . ToEditorFormattedString ( ) ,
Font = OsuFont . GetFont ( size : TEXT_SIZE , weight : FontWeight . Bold ) ,
Width = 70 ,
Anchor = Anchor . CentreLeft ,
Origin = Anchor . CentreLeft ,
} ,
new ControlGroupAttributes ( group , c = > c is TimingControlPoint )
{
Anchor = Anchor . CentreLeft ,
Origin = Anchor . CentreLeft ,
2021-04-19 17:25:30 +08:00
}
2022-11-27 10:47:02 +08:00
} ;
}
2021-04-19 17:25:30 +08:00
}
2019-10-18 16:59:54 +08:00
2022-11-24 13:32:20 +08:00
private partial class ControlGroupAttributes : CompositeDrawable
2019-10-18 16:59:54 +08:00
{
2021-04-19 17:25:30 +08:00
private readonly Func < ControlPoint , bool > matchFunction ;
2020-11-04 14:29:14 +08:00
private readonly IBindableList < ControlPoint > controlPoints = new BindableList < ControlPoint > ( ) ;
2019-10-18 16:59:54 +08:00
2019-10-27 14:19:36 +08:00
private readonly FillFlowContainer fill ;
2019-10-18 16:59:54 +08:00
2021-04-19 17:25:30 +08:00
public ControlGroupAttributes ( ControlPointGroup group , Func < ControlPoint , bool > matchFunction )
2019-10-27 14:19:36 +08:00
{
2021-04-19 17:25:30 +08:00
this . matchFunction = matchFunction ;
AutoSizeAxes = Axes . X ;
RelativeSizeAxes = Axes . Y ;
2022-11-27 10:47:02 +08:00
Name = @"ControlGroupAttributes" ;
2021-04-19 17:25:30 +08:00
2019-10-27 14:19:36 +08:00
InternalChild = fill = new FillFlowContainer
{
2021-04-19 17:25:30 +08:00
AutoSizeAxes = Axes . X ,
RelativeSizeAxes = Axes . Y ,
2019-10-27 14:19:36 +08:00
Direction = FillDirection . Horizontal ,
Spacing = new Vector2 ( 2 )
} ;
2020-11-04 14:29:14 +08:00
controlPoints . BindTo ( group . ControlPoints ) ;
2020-10-01 18:29:34 +08:00
}
[BackgroundDependencyLoader]
private void load ( )
{
2019-10-27 14:19:36 +08:00
createChildren ( ) ;
}
2019-10-18 16:59:54 +08:00
2020-10-08 04:57:20 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2022-06-24 20:25:23 +08:00
controlPoints . CollectionChanged + = ( _ , _ ) = > createChildren ( ) ;
2020-10-08 04:57:20 +08:00
}
2019-10-27 14:19:36 +08:00
private void createChildren ( )
{
2021-04-19 14:06:07 +08:00
fill . ChildrenEnumerable = controlPoints
2021-04-19 17:25:30 +08:00
. Where ( matchFunction )
2021-04-19 14:06:07 +08:00
. Select ( createAttribute )
// arbitrary ordering to make timing points first.
// probably want to explicitly define order in the future.
. OrderByDescending ( c = > c . GetType ( ) . Name ) ;
2019-10-18 16:59:54 +08:00
}
2022-11-30 13:20:54 +08:00
private Drawable createAttribute ( ControlPoint controlPoint )
2019-10-27 14:19:36 +08:00
{
switch ( controlPoint )
{
case TimingControlPoint timing :
2021-04-19 14:06:07 +08:00
return new TimingRowAttribute ( timing ) ;
2019-10-27 14:19:36 +08:00
case DifficultyControlPoint difficulty :
2021-04-19 14:57:27 +08:00
return new DifficultyRowAttribute ( difficulty ) ;
2019-10-27 14:19:36 +08:00
case EffectControlPoint effect :
2021-04-19 15:28:18 +08:00
return new EffectRowAttribute ( effect ) ;
2019-10-27 14:19:36 +08:00
case SampleControlPoint sample :
2021-04-19 15:10:37 +08:00
return new SampleRowAttribute ( sample ) ;
2022-11-27 10:45:56 +08:00
}
2022-11-30 13:20:54 +08:00
throw new ArgumentOutOfRangeException ( nameof ( controlPoint ) , $"Control point type {controlPoint.GetType()} is not supported" ) ;
2019-10-27 14:19:36 +08:00
}
2019-10-18 16:59:54 +08:00
}
}
}