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-04-13 17:19:50 +08:00
using System ;
2018-11-20 15:51:59 +08:00
using osuTK ;
using osuTK.Graphics ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Allocation ;
using osu.Framework.Extensions.Color4Extensions ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Colour ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Shapes ;
using osu.Framework.Graphics.Sprites ;
using osu.Framework.Graphics.Textures ;
using osu.Game.Graphics ;
using osu.Game.Graphics.Containers ;
using osu.Game.Graphics.Sprites ;
2018-05-22 21:29:52 +08:00
using osu.Game.Graphics.UserInterface ;
2018-06-28 18:23:56 +08:00
using osu.Game.Overlays.Profile.Components ;
2018-04-16 05:44:59 +08:00
using osu.Game.Overlays.Profile.Header ;
2018-04-13 17:19:50 +08:00
using osu.Game.Users ;
2019-02-28 13:55:45 +08:00
using Humanizer ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Overlays.Profile
{
public class ProfileHeader : Container
{
2018-04-20 12:01:23 +08:00
private readonly LinkFlowContainer infoTextLeft ;
2018-04-13 17:19:50 +08:00
private readonly LinkFlowContainer infoTextRight ;
private readonly FillFlowContainer < SpriteText > scoreText , scoreNumberText ;
private readonly RankGraph rankGraph ;
public readonly SupporterIcon SupporterTag ;
private readonly Container coverContainer ;
private readonly Sprite levelBadge ;
private readonly SpriteText levelText ;
private readonly GradeBadge gradeSSPlus , gradeSS , gradeSPlus , gradeS , gradeA ;
private readonly Box colourBar ;
private readonly DrawableFlag countryFlag ;
2018-04-16 05:44:59 +08:00
private readonly BadgeContainer badgeContainer ;
2018-04-13 17:19:50 +08:00
private const float cover_height = 350 ;
private const float info_height = 150 ;
private const float info_width = 220 ;
private const float avatar_size = 110 ;
private const float level_position = 30 ;
private const float level_height = 60 ;
2018-04-16 05:44:59 +08:00
private const float stats_width = 280 ;
2018-04-13 17:19:50 +08:00
public ProfileHeader ( User user )
{
RelativeSizeAxes = Axes . X ;
Height = cover_height + info_height ;
Children = new Drawable [ ]
{
coverContainer = new Container
{
RelativeSizeAxes = Axes . X ,
Height = cover_height ,
Masking = true ,
Children = new Drawable [ ]
{
new Box
{
RelativeSizeAxes = Axes . Both ,
Colour = ColourInfo . GradientVertical ( Color4 . Black . Opacity ( 0.1f ) , Color4 . Black . Opacity ( 0.75f ) )
} ,
new Container
{
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
2018-04-16 05:44:59 +08:00
Padding = new MarginPadding { Left = UserProfileOverlay . CONTENT_X_MARGIN , Bottom = 20 , Right = stats_width + UserProfileOverlay . CONTENT_X_MARGIN } ,
AutoSizeAxes = Axes . Y ,
RelativeSizeAxes = Axes . X ,
2018-04-13 17:19:50 +08:00
Children = new Drawable [ ]
{
new UpdateableAvatar
{
User = user ,
Size = new Vector2 ( avatar_size ) ,
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
Masking = true ,
CornerRadius = 5 ,
2018-12-20 19:08:22 +08:00
OpenOnClick = { Value = false } ,
2018-04-13 17:19:50 +08:00
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType . Shadow ,
Colour = Color4 . Black . Opacity ( 0.25f ) ,
Radius = 4 ,
} ,
} ,
new Container
{
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
X = avatar_size + 10 ,
AutoSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
SupporterTag = new SupporterIcon
{
Alpha = 0 ,
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
Y = - 75 ,
Size = new Vector2 ( 25 , 25 )
} ,
2018-05-22 21:29:52 +08:00
new FillFlowContainer
2018-04-13 17:19:50 +08:00
{
2018-05-22 21:29:52 +08:00
Direction = FillDirection . Horizontal ,
AutoSizeAxes = Axes . Both ,
2018-04-13 17:19:50 +08:00
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
Y = - 48 ,
2018-05-22 21:29:52 +08:00
Children = new Drawable [ ]
{
2018-12-22 13:40:55 +08:00
usernameText = new OsuSpriteText
2018-05-22 21:29:52 +08:00
{
Text = user . Username ,
2019-02-20 15:52:36 +08:00
Font = OsuFont . GetFont ( size : 30 , weight : FontWeight . Regular , italics : true )
2018-05-22 21:29:52 +08:00
} ,
new ExternalLinkButton ( $@"https://osu.ppy.sh/users/{user.Id}" )
{
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
Margin = new MarginPadding { Left = 3 , Bottom = 3 } , //To better lineup with the font
} ,
}
2018-04-13 17:19:50 +08:00
} ,
countryFlag = new DrawableFlag ( user . Country )
{
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
Width = 30 ,
Height = 20
}
}
2018-04-16 05:44:59 +08:00
} ,
badgeContainer = new BadgeContainer
{
2018-04-17 01:22:11 +08:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
2018-04-16 05:44:59 +08:00
Origin = Anchor . BottomLeft ,
Margin = new MarginPadding { Bottom = 5 } ,
Alpha = 0 ,
} ,
2018-04-13 17:19:50 +08:00
}
} ,
colourBar = new Box
{
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
X = UserProfileOverlay . CONTENT_X_MARGIN ,
Height = 5 ,
Width = info_width ,
Alpha = 0
}
}
} ,
2018-07-10 12:06:03 +08:00
new Box // this is a temporary workaround for incorrect masking behaviour of FillMode.Fill used in UserCoverBackground (see https://github.com/ppy/osu-framework/issues/1675)
{
RelativeSizeAxes = Axes . X ,
Height = 1 ,
Y = cover_height ,
Colour = OsuColour . Gray ( 34 ) ,
} ,
2019-02-20 18:32:30 +08:00
infoTextLeft = new LinkFlowContainer ( t = > t . Font = t . Font . With ( size : 14 ) )
2018-04-13 17:19:50 +08:00
{
X = UserProfileOverlay . CONTENT_X_MARGIN ,
Y = cover_height + 20 ,
Width = info_width ,
AutoSizeAxes = Axes . Y ,
ParagraphSpacing = 0.8f ,
LineSpacing = 0.2f
} ,
2019-02-20 15:52:36 +08:00
infoTextRight = new LinkFlowContainer ( t = > t . Font = OsuFont . GetFont ( size : 14 , weight : FontWeight . Regular , italics : true ) )
2018-04-13 17:19:50 +08:00
{
X = UserProfileOverlay . CONTENT_X_MARGIN + info_width + 20 ,
Y = cover_height + 20 ,
Width = info_width ,
AutoSizeAxes = Axes . Y ,
ParagraphSpacing = 0.8f ,
LineSpacing = 0.2f
} ,
new Container
{
X = - UserProfileOverlay . CONTENT_X_MARGIN ,
RelativeSizeAxes = Axes . Y ,
2018-04-16 05:44:59 +08:00
Width = stats_width ,
2018-04-13 17:19:50 +08:00
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
Children = new Drawable [ ]
{
new Container
{
RelativeSizeAxes = Axes . X ,
Y = level_position ,
Height = level_height ,
Children = new Drawable [ ]
{
new Box
{
Colour = Color4 . Black . Opacity ( 0.5f ) ,
RelativeSizeAxes = Axes . Both
} ,
levelBadge = new Sprite
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Height = 50 ,
Width = 50 ,
Alpha = 0
} ,
levelText = new OsuSpriteText
{
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
Y = 11 ,
2019-02-12 12:04:46 +08:00
Font = OsuFont . GetFont ( size : 20 )
2018-04-13 17:19:50 +08:00
}
}
} ,
new Container
{
RelativeSizeAxes = Axes . X ,
Y = cover_height ,
Anchor = Anchor . TopCentre ,
Origin = Anchor . BottomCentre ,
Height = cover_height - level_height - level_position - 5 ,
Children = new Drawable [ ]
{
new Box
{
Colour = Color4 . Black . Opacity ( 0.5f ) ,
RelativeSizeAxes = Axes . Both
} ,
scoreText = new FillFlowContainer < SpriteText >
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Direction = FillDirection . Vertical ,
Padding = new MarginPadding { Horizontal = 20 , Vertical = 18 } ,
Spacing = new Vector2 ( 0 , 2 )
} ,
scoreNumberText = new FillFlowContainer < SpriteText >
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Direction = FillDirection . Vertical ,
Padding = new MarginPadding { Horizontal = 20 , Vertical = 18 } ,
Spacing = new Vector2 ( 0 , 2 )
} ,
new FillFlowContainer < GradeBadge >
{
AutoSizeAxes = Axes . Both ,
Direction = FillDirection . Horizontal ,
Anchor = Anchor . BottomCentre ,
Origin = Anchor . BottomCentre ,
Y = - 64 ,
Spacing = new Vector2 ( 20 , 0 ) ,
Children = new [ ]
{
gradeSSPlus = new GradeBadge ( "SSPlus" ) { Alpha = 0 } ,
gradeSS = new GradeBadge ( "SS" ) { Alpha = 0 } ,
}
} ,
new FillFlowContainer < GradeBadge >
{
AutoSizeAxes = Axes . Both ,
Direction = FillDirection . Horizontal ,
Anchor = Anchor . BottomCentre ,
Origin = Anchor . BottomCentre ,
Y = - 18 ,
Spacing = new Vector2 ( 20 , 0 ) ,
Children = new [ ]
{
gradeSPlus = new GradeBadge ( "SPlus" ) { Alpha = 0 } ,
gradeS = new GradeBadge ( "S" ) { Alpha = 0 } ,
gradeA = new GradeBadge ( "A" ) { Alpha = 0 } ,
}
}
}
} ,
new Container
{
RelativeSizeAxes = Axes . X ,
Anchor = Anchor . BottomCentre ,
Origin = Anchor . BottomCentre ,
Height = info_height - 15 ,
Children = new Drawable [ ]
{
new Box
{
Colour = Color4 . Black . Opacity ( 0.25f ) ,
RelativeSizeAxes = Axes . Both
} ,
rankGraph = new RankGraph
{
RelativeSizeAxes = Axes . Both
}
}
}
}
}
} ;
}
[BackgroundDependencyLoader]
2018-08-31 06:04:40 +08:00
private void load ( TextureStore textures )
2018-04-13 17:19:50 +08:00
{
2018-08-31 06:04:40 +08:00
levelBadge . Texture = textures . Get ( @"Profile/levelbadge" ) ;
2018-04-13 17:19:50 +08:00
}
2018-12-22 13:40:55 +08:00
private readonly OsuSpriteText usernameText ;
2018-04-13 17:19:50 +08:00
private User user ;
public User User
{
2019-02-28 12:58:19 +08:00
get = > user ;
2018-04-13 17:19:50 +08:00
set
{
user = value ;
loadUser ( ) ;
}
}
private void loadUser ( )
{
LoadComponentAsync ( new UserCoverBackground ( user )
{
RelativeSizeAxes = Axes . Both ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
FillMode = FillMode . Fill ,
OnLoadComplete = d = > d . FadeInFromZero ( 200 ) ,
Depth = float . MaxValue ,
} , coverContainer . Add ) ;
if ( user . IsSupporter )
SupporterTag . Show ( ) ;
2018-12-22 13:40:55 +08:00
usernameText . Text = user . Username ;
2018-04-13 17:19:50 +08:00
if ( ! string . IsNullOrEmpty ( user . Colour ) )
{
colourBar . Colour = OsuColour . FromHex ( user . Colour ) ;
colourBar . Show ( ) ;
}
2019-02-21 18:26:02 +08:00
void boldItalic ( SpriteText t ) = > t . Font = t . Font . With ( Typeface . Exo , weight : FontWeight . Bold , italics : true ) ;
2018-04-13 17:19:50 +08:00
void lightText ( SpriteText t ) = > t . Alpha = 0.8f ;
OsuSpriteText createScoreText ( string text ) = > new OsuSpriteText
{
2019-02-12 12:04:46 +08:00
Font = OsuFont . GetFont ( size : 14 ) ,
2018-04-13 17:19:50 +08:00
Text = text
} ;
OsuSpriteText createScoreNumberText ( string text ) = > new OsuSpriteText
{
2019-02-12 12:04:46 +08:00
Font = OsuFont . GetFont ( size : 14 , weight : FontWeight . Bold ) ,
2018-04-13 17:19:50 +08:00
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
Text = text
} ;
if ( user . Country ! = null )
{
2018-04-19 20:46:42 +08:00
infoTextLeft . AddText ( "From " , lightText ) ;
2018-04-13 17:19:50 +08:00
infoTextLeft . AddText ( user . Country . FullName , boldItalic ) ;
countryFlag . Country = user . Country ;
}
infoTextLeft . NewParagraph ( ) ;
2018-06-15 21:00:41 +08:00
if ( user . JoinDate . ToUniversalTime ( ) . Year < 2008 )
{
infoTextLeft . AddText ( new DrawableJoinDate ( user . JoinDate ) , lightText ) ;
}
else
2018-04-13 17:19:50 +08:00
{
infoTextLeft . AddText ( "Joined " , lightText ) ;
2018-06-15 21:00:41 +08:00
infoTextLeft . AddText ( new DrawableJoinDate ( user . JoinDate ) , boldItalic ) ;
2018-04-13 17:19:50 +08:00
}
2018-10-04 23:25:40 +08:00
if ( user . LastVisit . HasValue )
{
infoTextLeft . NewLine ( ) ;
infoTextLeft . AddText ( "Last seen " , lightText ) ;
infoTextLeft . AddText ( new DrawableDate ( user . LastVisit . Value ) , boldItalic ) ;
}
2018-04-13 17:19:50 +08:00
if ( user . PlayStyle ? . Length > 0 )
{
2018-12-31 05:40:17 +08:00
infoTextLeft . NewParagraph ( ) ;
2018-04-13 17:19:50 +08:00
infoTextLeft . AddText ( "Plays with " , lightText ) ;
infoTextLeft . AddText ( string . Join ( ", " , user . PlayStyle ) , boldItalic ) ;
}
2018-04-19 20:46:42 +08:00
infoTextLeft . NewLine ( ) ;
infoTextLeft . AddText ( "Contributed " , lightText ) ;
2019-02-28 13:55:45 +08:00
infoTextLeft . AddLink ( "forum post" . ToQuantity ( user . PostCount ) , url : $"https://osu.ppy.sh/users/{user.Id}/posts" , creationParameters : boldItalic ) ;
2018-04-19 20:46:42 +08:00
2018-04-13 17:19:50 +08:00
string websiteWithoutProtcol = user . Website ;
if ( ! string . IsNullOrEmpty ( websiteWithoutProtcol ) )
{
int protocolIndex = websiteWithoutProtcol . IndexOf ( "//" , StringComparison . Ordinal ) ;
if ( protocolIndex > = 0 )
websiteWithoutProtcol = websiteWithoutProtcol . Substring ( protocolIndex + 2 ) ;
}
tryAddInfoRightLine ( FontAwesome . fa_map_marker , user . Location ) ;
tryAddInfoRightLine ( FontAwesome . fa_heart_o , user . Interests ) ;
tryAddInfoRightLine ( FontAwesome . fa_suitcase , user . Occupation ) ;
infoTextRight . NewParagraph ( ) ;
if ( ! string . IsNullOrEmpty ( user . Twitter ) )
tryAddInfoRightLine ( FontAwesome . fa_twitter , "@" + user . Twitter , $@"https://twitter.com/{user.Twitter}" ) ;
2018-04-20 12:51:30 +08:00
tryAddInfoRightLine ( FontAwesome . fa_gamepad , user . Discord ) ;
2018-04-13 17:19:50 +08:00
tryAddInfoRightLine ( FontAwesome . fa_skype , user . Skype , @"skype:" + user . Skype + @"?chat" ) ;
2018-04-19 20:46:42 +08:00
tryAddInfoRightLine ( FontAwesome . fa_lastfm , user . Lastfm , $@"https://last.fm/users/{user.Lastfm}" ) ;
tryAddInfoRightLine ( FontAwesome . fa_globe , websiteWithoutProtcol , user . Website ) ;
2018-04-13 17:19:50 +08:00
if ( user . Statistics ! = null )
{
levelBadge . Show ( ) ;
levelText . Text = user . Statistics . Level . Current . ToString ( ) ;
scoreText . Add ( createScoreText ( "Ranked Score" ) ) ;
scoreNumberText . Add ( createScoreNumberText ( user . Statistics . RankedScore . ToString ( @"#,0" ) ) ) ;
scoreText . Add ( createScoreText ( "Accuracy" ) ) ;
scoreNumberText . Add ( createScoreNumberText ( $"{user.Statistics.Accuracy:0.##}%" ) ) ;
scoreText . Add ( createScoreText ( "Play Count" ) ) ;
scoreNumberText . Add ( createScoreNumberText ( user . Statistics . PlayCount . ToString ( @"#,0" ) ) ) ;
scoreText . Add ( createScoreText ( "Total Score" ) ) ;
scoreNumberText . Add ( createScoreNumberText ( user . Statistics . TotalScore . ToString ( @"#,0" ) ) ) ;
scoreText . Add ( createScoreText ( "Total Hits" ) ) ;
scoreNumberText . Add ( createScoreNumberText ( user . Statistics . TotalHits . ToString ( @"#,0" ) ) ) ;
scoreText . Add ( createScoreText ( "Max Combo" ) ) ;
scoreNumberText . Add ( createScoreNumberText ( user . Statistics . MaxCombo . ToString ( @"#,0" ) ) ) ;
scoreText . Add ( createScoreText ( "Replays Watched by Others" ) ) ;
scoreNumberText . Add ( createScoreNumberText ( user . Statistics . ReplaysWatched . ToString ( @"#,0" ) ) ) ;
gradeSSPlus . DisplayCount = user . Statistics . GradesCount . SSPlus ;
gradeSSPlus . Show ( ) ;
gradeSS . DisplayCount = user . Statistics . GradesCount . SS ;
gradeSS . Show ( ) ;
gradeSPlus . DisplayCount = user . Statistics . GradesCount . SPlus ;
gradeSPlus . Show ( ) ;
gradeS . DisplayCount = user . Statistics . GradesCount . S ;
gradeS . Show ( ) ;
gradeA . DisplayCount = user . Statistics . GradesCount . A ;
gradeA . Show ( ) ;
rankGraph . User . Value = user ;
}
2018-04-16 05:44:59 +08:00
badgeContainer . ShowBadges ( user . Badges ) ;
2018-04-13 17:19:50 +08:00
}
private void tryAddInfoRightLine ( FontAwesome icon , string str , string url = null )
{
if ( string . IsNullOrEmpty ( str ) ) return ;
infoTextRight . AddIcon ( icon ) ;
if ( url ! = null )
{
infoTextRight . AddLink ( " " + str , url ) ;
}
else
{
infoTextRight . AddText ( " " + str ) ;
}
infoTextRight . NewLine ( ) ;
}
}
}