2020-06-15 21:45:18 +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.
2020-06-22 17:38:41 +08:00
using System ;
2020-06-19 19:31:52 +08:00
using System.Linq ;
2020-06-19 18:12:55 +08:00
using System.Threading ;
2020-06-22 17:38:41 +08:00
using System.Threading.Tasks ;
2020-06-18 21:21:30 +08:00
using osu.Framework.Allocation ;
using osu.Framework.Bindables ;
2020-06-15 21:45:18 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2020-06-23 14:21:23 +08:00
using osu.Framework.Input.Events ;
2020-06-22 17:38:41 +08:00
using osu.Game.Beatmaps ;
2020-06-19 18:12:55 +08:00
using osu.Game.Graphics.UserInterface ;
2020-06-18 21:21:30 +08:00
using osu.Game.Online.Placeholders ;
2020-06-22 17:38:41 +08:00
using osu.Game.Rulesets.Mods ;
2020-06-15 21:45:18 +08:00
using osu.Game.Scoring ;
using osuTK ;
namespace osu.Game.Screens.Ranking.Statistics
{
2020-06-18 15:50:45 +08:00
public class StatisticsPanel : VisibilityContainer
2020-06-15 21:45:18 +08:00
{
2020-06-18 16:06:05 +08:00
public const float SIDE_PADDING = 30 ;
2020-06-18 21:21:30 +08:00
public readonly Bindable < ScoreInfo > Score = new Bindable < ScoreInfo > ( ) ;
2020-06-18 15:50:45 +08:00
2020-06-18 21:21:30 +08:00
protected override bool StartHidden = > true ;
2020-06-15 21:45:18 +08:00
2020-06-22 17:38:41 +08:00
[Resolved]
private BeatmapManager beatmapManager { get ; set ; }
2020-06-18 21:21:30 +08:00
private readonly Container content ;
2020-06-19 18:12:55 +08:00
private readonly LoadingSpinner spinner ;
2020-06-15 21:45:18 +08:00
2020-06-18 21:21:30 +08:00
public StatisticsPanel ( )
{
2020-06-18 15:50:45 +08:00
InternalChild = new Container
2020-06-15 21:45:18 +08:00
{
2020-06-18 15:50:45 +08:00
RelativeSizeAxes = Axes . Both ,
Padding = new MarginPadding
2020-06-15 21:45:18 +08:00
{
2020-06-18 16:06:05 +08:00
Left = ScorePanel . EXPANDED_WIDTH + SIDE_PADDING * 3 ,
Right = SIDE_PADDING ,
Top = SIDE_PADDING ,
Bottom = 50 // Approximate padding to the bottom of the score panel.
2020-06-15 21:45:18 +08:00
} ,
2020-06-19 18:12:55 +08:00
Children = new Drawable [ ]
{
content = new Container { RelativeSizeAxes = Axes . Both } ,
spinner = new LoadingSpinner ( )
}
2020-06-18 21:21:30 +08:00
} ;
}
[BackgroundDependencyLoader]
private void load ( )
{
Score . BindValueChanged ( populateStatistics , true ) ;
}
2020-06-19 18:12:55 +08:00
private CancellationTokenSource loadCancellation ;
2020-06-18 21:21:30 +08:00
private void populateStatistics ( ValueChangedEvent < ScoreInfo > score )
{
2020-06-19 18:12:55 +08:00
loadCancellation ? . Cancel ( ) ;
2020-06-22 19:20:42 +08:00
loadCancellation = null ;
2020-06-19 18:12:55 +08:00
2020-06-18 21:21:30 +08:00
foreach ( var child in content )
child . FadeOut ( 150 ) . Expire ( ) ;
2021-11-08 11:35:30 +08:00
spinner . Hide ( ) ;
2020-06-18 21:21:30 +08:00
var newScore = score . NewValue ;
2020-06-19 21:47:55 +08:00
if ( newScore = = null )
return ;
2020-06-18 21:21:30 +08:00
if ( newScore . HitEvents = = null | | newScore . HitEvents . Count = = 0 )
2020-09-24 11:39:08 +08:00
{
2020-09-24 11:21:08 +08:00
content . Add ( new FillFlowContainer
{
RelativeSizeAxes = Axes . Both ,
Direction = FillDirection . Vertical ,
Children = new Drawable [ ]
{
new MessagePlaceholder ( "Extended statistics are only available after watching a replay!" ) ,
new ReplayDownloadButton ( newScore )
{
Scale = new Vector2 ( 1.5f ) ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
} ,
}
} ) ;
2020-09-24 11:39:08 +08:00
}
2020-06-18 21:21:30 +08:00
else
{
2020-06-19 18:12:55 +08:00
spinner . Show ( ) ;
2020-06-22 17:38:41 +08:00
var localCancellationSource = loadCancellation = new CancellationTokenSource ( ) ;
IBeatmap playableBeatmap = null ;
2020-06-15 21:45:18 +08:00
2020-06-22 17:38:41 +08:00
// Todo: The placement of this is temporary. Eventually we'll both generate the playable beatmap _and_ run through it in a background task to generate the hit events.
Task . Run ( ( ) = >
{
2021-10-04 16:35:53 +08:00
playableBeatmap = beatmapManager . GetWorkingBeatmap ( newScore . BeatmapInfo ) . GetPlayableBeatmap ( newScore . Ruleset , newScore . Mods ? ? Array . Empty < Mod > ( ) ) ;
2020-06-22 22:22:49 +08:00
} , loadCancellation . Token ) . ContinueWith ( t = > Schedule ( ( ) = >
2020-06-16 16:49:28 +08:00
{
2020-06-22 17:38:41 +08:00
var rows = new FillFlowContainer
2020-06-18 21:21:30 +08:00
{
2020-09-24 11:21:46 +08:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2020-06-22 17:38:41 +08:00
Direction = FillDirection . Vertical ,
Spacing = new Vector2 ( 30 , 15 ) ,
2020-08-27 04:34:02 +08:00
Alpha = 0
2020-06-22 17:38:41 +08:00
} ;
2020-06-18 21:21:30 +08:00
2020-08-28 02:07:30 +08:00
foreach ( var row in newScore . Ruleset . CreateInstance ( ) . CreateStatisticsForScore ( newScore , playableBeatmap ) )
{
rows . Add ( new GridContainer
{
2020-08-28 02:51:28 +08:00
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
2020-08-28 02:07:30 +08:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Content = new [ ]
{
row . Columns ? . Select ( c = > new StatisticContainer ( c )
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
} ) . Cast < Drawable > ( ) . ToArray ( )
} ,
ColumnDimensions = Enumerable . Range ( 0 , row . Columns ? . Length ? ? 0 )
. Select ( i = > row . Columns [ i ] . Dimension ? ? new Dimension ( ) ) . ToArray ( ) ,
RowDimensions = new [ ] { new Dimension ( GridSizeMode . AutoSize ) }
} ) ;
}
2020-06-22 17:38:41 +08:00
LoadComponentAsync ( rows , d = >
{
if ( Score . Value ! = newScore )
return ;
2020-06-19 18:12:55 +08:00
2020-06-22 17:38:41 +08:00
spinner . Hide ( ) ;
content . Add ( d ) ;
2020-08-27 04:34:02 +08:00
d . FadeIn ( 250 , Easing . OutQuint ) ;
2020-06-22 17:38:41 +08:00
} , localCancellationSource . Token ) ;
2020-06-22 22:22:49 +08:00
} ) , localCancellationSource . Token ) ;
2020-06-16 16:49:28 +08:00
}
2020-06-15 21:45:18 +08:00
}
2020-06-18 15:50:45 +08:00
2020-06-23 14:21:23 +08:00
protected override bool OnClick ( ClickEvent e )
{
ToggleVisibility ( ) ;
return true ;
}
2020-06-19 20:41:48 +08:00
protected override void PopIn ( ) = > this . FadeIn ( 150 , Easing . OutQuint ) ;
2020-06-18 15:50:45 +08:00
2020-06-19 20:41:48 +08:00
protected override void PopOut ( ) = > this . FadeOut ( 150 , Easing . OutQuint ) ;
2020-06-22 19:20:42 +08:00
protected override void Dispose ( bool isDisposing )
{
loadCancellation ? . Cancel ( ) ;
base . Dispose ( isDisposing ) ;
}
2020-06-15 21:45:18 +08:00
}
}