1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 09:22:54 +08:00

Merge pull request #936 from huoyaoyuan/profile

Basic User Profile Page
This commit is contained in:
Dean Herbert 2017-07-17 14:10:52 +09:00 committed by GitHub
commit 406cf9d0d4
30 changed files with 1571 additions and 107 deletions

View File

@ -0,0 +1,64 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework.Testing;
using osu.Game.Overlays;
using osu.Game.Users;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseUserProfile : TestCase
{
public override string Description => "Tests user's profile page.";
public TestCaseUserProfile()
{
var profile = new UserProfileOverlay();
Add(profile);
AddStep("Show offline dummy", () => profile.ShowUser(new User
{
Username = @"Somebody",
Id = 1,
Country = new Country { FullName = @"Alien" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
JoinDate = DateTimeOffset.Now.AddDays(-1),
LastVisit = DateTimeOffset.Now,
Age = 1,
ProfileOrder = new[] { "me" },
CountryRank = 1,
Statistics = new UserStatistics
{
Rank = 2148,
PP = 4567.89m
},
AllRankHistories = new User.RankHistories
{
Osu = new User.RankHistory
{
Mode = @"osu",
Data = Enumerable.Range(2345,45).Concat(Enumerable.Range(2109,40)).ToArray()
}
}
}, false));
AddStep("Show ppy", () => profile.ShowUser(new User
{
Username = @"peppy",
Id = 2,
Country = new Country { FullName = @"Australia", FlagName = @"AU" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
}));
AddStep("Show flyte", () => profile.ShowUser(new User
{
Username = @"flyte",
Id = 3103765,
Country = new Country { FullName = @"Japan", FlagName = @"JP" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
}));
AddStep("Hide", profile.Hide);
AddStep("Show without reload", profile.Show);
}
}
}

View File

@ -216,6 +216,7 @@
<Compile Include="Tests\TestCaseTextAwesome.cs" />
<Compile Include="Tests\TestCasePlaySongSelect.cs" />
<Compile Include="Tests\TestCaseTwoLayerButton.cs" />
<Compile Include="Tests\TestCaseUserProfile.cs" />
<Compile Include="VisualTestGame.cs" />
<Compile Include="Platform\TestStorage.cs" />
<Compile Include="Tests\TestCaseSettings.cs" />

View File

@ -0,0 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.Containers
{
public class OsuTextFlowContainer : TextFlowContainer
{
public OsuTextFlowContainer(Action<SpriteText> defaultCreationParameters = null) : base(defaultCreationParameters)
{
}
protected override SpriteText CreateSpriteText() => new OsuSpriteText();
public void AddIcon(FontAwesome icon, Action<SpriteText> creationParameters = null) => AddText(((char)icon).ToString(), creationParameters);
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
@ -13,11 +12,15 @@ namespace osu.Game.Graphics.Containers
/// <summary>
/// A container that can scroll to each section inside it.
/// </summary>
public class SectionsContainer : Container
public class SectionsContainer<T> : Container<T>
where T : Drawable
{
private Drawable expandableHeader, fixedHeader, footer;
public readonly ScrollContainer ScrollContainer;
private readonly Container<Drawable> sectionsContainer;
private Drawable expandableHeader, fixedHeader, footer, headerBackground;
private readonly ScrollContainer scrollContainer;
private readonly Container headerBackgroundContainer;
private readonly FlowContainer<T> scrollContentContainer;
protected override Container<T> Content => scrollContentContainer;
public Drawable ExpandableHeader
{
@ -26,12 +29,11 @@ namespace osu.Game.Graphics.Containers
{
if (value == expandableHeader) return;
if (expandableHeader != null)
Remove(expandableHeader);
expandableHeader?.Expire();
expandableHeader = value;
if (value == null) return;
Add(expandableHeader);
AddInternal(expandableHeader);
lastKnownScroll = float.NaN;
}
}
@ -43,12 +45,11 @@ namespace osu.Game.Graphics.Containers
{
if (value == fixedHeader) return;
if (fixedHeader != null)
Remove(fixedHeader);
fixedHeader?.Expire();
fixedHeader = value;
if (value == null) return;
Add(fixedHeader);
AddInternal(fixedHeader);
lastKnownScroll = float.NaN;
}
}
@ -61,69 +62,84 @@ namespace osu.Game.Graphics.Containers
if (value == footer) return;
if (footer != null)
ScrollContainer.Remove(footer);
scrollContainer.Remove(footer);
footer = value;
if (value == null) return;
footer.Anchor |= Anchor.y2;
footer.Origin |= Anchor.y2;
ScrollContainer.Add(footer);
scrollContainer.Add(footer);
lastKnownScroll = float.NaN;
}
}
public Bindable<Drawable> SelectedSection { get; } = new Bindable<Drawable>();
protected virtual Container<Drawable> CreateScrollContentContainer()
=> new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both
};
private List<Drawable> sections = new List<Drawable>();
public IEnumerable<Drawable> Sections
public Drawable HeaderBackground
{
get { return sections; }
get { return headerBackground; }
set
{
foreach (var section in sections)
sectionsContainer.Remove(section);
if (value == headerBackground) return;
sections = value.ToList();
if (sections.Count == 0) return;
headerBackgroundContainer.Clear();
headerBackground = value;
if (value == null) return;
headerBackgroundContainer.Add(headerBackground);
sectionsContainer.AddRange(sections);
SelectedSection.Value = sections[0];
lastKnownScroll = float.NaN;
}
}
public Bindable<T> SelectedSection { get; } = new Bindable<T>();
protected virtual FlowContainer<T> CreateScrollContentContainer()
=> new FillFlowContainer<T>
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
};
public override void Add(T drawable)
{
base.Add(drawable);
lastKnownScroll = float.NaN;
headerHeight = float.NaN;
footerHeight = float.NaN;
}
private float headerHeight, footerHeight;
private readonly MarginPadding originalSectionsMargin;
private void updateSectionsMargin()
{
if (sections.Count == 0) return;
if (!Children.Any()) return;
var newMargin = originalSectionsMargin;
newMargin.Top += headerHeight;
newMargin.Bottom += footerHeight;
sectionsContainer.Margin = newMargin;
scrollContentContainer.Margin = newMargin;
}
public SectionsContainer()
{
Add(ScrollContainer = new OsuScrollContainer
AddInternal(scrollContainer = new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
Masking = false,
Children = new Drawable[] { sectionsContainer = CreateScrollContentContainer() }
Masking = true,
ScrollbarVisible = false,
Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() },
Depth = float.MaxValue
});
originalSectionsMargin = sectionsContainer.Margin;
AddInternal(headerBackgroundContainer = new Container
{
RelativeSizeAxes = Axes.X,
Depth = float.MaxValue / 2
});
originalSectionsMargin = scrollContentContainer.Margin;
}
public void ScrollTo(Drawable section) => ScrollContainer.ScrollTo(ScrollContainer.GetChildPosInContent(section) - FixedHeader.BoundingBox.Height);
public void ScrollTo(Drawable section) => scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - (FixedHeader?.BoundingBox.Height ?? 0));
private float lastKnownScroll;
protected override void UpdateAfterChildren()
@ -139,25 +155,30 @@ namespace osu.Game.Graphics.Containers
updateSectionsMargin();
}
float currentScroll = Math.Max(0, ScrollContainer.Current);
float currentScroll = scrollContainer.Current;
if (currentScroll != lastKnownScroll)
{
lastKnownScroll = currentScroll;
if (expandableHeader != null && fixedHeader != null)
if (ExpandableHeader != null && FixedHeader != null)
{
float offset = Math.Min(expandableHeader.LayoutSize.Y, currentScroll);
float offset = Math.Min(ExpandableHeader.LayoutSize.Y, currentScroll);
expandableHeader.Y = -offset;
fixedHeader.Y = -offset + expandableHeader.LayoutSize.Y;
ExpandableHeader.Y = -offset;
FixedHeader.Y = -offset + ExpandableHeader.LayoutSize.Y;
}
Drawable bestMatch = null;
float minDiff = float.MaxValue;
headerBackgroundContainer.Height = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0);
headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0;
foreach (var section in sections)
T bestMatch = null;
float minDiff = float.MaxValue;
float scrollOffset = FixedHeader?.LayoutSize.Y ?? 0;
foreach (var section in Children)
{
float diff = Math.Abs(ScrollContainer.GetChildPosInContent(section) - currentScroll);
float diff = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset);
if (diff < minDiff)
{
minDiff = diff;

View File

@ -0,0 +1,102 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
namespace osu.Game.Graphics.UserInterface
{
public class LineGraph : Container
{
/// <summary>
/// Manually set the max value, otherwise <see cref="Enumerable.Max(IEnumerable{float})"/> will be used.
/// </summary>
public float? MaxValue { get; set; }
/// <summary>
/// Manually set the min value, otherwise <see cref="Enumerable.Min(IEnumerable{float})"/> will be used.
/// </summary>
public float? MinValue { get; set; }
public float ActualMaxValue { get; private set; } = float.NaN;
public float ActualMinValue { get; private set; } = float.NaN;
private const double transform_duration = 1500;
/// <summary>
/// Hold an empty area if values are less.
/// </summary>
public int DefaultValueCount;
private readonly Container<Path> maskingContainer;
private readonly Path path;
private float[] values;
/// <summary>
/// A list of floats decides position of each line node.
/// </summary>
public IEnumerable<float> Values
{
get { return values; }
set
{
values = value.ToArray();
applyPath();
maskingContainer.Width = 0;
maskingContainer.ResizeWidthTo(1, transform_duration, EasingTypes.OutQuint);
}
}
public LineGraph()
{
Add(maskingContainer = new Container<Path>
{
Masking = true,
RelativeSizeAxes = Axes.Both,
Child = path = new Path { RelativeSizeAxes = Axes.Both, PathWidth = 1 }
});
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) != 0)
applyPath();
return base.Invalidate(invalidation, source, shallPropagate);
}
private void applyPath()
{
path.ClearVertices();
if (values == null) return;
int count = Math.Max(values.Length, DefaultValueCount);
float max = values.Max(), min = values.Min();
if (MaxValue > max) max = MaxValue.Value;
if (MinValue < min) min = MinValue.Value;
ActualMaxValue = max;
ActualMinValue = min;
for (int i = 0; i < values.Length; i++)
{
float x = (i + count - values.Length) / (float)(count - 1) * DrawWidth - 1;
float y = GetYPosition(values[i]) * DrawHeight - 1;
// the -1 is for inner offset in path (actually -PathWidth)
path.AddVertex(new Vector2(x, y));
}
}
protected float GetYPosition(float value)
{
if (ActualMaxValue == ActualMinValue) return 0;
return (ActualMaxValue - value) / (ActualMaxValue - ActualMinValue);
}
}
}

