2023-01-08 06:40:02 +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.
2023-01-08 08:47:22 +08:00
using System ;
2023-01-08 06:40:02 +08:00
using System.Collections.Generic ;
using System.Threading ;
using osuTK ;
using osu.Framework.Allocation ;
using osu.Framework.Bindables ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2023-01-08 22:42:42 +08:00
using osu.Framework.Graphics.Effects ;
2023-01-08 06:40:02 +08:00
using osu.Game.Beatmaps ;
using osu.Game.Beatmaps.Drawables ;
using osu.Game.Graphics ;
using osu.Game.Graphics.Sprites ;
using osu.Framework.Graphics.Shapes ;
using osu.Framework.Localisation ;
using osu.Game.Configuration ;
2023-01-08 08:47:22 +08:00
using osu.Game.Graphics.UserInterface ;
2023-01-08 06:40:02 +08:00
using osu.Game.Rulesets ;
using osu.Game.Rulesets.Mods ;
namespace osu.Game.Screens.Select
{
public partial class BeatmapInfoWedgeV2 : VisibilityContainer
{
2023-02-21 23:07:26 +08:00
public const float WEDGE_HEIGHT = 120 ;
2023-01-08 08:47:22 +08:00
private const float shear_width = 21 ;
2023-01-08 06:40:02 +08:00
private const float transition_duration = 250 ;
2023-01-08 22:42:42 +08:00
private const float corner_radius = 10 ;
2023-01-17 05:46:18 +08:00
private const float colour_bar_width = 30 ;
2023-01-08 06:40:02 +08:00
2023-01-11 00:34:47 +08:00
/// Todo: move this const out to song select when more new design elements are implemented for the beatmap details area, since it applies to text alignment of various elements
private const float text_margin = 62 ;
2023-02-21 23:07:26 +08:00
private static readonly Vector2 wedged_container_shear = new Vector2 ( shear_width / WEDGE_HEIGHT , 0 ) ;
2023-01-08 06:40:02 +08:00
[Resolved]
2023-01-08 19:56:32 +08:00
private IBindable < RulesetInfo > ruleset { get ; set ; } = null ! ;
2023-01-08 06:53:19 +08:00
2023-01-18 20:55:52 +08:00
[Resolved]
private OsuColour colours { get ; set ; } = null ! ;
2023-01-08 19:56:32 +08:00
protected Container ? DisplayedContent { get ; private set ; }
2023-01-08 06:40:02 +08:00
2023-01-08 19:56:32 +08:00
protected WedgeInfoText ? Info { get ; private set ; }
2023-01-08 06:53:19 +08:00
2023-09-05 03:56:20 +08:00
private Container difficultyColourBar = null ! ;
private StarCounter starCounter = null ! ;
private BufferedContainer bufferedContent = null ! ;
2023-01-08 08:24:47 +08:00
2023-01-08 06:40:02 +08:00
public BeatmapInfoWedgeV2 ( )
{
2023-02-21 23:07:26 +08:00
Height = WEDGE_HEIGHT ;
2023-01-08 06:40:02 +08:00
Shear = wedged_container_shear ;
Masking = true ;
2023-09-05 03:42:01 +08:00
Margin = new MarginPadding { Left = - corner_radius } ;
2023-01-08 22:42:42 +08:00
EdgeEffect = new EdgeEffectParameters
{
2023-03-16 15:51:57 +08:00
Colour = Colour4 . Black . Opacity ( 0.2f ) ,
2023-01-08 22:42:42 +08:00
Type = EdgeEffectType . Shadow ,
2023-03-16 15:51:57 +08:00
Radius = 3 ,
2023-01-08 22:42:42 +08:00
} ;
CornerRadius = corner_radius ;
2023-09-05 03:56:20 +08:00
}
2023-01-08 08:47:22 +08:00
2023-09-05 03:56:20 +08:00
[BackgroundDependencyLoader]
private void load ( )
{
2023-01-24 00:00:46 +08:00
// We want to buffer the wedge to avoid weird transparency overlaps between the colour bar and the background.
2023-02-04 02:53:04 +08:00
Child = bufferedContent = new BufferedContainer ( pixelSnapping : true )
2023-01-08 06:40:02 +08:00
{
2023-01-24 00:00:46 +08:00
RelativeSizeAxes = Axes . Both ,
Children = new Drawable [ ]
2023-01-08 08:47:22 +08:00
{
2023-01-24 00:00:46 +08:00
// These elements can't be grouped with the rest of the content, due to being present either outside or under the backgrounds area
difficultyColourBar = new Container
{
Colour = Colour4 . Transparent ,
Depth = float . MaxValue ,
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
RelativeSizeAxes = Axes . Y ,
// By limiting the width we avoid this box showing up as an outline around the drawables that are on top of it.
Width = colour_bar_width + corner_radius ,
Child = new Box { RelativeSizeAxes = Axes . Both }
} ,
2023-02-04 02:52:01 +08:00
new Container
2023-01-24 00:00:46 +08:00
{
2023-02-04 02:52:01 +08:00
// Applying the shear to this container and nesting the starCounter inside avoids
// the deformation that occurs if the shear is applied to the starCounter whilst rotated
2023-01-24 00:00:46 +08:00
Shear = - wedged_container_shear ,
X = - colour_bar_width / 2 ,
2023-02-04 02:52:01 +08:00
Anchor = Anchor . CentreRight ,
Origin = Anchor . Centre ,
RelativeSizeAxes = Axes . Y ,
Width = colour_bar_width ,
Child = starCounter = new StarCounter
{
2023-02-21 23:07:26 +08:00
Rotation = ( float ) ( Math . Atan ( shear_width / WEDGE_HEIGHT ) * ( 180 / Math . PI ) ) ,
2023-02-04 02:52:01 +08:00
Colour = Colour4 . Transparent ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Scale = new Vector2 ( 0.35f ) ,
Direction = FillDirection . Vertical
}
2023-01-24 00:00:46 +08:00
}
2023-01-08 08:47:22 +08:00
}
2023-01-08 06:40:02 +08:00
} ;
}
2023-09-05 03:56:20 +08:00
protected override void LoadComplete ( )
2023-01-08 06:40:02 +08:00
{
2023-09-05 03:56:20 +08:00
base . LoadComplete ( ) ;
2023-01-08 06:40:02 +08:00
ruleset . BindValueChanged ( _ = > updateDisplay ( ) ) ;
}
2023-01-30 20:13:57 +08:00
private const double animation_duration = 600 ;
2023-01-08 06:40:02 +08:00
protected override void PopIn ( )
{
this . MoveToX ( 0 , animation_duration , Easing . OutQuint ) ;
2023-01-30 20:13:57 +08:00
this . FadeIn ( 200 , Easing . In ) ;
2023-01-08 06:40:02 +08:00
}
protected override void PopOut ( )
{
2023-01-30 20:13:57 +08:00
this . MoveToX ( - 150 , animation_duration , Easing . OutQuint ) ;
this . FadeOut ( 200 , Easing . OutQuint ) ;
2023-01-08 06:40:02 +08:00
}
2023-01-08 19:56:32 +08:00
private WorkingBeatmap ? beatmap ;
2023-01-08 06:40:02 +08:00
2023-01-08 19:56:32 +08:00
public WorkingBeatmap ? Beatmap
2023-01-08 06:40:02 +08:00
{
get = > beatmap ;
set
{
if ( beatmap = = value ) return ;
beatmap = value ;
updateDisplay ( ) ;
}
}
2023-01-08 19:56:32 +08:00
private Container ? loadingInfo ;
2023-01-08 06:53:19 +08:00
2023-01-08 06:40:02 +08:00
private void updateDisplay ( )
{
2023-01-30 23:13:55 +08:00
Scheduler . AddOnce ( ( ) = >
2023-01-08 06:40:02 +08:00
{
if ( beatmap = = null )
{
removeOldInfo ( ) ;
return ;
}
LoadComponentAsync ( loadingInfo = new Container
{
2023-09-05 03:38:40 +08:00
Padding = new MarginPadding { Right = colour_bar_width } ,
2023-01-08 06:40:02 +08:00
RelativeSizeAxes = Axes . Both ,
Depth = DisplayedContent ? . Depth + 1 ? ? 0 ,
2023-09-05 03:38:40 +08:00
Child = new Container
2023-01-08 06:40:02 +08:00
{
2023-09-05 03:38:40 +08:00
Masking = true ,
CornerRadius = corner_radius ,
RelativeSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
// TODO: New wedge design uses a coloured horizontal gradient for its background, however this lacks implementation information in the figma draft.
// pending https://www.figma.com/file/DXKwqZhD5yyb1igc3mKo1P?node-id=2980:3361#340801912 being answered.
new BeatmapInfoWedgeBackground ( beatmap ) { Shear = - Shear } ,
Info = new WedgeInfoText ( beatmap ) { Shear = - Shear }
}
2023-01-08 06:40:02 +08:00
}
} , loaded = >
{
2023-01-11 00:34:47 +08:00
// Ensure we are the most recent loaded wedge.
2023-01-08 06:40:02 +08:00
if ( loaded ! = loadingInfo ) return ;
removeOldInfo ( ) ;
2023-01-24 00:00:46 +08:00
bufferedContent . Add ( DisplayedContent = loaded ) ;
2023-01-18 20:55:52 +08:00
2023-01-24 00:00:46 +08:00
Info . DisplayedStars . BindValueChanged ( s = >
2023-01-18 20:55:52 +08:00
{
starCounter . Current = ( float ) s . NewValue ;
starCounter . Colour = s . NewValue > = 6.5 ? colours . Orange1 : Colour4 . Black . Opacity ( 0.75f ) ;
difficultyColourBar . FadeColour ( colours . ForStarDifficulty ( s . NewValue ) ) ;
} , true ) ;
2023-01-08 06:40:02 +08:00
} ) ;
2023-01-30 23:13:55 +08:00
} ) ;
2023-01-30 23:12:26 +08:00
void removeOldInfo ( )
{
State . Value = beatmap = = null ? Visibility . Hidden : Visibility . Visible ;
DisplayedContent ? . FadeOut ( transition_duration ) ;
DisplayedContent ? . Expire ( ) ;
DisplayedContent = null ;
}
2023-01-08 06:40:02 +08:00
}
public partial class WedgeInfoText : Container
{
2023-01-08 19:56:32 +08:00
public OsuSpriteText TitleLabel { get ; private set ; } = null ! ;
public OsuSpriteText ArtistLabel { get ; private set ; } = null ! ;
2023-01-24 00:00:46 +08:00
private StarRatingDisplay starRatingDisplay = null ! ;
2023-01-08 06:40:02 +08:00
2023-01-08 19:56:32 +08:00
private ILocalisedBindableString titleBinding = null ! ;
private ILocalisedBindableString artistBinding = null ! ;
2023-01-08 06:40:02 +08:00
2023-01-08 22:42:42 +08:00
private readonly WorkingBeatmap working ;
2023-01-08 06:40:02 +08:00
2023-01-24 00:00:46 +08:00
public IBindable < double > DisplayedStars = > starRatingDisplay . DisplayedStars ;
2023-01-08 06:40:02 +08:00
[Resolved]
2023-01-08 22:42:42 +08:00
private IBindable < IReadOnlyList < Mod > > mods { get ; set ; } = null ! ;
2023-01-08 06:40:02 +08:00
[Resolved]
2023-01-08 19:56:32 +08:00
private BeatmapDifficultyCache difficultyCache { get ; set ; } = null ! ;
private ModSettingChangeTracker ? settingChangeTracker ;
2023-01-08 06:40:02 +08:00
2023-01-08 19:56:32 +08:00
private IBindable < StarDifficulty ? > ? starDifficulty ;
private CancellationTokenSource ? cancellationSource ;
2023-01-08 06:40:02 +08:00
2023-01-08 22:42:42 +08:00
public WedgeInfoText ( WorkingBeatmap working )
{
this . working = working ;
2023-09-05 03:56:20 +08:00
RelativeSizeAxes = Axes . Both ;
2023-01-08 22:42:42 +08:00
}
2023-01-08 06:40:02 +08:00
[BackgroundDependencyLoader]
private void load ( LocalisationManager localisation )
{
2023-01-08 22:42:42 +08:00
var beatmapInfo = working . BeatmapInfo ;
var metadata = working . Metadata ;
2023-01-08 06:40:02 +08:00
titleBinding = localisation . GetLocalisedBindableString ( new RomanisableString ( metadata . TitleUnicode , metadata . Title ) ) ;
artistBinding = localisation . GetLocalisedBindableString ( new RomanisableString ( metadata . ArtistUnicode , metadata . Artist ) ) ;
Children = new Drawable [ ]
{
new FillFlowContainer
{
Name = "Topright-aligned metadata" ,
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
Direction = FillDirection . Vertical ,
2023-01-11 01:03:28 +08:00
Padding = new MarginPadding { Top = 3 , Right = 8 } ,
2023-01-08 06:40:02 +08:00
AutoSizeAxes = Axes . Both ,
Shear = wedged_container_shear ,
Spacing = new Vector2 ( 0f , 5f ) ,
Children = new Drawable [ ]
{
2023-01-24 00:00:46 +08:00
starRatingDisplay = new StarRatingDisplay ( default , animated : true )
2023-01-08 06:40:02 +08:00
{
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
Shear = - wedged_container_shear ,
Alpha = 0f ,
} ,
new BeatmapSetOnlineStatusPill
{
AutoSizeAxes = Axes . Both ,
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
Shear = - wedged_container_shear ,
TextSize = 11 ,
TextPadding = new MarginPadding { Horizontal = 8 , Vertical = 2 } ,
Status = beatmapInfo . Status ,
Alpha = string . IsNullOrEmpty ( beatmapInfo . DifficultyName ) ? 0 : 1
}
}
} ,
new FillFlowContainer
{
2023-01-08 08:24:47 +08:00
Name = "Top-left aligned metadata" ,
2023-01-08 06:40:02 +08:00
Direction = FillDirection . Vertical ,
2023-01-17 05:46:18 +08:00
Padding = new MarginPadding { Horizontal = text_margin + shear_width , Top = 12 } ,
2023-01-08 06:40:02 +08:00
AutoSizeAxes = Axes . Y ,
RelativeSizeAxes = Axes . X ,
Children = new Drawable [ ]
{
2023-09-05 02:34:21 +08:00
TitleLabel = new TruncatingSpriteText
2023-01-08 06:40:02 +08:00
{
2023-01-08 19:03:02 +08:00
Shadow = true ,
2023-01-08 06:40:02 +08:00
Current = { BindTarget = titleBinding } ,
2023-01-08 08:24:47 +08:00
Font = OsuFont . TorusAlternate . With ( size : 40 , weight : FontWeight . SemiBold ) ,
2023-01-08 06:40:02 +08:00
RelativeSizeAxes = Axes . X ,
} ,
2023-09-05 02:34:21 +08:00
ArtistLabel = new TruncatingSpriteText
2023-01-08 06:40:02 +08:00
{
2023-01-11 00:34:47 +08:00
// TODO : figma design has a diffused shadow, instead of the solid one present here, not possible currently as far as i'm aware.
2023-01-08 19:03:02 +08:00
Shadow = true ,
2023-01-08 06:40:02 +08:00
Current = { BindTarget = artistBinding } ,
2023-01-11 00:34:47 +08:00
// Not sure if this should be semi bold or medium
2023-01-08 08:24:47 +08:00
Font = OsuFont . Torus . With ( size : 20 , weight : FontWeight . SemiBold ) ,
2023-01-08 06:40:02 +08:00
RelativeSizeAxes = Axes . X ,
}
}
}
} ;
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2023-01-08 22:42:42 +08:00
starDifficulty = difficultyCache . GetBindableDifficulty ( working . BeatmapInfo , ( cancellationSource = new CancellationTokenSource ( ) ) . Token ) ;
2023-01-08 19:56:32 +08:00
starDifficulty . BindValueChanged ( s = >
2023-01-08 06:40:02 +08:00
{
2023-01-24 00:00:46 +08:00
starRatingDisplay . Current . Value = s . NewValue ? ? default ;
2023-01-08 06:40:02 +08:00
// Don't roll the counter on initial display (but still allow it to roll on applying mods etc.)
2023-02-04 23:52:30 +08:00
if ( starRatingDisplay . Alpha > 0 )
2023-01-24 00:00:46 +08:00
starRatingDisplay . FinishTransforms ( true ) ;
2023-01-08 06:40:02 +08:00
2023-01-24 00:00:46 +08:00
starRatingDisplay . FadeIn ( transition_duration ) ;
2023-01-08 06:40:02 +08:00
} ) ;
mods . BindValueChanged ( m = >
{
settingChangeTracker ? . Dispose ( ) ;
settingChangeTracker = new ModSettingChangeTracker ( m . NewValue ) ;
} , true ) ;
}
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
2023-01-08 19:56:32 +08:00
cancellationSource ? . Cancel ( ) ;
2023-01-08 06:40:02 +08:00
settingChangeTracker ? . Dispose ( ) ;
}
}
}
}