View File

@ -23,7 +23,7 @@ namespace osu.Game.Graphics.UserInterface
protected override TabItem<T> CreateTabItem(T value) => new OsuTabItem(value);
private bool isEnumType => typeof(T).IsEnum;
private static bool isEnumType => typeof(T).IsEnum;
public OsuTabControl()
{

View File

@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
@ -23,12 +24,14 @@ namespace osu.Game.Graphics.UserInterface
Height = 30;
}
private class PageTabItem : TabItem<T>
public class PageTabItem : TabItem<T>
{
private const float transition_duration = 100;
private readonly Box box;
protected readonly SpriteText Text;
public PageTabItem(T value) : base(value)
{
AutoSizeAxes = Axes.X;
@ -36,12 +39,12 @@ namespace osu.Game.Graphics.UserInterface
Children = new Drawable[]
{
new OsuSpriteText
Text = new OsuSpriteText
{
Margin = new MarginPadding { Top = 8, Bottom = 8 },
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Text = (value as Enum).GetDescription() ?? value.ToString(),
Text = (value as Enum)?.GetDescription() ?? value.ToString(),
TextSize = 14,
Font = @"Exo2.0-Bold",
},

View File

@ -7,9 +7,9 @@ namespace osu.Game.Online.API.Requests
{
public class GetUserRequest : APIRequest<User>
{
private int? userId;
private long? userId;
public GetUserRequest(int? userId = null)
public GetUserRequest(long? userId = null)
{
this.userId = userId;
}

View File

@ -45,6 +45,8 @@ namespace osu.Game
private SocialOverlay social;
private UserProfileOverlay userProfile;
private Intro intro
{
get
@ -173,6 +175,7 @@ namespace osu.Game
LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
LoadComponentAsync(social = new SocialOverlay { Depth = -1 }, mainContent.Add);
LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add);
LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -1 }, mainContent.Add);
LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add);
LoadComponentAsync(musicController = new MusicController
{
@ -207,6 +210,7 @@ namespace osu.Game
Dependencies.Cache(settings);
Dependencies.Cache(social);
Dependencies.Cache(chat);
Dependencies.Cache(userProfile);
Dependencies.Cache(musicController);
Dependencies.Cache(notificationManager);
Dependencies.Cache(dialogOverlay);
@ -322,6 +326,7 @@ namespace osu.Game
chat.State = Visibility.Hidden;
direct.State = Visibility.Hidden;
social.State = Visibility.Hidden;
userProfile.State = Visibility.Hidden;
}
else
{

View File

@ -46,6 +46,7 @@ namespace osu.Game.Overlays
settingsSection = new LoginSettings
{
Padding = new MarginPadding(10),
RequestHide = Hide,
},
new Box
{

View File

@ -2,15 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Database;
using osu.Game.Graphics;
using OpenTK.Graphics;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Sprites;
using System.Collections.Generic;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Music
{
@ -77,7 +78,7 @@ namespace osu.Game.Overlays.Music
Margin = new MarginPadding { Left = 5 },
Padding = new MarginPadding { Top = 2 },
},
text = new TextFlowContainer
text = new OsuTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,

View File

@ -0,0 +1,483 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using OpenTK.Graphics;
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;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile
{
public class ProfileHeader : Container
{
private readonly OsuTextFlowContainer infoTextLeft, infoTextRight;
private readonly FillFlowContainer<SpriteText> scoreText, scoreNumberText;
private readonly Container coverContainer, chartContainer, supporterTag;
private readonly Sprite levelBadge;
private readonly SpriteText levelText;
private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA;
private readonly Box colourBar;
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;
public ProfileHeader(User user)
{
RelativeSizeAxes = Axes.X;
Height = cover_height + info_height;
Children = new Drawable[]
{
coverContainer = new Container
{
RelativeSizeAxes = Axes.X,
Height = cover_height,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f))
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = -20,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new UpdateableAvatar
{
User = user,
Size = new Vector2(avatar_size),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Masking = true,
CornerRadius = 5,
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 CircularContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Y = -75,
Size = new Vector2(25, 25),
Masking = true,
BorderThickness = 3,
BorderColour = Color4.White,
Alpha = 0,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
},
new TextAwesome
{
Icon = FontAwesome.fa_heart,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 12
}
}
},
new OsuSpriteText
{
Text = user.Username,
TextSize = 30,
Font = @"Exo2.0-RegularItalic",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Y = -48
},
new DrawableFlag(user.Country?.FlagName ?? "__")
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Width = 30,
Height = 20
}
}
}
}
},
colourBar = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = UserProfileOverlay.CONTENT_X_MARGIN,
Height = 5,
Width = info_width,
Alpha = 0
}
}
},
infoTextLeft = new OsuTextFlowContainer(t =>
{
t.TextSize = 14;
t.Alpha = 0.8f;
})
{
X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = cover_height + 20,
Width = info_width,
AutoSizeAxes = Axes.Y,
ParagraphSpacing = 0.8f,
LineSpacing = 0.2f
},
infoTextRight = new OsuTextFlowContainer(t =>
{
t.TextSize = 14;
t.Font = @"Exo2.0-RegularItalic";
})
{
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,
Width = 280,
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,
TextSize = 20
}
}
},
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 },
}
}
}
},
chartContainer = 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
}
}
}
}
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
levelBadge.Texture = textures.Get(@"Profile/levelbadge");
}
private User user;
public User User
{
get
{
return user;
}
set
{
user = value;
loadUser();
}
}
private void loadUser()
{
coverContainer.Add(new AsyncLoadWrapper(new UserCoverBackground(user)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
OnLoadComplete = d => d.FadeInFromZero(200)
})
{
Masking = true,
RelativeSizeAxes = Axes.Both,
Depth = float.MaxValue
});
if (user.IsSupporter) supporterTag.Show();
if (!string.IsNullOrEmpty(user.Colour))
{
colourBar.Colour = OsuColour.FromHex(user.Colour);
colourBar.Show();
}
Action<SpriteText> boldItalic = t =>
{
t.Font = @"Exo2.0-BoldItalic";
t.Alpha = 1;
};
if (user.Age != null)
{
infoTextLeft.AddText($"{user.Age} years old ", boldItalic);
}
if (user.Country != null)
{
infoTextLeft.AddText("from ");
infoTextLeft.AddText(user.Country.FullName, boldItalic);
}
infoTextLeft.NewParagraph();
if (user.JoinDate.ToUniversalTime().Year < 2008)
{
infoTextLeft.AddText("Here since the beginning", boldItalic);
}
else
{
infoTextLeft.AddText("Joined ");
infoTextLeft.AddText(user.JoinDate.LocalDateTime.ToShortDateString(), boldItalic);
}
infoTextLeft.NewLine();
infoTextLeft.AddText("Last seen ");
infoTextLeft.AddText(user.LastVisit.LocalDateTime.ToShortDateString(), boldItalic);
infoTextLeft.NewParagraph();
if (user.PlayStyle?.Length > 0)
{
infoTextLeft.AddText("Plays with ");
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
}
tryAddInfoRightLine(FontAwesome.fa_map_marker, user.Location);
tryAddInfoRightLine(FontAwesome.fa_heart_o, user.Intrerests);
tryAddInfoRightLine(FontAwesome.fa_suitcase, user.Occupation);
infoTextRight.NewParagraph();
if (!string.IsNullOrEmpty(user.Twitter))
tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter);
tryAddInfoRightLine(FontAwesome.fa_globe, user.Website);
tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype);
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}%"));
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("Replay Watched by Others"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplayWatched.ToString(@"#,0")));
gradeSS.DisplayCount = user.Statistics.GradesCount.SS;
gradeSS.Show();
gradeS.DisplayCount = user.Statistics.GradesCount.S;
gradeS.Show();
gradeA.DisplayCount = user.Statistics.GradesCount.A;
gradeA.Show();
gradeSPlus.DisplayCount = 0;
gradeSSPlus.DisplayCount = 0;
chartContainer.Add(new RankChart(user) { RelativeSizeAxes = Axes.Both });
}
}
// These could be local functions when C# 7 enabled
private OsuSpriteText createScoreText(string text) => new OsuSpriteText
{
TextSize = 14,
Text = text
};
private OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Text = text
};
private void tryAddInfoRightLine(FontAwesome icon, string str)
{
if (string.IsNullOrEmpty(str)) return;
infoTextRight.AddIcon(icon);
infoTextRight.AddText(" " + str);
infoTextRight.NewLine();
}
private class GradeBadge : Container
{
private const float width = 50;
private readonly string grade;
private readonly Sprite badge;
private readonly SpriteText numberText;
public int DisplayCount
{
set { numberText.Text = value.ToString(@"#,0"); }
}
public GradeBadge(string grade)
{
this.grade = grade;
Width = width;
Height = 41;
Add(badge = new Sprite
{
Width = width,
Height = 26
});
Add(numberText = new SpriteText
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
TextSize = 14,
Font = @"Exo2.0-Bold"
});
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
badge.Texture = textures.Get($"Grades/{grade}");
}
}
}
}

View File

@ -0,0 +1,74 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Profile
{
public abstract class ProfileSection : FillFlowContainer
{
public abstract string Title { get; }
public abstract string Identifier { get; }
private readonly FillFlowContainer content;
protected override Container<Drawable> Content => content;
protected ProfileSection()
{
Direction = FillDirection.Vertical;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new OsuSpriteText
{
Text = Title,
TextSize = 20,
Font = @"Exo2.0-RegularItalic",
Margin = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Vertical = 10
}
},
content = new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Bottom = 20
}
},
new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = OsuColour.Gray(34),
EdgeSmoothness = new Vector2(1)
}
};
// placeholder
Add(new OsuSpriteText
{
Text = @"coming soon!",
TextSize = 16,
Font = @"Exo2.0-Medium",
Colour = Color4.Gray,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Top = 100, Bottom = 100 }
});
}
}
}

View File

@ -0,0 +1,177 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile
{
public class RankChart : Container
{
private readonly SpriteText rankText, performanceText, relativeText;
private readonly RankChartLineGraph graph;
private readonly int[] ranks;
private const float primary_textsize = 25, secondary_textsize = 13, padding = 10;
private readonly User user;
public RankChart(User user)
{
this.user = user;
Padding = new MarginPadding { Vertical = padding };
Children = new Drawable[]
{
rankText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = @"Exo2.0-RegularItalic",
TextSize = primary_textsize
},
relativeText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = @"Exo2.0-RegularItalic",
Y = 25,
TextSize = secondary_textsize
},
performanceText = new OsuSpriteText
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Font = @"Exo2.0-RegularItalic",
TextSize = secondary_textsize
},
graph = new RankChartLineGraph
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Y = -secondary_textsize,
DefaultValueCount = 90,
BallRelease = updateRankTexts,
BallMove = showHistoryRankTexts
}
};
ranks = user.AllRankHistories?.Osu?.Data ?? new[] { user.Statistics.Rank };
}
private void updateRankTexts()
{
rankText.Text = user.Statistics.Rank > 0 ? $"#{user.Statistics.Rank:#,0}" : "no rank";
performanceText.Text = user.Statistics.PP != null ? $"{user.Statistics.PP:#,0}pp" : string.Empty;
relativeText.Text = $"{user.Country?.FullName} #{user.CountryRank:#,0}";
}
private void showHistoryRankTexts(int dayIndex)
{
rankText.Text = ranks[dayIndex] > 0 ? $"#{ranks[dayIndex]:#,0}" : "no rank";
relativeText.Text = dayIndex == ranks.Length ? "Now" : $"{ranks.Length - dayIndex} days ago";
//plural should be handled in a general way
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
graph.Colour = colours.Yellow;
if (user.Statistics.Rank > 0)
{
// use logarithmic coordinates
graph.Values = ranks.Select(x => -(float)Math.Log(x));
graph.ResetBall();
}
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) != 0)
{
graph.Height = DrawHeight - padding * 2 - primary_textsize - secondary_textsize * 2;
}
return base.Invalidate(invalidation, source, shallPropagate);
}
private class RankChartLineGraph : LineGraph
{
private readonly CircularContainer ball;
private bool ballShown;
private const double transform_duration = 100;
public Action<int> BallMove;
public Action BallRelease;
public RankChartLineGraph()
{
Add(ball = new CircularContainer
{
Size = new Vector2(8),
Masking = true,
Origin = Anchor.Centre,
Alpha = 0,
RelativePositionAxes = Axes.Both,
Children = new Drawable[]
{
new Box { RelativeSizeAxes = Axes.Both }
}
});
}
public void ResetBall()
{
ball.MoveTo(new Vector2(1, GetYPosition(Values.Last())), ballShown ? transform_duration : 0, EasingTypes.OutQuint);
ball.Show();
BallRelease();
ballShown = true;
}
protected override bool OnMouseMove(InputState state)
{
if (ballShown)
{
var values = (IList<float>)Values;
var position = ToLocalSpace(state.Mouse.NativeState.Position);
int count = Math.Max(values.Count, DefaultValueCount);
int index = (int)Math.Round(position.X / DrawWidth * (count - 1));
if (index >= count - values.Count)
{
int i = index + values.Count - count;
float y = GetYPosition(values[i]);
if (Math.Abs(y * DrawHeight - position.Y) <= 8f)
{
ball.MoveTo(new Vector2(index / (float)(count - 1), y), transform_duration, EasingTypes.OutQuint);
BallMove(i);
}
}
}
return base.OnMouseMove(state);
}
protected override void OnHoverLost(InputState state)
{
if (ballShown)
ResetBall();
base.OnHoverLost(state);
}
}
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class AboutSection : ProfileSection
{
public override string Title => "me!";
public override string Identifier => "me";
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class BeatmapsSection : ProfileSection
{
public override string Title => "Beatmaps";
public override string Identifier => "beatmaps";
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class HistoricalSection : ProfileSection
{
public override string Title => "Historical";
public override string Identifier => "historical";
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class KudosuSection : ProfileSection
{
public override string Title => "Kudosu!";
public override string Identifier => "kudosu";
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class MedalsSection : ProfileSection
{
public override string Title => "Medals";
public override string Identifier => "medals";
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class RanksSection : ProfileSection
{
public override string Title => "Ranks";
public override string Identifier => "top_ranks";
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class RecentSection : ProfileSection
{
public override string Title => "Recent";
public override string Identifier => "recent_activities";
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -31,6 +32,11 @@ namespace osu.Game.Overlays.Settings.Sections.General
private UserPanel panel;
private UserDropdown dropdown;
/// <summary>
/// Called to request a hide of a parent displaying this container.
/// </summary>
public Action RequestHide;
public override RectangleF BoundingBox => bounding ? base.BoundingBox : RectangleF.Empty;
public bool Bounding
@ -58,6 +64,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
{
this.inputManager = inputManager;
this.colours = colours;
api?.Register(this);
}
@ -129,7 +136,11 @@ namespace osu.Game.Overlays.Settings.Sections.General
},
},
},
panel = new UserPanel(api.LocalUser.Value) { RelativeSizeAxes = Axes.X },
panel = new UserPanel(api.LocalUser.Value)
{
RelativeSizeAxes = Axes.X,
Action = RequestHide
},
dropdown = new UserDropdown { RelativeSizeAxes = Axes.X },
},
},

View File

@ -86,7 +86,7 @@ namespace osu.Game.Overlays
},
Exit = Hide,
},
Sections = sections,
Children = sections,
Footer = new SettingsFooter()
},
sidebar = new Sidebar
@ -163,13 +163,12 @@ namespace osu.Game.Overlays
sectionsContainer.Padding = new MarginPadding { Top = getToolbarHeight() };
}
private class SettingsSectionsContainer : SectionsContainer
private class SettingsSectionsContainer : SectionsContainer<SettingsSection>
{
public SearchContainer SearchContainer;
private readonly Box headerBackground;
public SearchContainer<SettingsSection> SearchContainer;
protected override Container<Drawable> CreateScrollContentContainer()
=> SearchContainer = new SearchContainer
protected override FlowContainer<SettingsSection> CreateScrollContentContainer()
=> SearchContainer = new SearchContainer<SettingsSection>
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
@ -178,12 +177,11 @@ namespace osu.Game.Overlays
public SettingsSectionsContainer()
{
ScrollContainer.ScrollbarVisible = false;
Add(headerBackground = new Box
HeaderBackground = new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.X
});
RelativeSizeAxes = Axes.Both
};
}
protected override void UpdateAfterChildren()
@ -191,9 +189,7 @@ namespace osu.Game.Overlays
base.UpdateAfterChildren();
// no null check because the usage of this class is strict
headerBackground.Height = ExpandableHeader.LayoutSize.Y + FixedHeader.LayoutSize.Y;
headerBackground.Y = ExpandableHeader.Y;
headerBackground.Alpha = -ExpandableHeader.Y / ExpandableHeader.LayoutSize.Y * 0.5f;
HeaderBackground.Alpha = -ExpandableHeader.Y / ExpandableHeader.LayoutSize.Y * 0.5f;
}
}
}

View File

@ -0,0 +1,224 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile;
using osu.Game.Overlays.Profile.Sections;
using osu.Game.Users;
namespace osu.Game.Overlays
{
public class UserProfileOverlay : WaveOverlayContainer
{
private ProfileSection lastSection;
private ProfileSection[] sections;
private GetUserRequest userReq;
private APIAccess api;
private ProfileHeader header;
private SectionsContainer<ProfileSection> sectionsContainer;
private ProfileTabControl tabs;
public const float CONTENT_X_MARGIN = 50;
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
protected override bool OnClick(InputState state)
{
State = Visibility.Hidden;
return true;
}
public UserProfileOverlay()
{
FirstWaveColour = OsuColour.Gray(0.4f);
SecondWaveColour = OsuColour.Gray(0.3f);
ThirdWaveColour = OsuColour.Gray(0.2f);
FourthWaveColour = OsuColour.Gray(0.1f);
RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both;
Width = 0.85f;
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0),
Type = EdgeEffectType.Shadow,
Radius = 10
};
}
[BackgroundDependencyLoader]
private void load(APIAccess api)
{
this.api = api;
}
protected override void PopIn()
{
base.PopIn();
FadeEdgeEffectTo(0.5f, APPEAR_DURATION, EasingTypes.In);
}
protected override void PopOut()
{
base.PopOut();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, EasingTypes.Out);
}
public void ShowUser(User user, bool fetchOnline = true)
{
userReq?.Cancel();
Clear();
lastSection = null;
sections = new ProfileSection[]
{
new AboutSection(),
new RecentSection(),
new RanksSection(),
new MedalsSection(),
new HistoricalSection(),
new BeatmapsSection(),
new KudosuSection()
};
tabs = new ProfileTabControl
{
RelativeSizeAxes = Axes.X,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Height = 30
};
Add(new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
});
header = new ProfileHeader(user);
Add(sectionsContainer = new SectionsContainer<ProfileSection>
{
RelativeSizeAxes = Axes.Both,
ExpandableHeader = header,
FixedHeader = tabs,
HeaderBackground = new Box
{
Colour = OsuColour.Gray(34),
RelativeSizeAxes = Axes.Both
}
});
sectionsContainer.SelectedSection.ValueChanged += s =>
{
if (lastSection != s)
{
lastSection = s;
tabs.Current.Value = lastSection;
}
};
tabs.Current.ValueChanged += s =>
{
if (lastSection == null)
{
lastSection = sectionsContainer.Children.FirstOrDefault();
if (lastSection != null)
tabs.Current.Value = lastSection;
return;
}
if (lastSection != s)
{
lastSection = s;
sectionsContainer.ScrollTo(lastSection);
}
};
if (fetchOnline)
{
userReq = new GetUserRequest(user.Id);
userReq.Success += userLoadComplete;
api.Queue(userReq);
}
else
{
userReq = null;
userLoadComplete(user);
}
Show();
}
private void userLoadComplete(User user)
{
header.User = user;
for (int i = 0; i < user.ProfileOrder.Length; i++)
{
string id = user.ProfileOrder[i];
var sec = sections.FirstOrDefault(s => s.Identifier == id);
if (sec != null)
{
sec.Depth = -i;
sectionsContainer.Add(sec);
tabs.AddItem(sec);
}
}
}
private class ProfileTabControl : PageTabControl<ProfileSection>
{
private readonly Box bottom;
public ProfileTabControl()
{
TabContainer.RelativeSizeAxes &= ~Axes.X;
TabContainer.AutoSizeAxes |= Axes.X;
TabContainer.Anchor |= Anchor.x1;
TabContainer.Origin |= Anchor.x1;
Add(bottom = new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
EdgeSmoothness = new Vector2(1)
});
}
protected override TabItem<ProfileSection> CreateTabItem(ProfileSection value) => new ProfileTabItem(value);
protected override Dropdown<ProfileSection> CreateDropdown() => null;
private class ProfileTabItem : PageTabItem
{
public ProfileTabItem(ProfileSection value) : base(value)
{
Text.Text = value.Title;
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
bottom.Colour = colours.Yellow;
}
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using Newtonsoft.Json;
using osu.Framework.Configuration;
@ -11,17 +12,20 @@ namespace osu.Game.Users
[JsonProperty(@"id")]
public long Id = 1;
[JsonProperty(@"join_date")]
public DateTimeOffset JoinDate;
[JsonProperty(@"username")]
public string Username;
[JsonProperty(@"country_code")]
public string CountryCode;
[JsonProperty(@"country")]
public Country Country;
public Bindable<UserStatus> Status = new Bindable<UserStatus>();
[JsonProperty(@"age")]
public int? Age;
public int GlobalRank;
public int CountryRank;
@ -51,5 +55,101 @@ namespace osu.Game.Users
[JsonProperty(@"id")]
public int? Id;
}
[JsonProperty(@"isAdmin")]
public bool IsAdmin;
[JsonProperty(@"isSupporter")]
public bool IsSupporter;
[JsonProperty(@"isGMT")]
public bool IsGMT;
[JsonProperty(@"isQAT")]
public bool IsQAT;
[JsonProperty(@"isBNG")]
public bool IsBNG;
[JsonProperty(@"is_active")]
public bool Active;
[JsonProperty(@"interests")]
public string Intrerests;
[JsonProperty(@"occupation")]
public string Occupation;
[JsonProperty(@"title")]
public string Title;
[JsonProperty(@"location")]
public string Location;
[JsonProperty(@"lastvisit")]
public DateTimeOffset LastVisit;
[JsonProperty(@"twitter")]
public string Twitter;
[JsonProperty(@"lastfm")]
public string Lastfm;
[JsonProperty(@"skype")]
public string Skype;
[JsonProperty(@"website")]
public string Website;
[JsonProperty(@"playstyle")]
public string[] PlayStyle;
[JsonProperty(@"playmode")]
public string PlayMode;
[JsonProperty(@"profileOrder")]
public string[] ProfileOrder;
[JsonProperty(@"kudosu")]
public KudosuCount Kudosu;
public class KudosuCount
{
[JsonProperty(@"total")]
public int Total;
[JsonProperty(@"available")]
public int Available;
}
[JsonProperty(@"defaultStatistics")]
public UserStatistics Statistics;
public class RankHistories
{
[JsonProperty(@"osu")]
public RankHistory Osu;
[JsonProperty(@"taiko")]
public RankHistory Taiko;
[JsonProperty(@"fruits")]
public RankHistory Fruits;
[JsonProperty(@"mania")]
public RankHistory Mania;
}
public class RankHistory
{
[JsonProperty(@"mode")]
public string Mode;
[JsonProperty(@"data")]
public int[] Data;
}
[JsonProperty(@"allRankHistories")]
public RankHistories AllRankHistories;
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Users
{
public class UserCoverBackground : Sprite
{
private readonly User user;
public UserCoverBackground(User user)
{
this.user = user;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
if (!string.IsNullOrEmpty(user.CoverUrl))
Texture = textures.Get(user.CoverUrl);
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
@ -9,29 +10,31 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
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.Sprites;
using osu.Game.Overlays;
namespace osu.Game.Users
{
public class UserPanel : Container
public class UserPanel : ClickableContainer
{
private readonly User user;
private const float height = 100;
private const float content_padding = 10;
private const float status_height = 30;
private OsuColour colours;
private readonly Container statusBar;
private readonly Box statusBg;
private readonly OsuSpriteText statusMessage;
public readonly Bindable<UserStatus> Status = new Bindable<UserStatus>();
public new Action Action;
public UserPanel(User user)
{
this.user = user;
Height = height - status_height;
Masking = true;
CornerRadius = 5;
@ -44,7 +47,7 @@ namespace osu.Game.Users
Children = new Drawable[]
{
new AsyncLoadWrapper(new CoverBackgroundSprite(user)
new AsyncLoadWrapper(new UserCoverBackground(user)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@ -162,11 +165,17 @@ namespace osu.Game.Users
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, UserProfileOverlay profile)
{
this.colours = colours;
Status.ValueChanged += displayStatus;
Status.ValueChanged += status => statusBg.FadeColour(status?.GetAppropriateColour(colours) ?? colours.Gray5, 500, EasingTypes.OutQuint);
base.Action = () =>
{
Action?.Invoke();
profile?.ShowUser(user);
};
}
protected override void LoadComplete()
@ -191,26 +200,8 @@ namespace osu.Game.Users
statusBar.FadeIn(transition_duration, EasingTypes.OutQuint);
ResizeHeightTo(height, transition_duration, EasingTypes.OutQuint);
statusBg.FadeColour(status.GetAppropriateColour(colours), 500, EasingTypes.OutQuint);
statusMessage.Text = status.Message;
}
}
private class CoverBackgroundSprite : Sprite
{
private readonly User user;
public CoverBackgroundSprite(User user)
{
this.user = user;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
if (!string.IsNullOrEmpty(user.CoverUrl))
Texture = textures.Get(user.CoverUrl);
}
}
}
}

View File

@ -0,0 +1,64 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
namespace osu.Game.Users
{
public class UserStatistics
{
[JsonProperty(@"level")]
public LevelInfo Level;
public struct LevelInfo
{
[JsonProperty(@"current")]
public int Current;
[JsonProperty(@"progress")]
public int Progress;
}
[JsonProperty(@"pp")]
public decimal? PP;
[JsonProperty(@"pp_rank")]
public int Rank;
[JsonProperty(@"ranked_score")]
public long RankedScore;
[JsonProperty(@"hit_accuracy")]
public decimal Accuracy;
[JsonProperty(@"play_count")]
public int PlayCount;
[JsonProperty(@"total_score")]
public long TotalScore;
[JsonProperty(@"total_hits")]
public int TotalHits;
[JsonProperty(@"maximum_combo")]
public int MaxCombo;
[JsonProperty(@"replays_watched_by_others")]
public int ReplayWatched;
[JsonProperty(@"grade_counts")]
public Grades GradesCount;
public struct Grades
{
[JsonProperty(@"ss")]
public int SS;
[JsonProperty(@"s")]
public int S;
[JsonProperty(@"a")]
public int A;
}
}
}

View File

@ -80,8 +80,10 @@
<Compile Include="Graphics\Containers\OsuFocusedOverlayContainer.cs" />
<Compile Include="Graphics\Containers\OsuScrollContainer.cs" />
<Compile Include="Graphics\Cursor\OsuContextMenuContainer.cs" />
<Compile Include="Graphics\Containers\OsuTextFlowContainer.cs" />
<Compile Include="Graphics\UserInterface\IconButton.cs" />
<Compile Include="Configuration\SelectionRandomType.cs" />
<Compile Include="Graphics\UserInterface\LineGraph.cs" />
<Compile Include="Graphics\UserInterface\MenuItemType.cs" />
<Compile Include="Graphics\UserInterface\OsuContextMenu.cs" />
<Compile Include="Graphics\UserInterface\OsuContextMenuItem.cs" />
@ -98,6 +100,18 @@
<Compile Include="Overlays\Settings\SettingsHeader.cs" />
<Compile Include="Overlays\Settings\Sections\Audio\MainMenuSettings.cs" />
<Compile Include="Overlays\Toolbar\ToolbarChatButton.cs" />
<Compile Include="Overlays\Profile\Sections\AboutSection.cs" />
<Compile Include="Overlays\Profile\Sections\BeatmapsSection.cs" />
<Compile Include="Overlays\Profile\Sections\HistoricalSection.cs" />
<Compile Include="Overlays\Profile\Sections\KudosuSection.cs" />
<Compile Include="Overlays\Profile\Sections\MedalsSection.cs" />
<Compile Include="Overlays\Profile\RankChart.cs" />
<Compile Include="Overlays\Profile\Sections\RanksSection.cs" />
<Compile Include="Overlays\Profile\Sections\RecentSection.cs" />
<Compile Include="Users\UserCoverBackground.cs" />
<Compile Include="Overlays\UserProfileOverlay.cs" />
<Compile Include="Overlays\Profile\ProfileHeader.cs" />
<Compile Include="Overlays\Profile\ProfileSection.cs" />
<Compile Include="Overlays\Toolbar\ToolbarSocialButton.cs" />
<Compile Include="Rulesets\Beatmaps\BeatmapConverter.cs" />
<Compile Include="Rulesets\Beatmaps\BeatmapProcessor.cs" />
@ -466,6 +480,7 @@
<Compile Include="Online\Multiplayer\Room.cs" />
<Compile Include="Online\Multiplayer\RoomStatus.cs" />
<Compile Include="Users\UserPanel.cs" />
<Compile Include="Users\UserStatistics.cs" />
<Compile Include="Users\UserStatus.cs" />
<Compile Include="Overlays\DirectOverlay.cs" />
<Compile Include="Overlays\Direct\FilterControl.cs" />
@ -516,11 +531,6 @@
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -192,6 +192,11 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RNG/@EntryIndexedValue">RNG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SRGB/@EntryIndexedValue">SRGB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TK/@EntryIndexedValue">TK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SS/@EntryIndexedValue">SS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PP/@EntryIndexedValue">PP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GMT/@EntryIndexedValue">GMT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QAT/@EntryIndexedValue">QAT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BNG/@EntryIndexedValue">BNG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=EnumMember/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&#xD;
&lt;Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"&gt;&#xD;