1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 11:23:00 +08:00

Merge branch 'master' into registration-loc

This commit is contained in:
Dean Herbert 2023-01-18 13:41:53 +09:00 committed by GitHub
commit 39ac7615ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 630 additions and 218 deletions

View File

@ -31,10 +31,8 @@ namespace osu.Game.Rulesets.Osu.Tests
[Test]
public void TestMaximumDistanceTrackingWithoutMovement(
[Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)]
float circleSize,
[Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)]
double velocity)
[Values(0, 5, 10)] float circleSize,
[Values(0, 5, 10)] double velocity)
{
const double time_slider_start = 1000;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@ -11,7 +9,10 @@ using osu.Game.Overlays;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
@ -27,15 +28,20 @@ namespace osu.Game.Tests.Visual.Online
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
private CommentsContainer commentsContainer;
private CommentsContainer commentsContainer = null!;
private TextBox editorTextBox = null!;
[SetUp]
public void SetUp() => Schedule(() =>
{
Child = new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = commentsContainer = new CommentsContainer()
});
};
editorTextBox = commentsContainer.ChildrenOfType<TextBox>().First();
});
[Test]
public void TestIdleState()
@ -126,6 +132,44 @@ namespace osu.Game.Tests.Visual.Online
commentsContainer.ChildrenOfType<DrawableComment>().Count(d => d.Comment.Pinned == withPinned) == 1);
}
[Test]
public void TestPost()
{
setUpCommentsResponse(new CommentBundle { Comments = new List<Comment>() });
AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123));
AddAssert("no comments placeholder shown", () => commentsContainer.ChildrenOfType<CommentsContainer.NoCommentsPlaceholder>().Any());
setUpPostResponse();
AddStep("enter text", () => editorTextBox.Current.Value = "comm");
AddStep("submit", () => commentsContainer.ChildrenOfType<RoundedButton>().First().TriggerClick());
AddUntilStep("comment sent", () =>
{
string writtenText = editorTextBox.Current.Value;
var comment = commentsContainer.ChildrenOfType<DrawableComment>().LastOrDefault();
return comment != null && comment.ChildrenOfType<SpriteText>().Any(y => y.Text == writtenText);
});
AddAssert("no comments placeholder removed", () => !commentsContainer.ChildrenOfType<CommentsContainer.NoCommentsPlaceholder>().Any());
}
[Test]
public void TestPostWithExistingComments()
{
setUpCommentsResponse(getExampleComments());
AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123));
setUpPostResponse();
AddStep("enter text", () => editorTextBox.Current.Value = "comm");
AddStep("submit", () => commentsContainer.ChildrenOfType<CommentEditor>().Single().ChildrenOfType<RoundedButton>().First().TriggerClick());
AddUntilStep("comment sent", () =>
{
string writtenText = editorTextBox.Current.Value;
var comment = commentsContainer.ChildrenOfType<DrawableComment>().LastOrDefault();
return comment != null && comment.ChildrenOfType<SpriteText>().Any(y => y.Text == writtenText);
});
}
private void setUpCommentsResponse(CommentBundle commentBundle)
=> AddStep("set up response", () =>
{
@ -139,7 +183,33 @@ namespace osu.Game.Tests.Visual.Online
};
});
private CommentBundle getExampleComments(bool withPinned = false)
private void setUpPostResponse()
=> AddStep("set up response", () =>
{
dummyAPI.HandleRequest = request =>
{
if (!(request is CommentPostRequest req))
return false;
req.TriggerSuccess(new CommentBundle
{
Comments = new List<Comment>
{
new Comment
{
Id = 98,
Message = req.Message,
LegacyName = "FirstUser",
CreatedAt = DateTimeOffset.Now,
VotesCount = 98,
}
}
});
return true;
};
});
private static CommentBundle getExampleComments(bool withPinned = false)
{
var bundle = new CommentBundle
{

View File

@ -113,7 +113,7 @@ namespace osu.Game.Tournament.Screens.Setup
new LabelledDropdown<RulesetInfo>
{
Label = "Ruleset",
Description = "Decides what stats are displayed and which ranks are retrieved for players.",
Description = "Decides what stats are displayed and which ranks are retrieved for players. This requires a restart to reload data for an existing bracket.",
Items = rulesets.AvailableRulesets,
Current = LadderInfo.Ruleset,
},

View File

@ -16,6 +16,7 @@ using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Graphics;
using osu.Game.Online;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Tournament.IO;
@ -44,6 +45,14 @@ namespace osu.Game.Tournament
return dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
}
public override EndpointConfiguration CreateEndpoints()
{
if (UseDevelopmentServer)
return base.CreateEndpoints();
return new ProductionEndpointConfiguration();
}
private TournamentSpriteText initialisationText;
[BackgroundDependencyLoader]
@ -156,9 +165,21 @@ namespace osu.Game.Tournament
addedInfo |= addSeedingBeatmaps();
if (addedInfo)
SaveChanges();
saveChanges();
ladder.CurrentMatch.Value = ladder.Matches.FirstOrDefault(p => p.Current.Value);
ladder.Ruleset.BindValueChanged(r =>
{
// Refetch player rank data on next startup as the ruleset has changed.
foreach (var team in ladder.Teams)
{
foreach (var player in team.Players)
player.Rank = null;
}
SaveChanges();
});
}
catch (Exception e)
{
@ -306,6 +327,11 @@ namespace osu.Game.Tournament
return;
}
saveChanges();
}
private void saveChanges()
{
foreach (var r in ladder.Rounds)
r.Matches = ladder.Matches.Where(p => p.Round.Value == r).Select(p => p.ID).ToList();

View File

@ -19,6 +19,11 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"join the real-time discussion");
/// <summary>
/// "Mention"
/// </summary>
public static LocalisableString MentionUser => new TranslatableString(getKey(@"mention_user"), @"Mention");
private static string getKey(string key) => $"{prefix}:{key}";
}
}

View File

@ -0,0 +1,41 @@
// 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.
using System.Net.Http;
using osu.Framework.IO.Network;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class CommentPostRequest : APIRequest<CommentBundle>
{
public readonly CommentableType Commentable;
public readonly long CommentableId;
public readonly string Message;
public readonly long? ParentCommentId;
public CommentPostRequest(CommentableType commentable, long commentableId, string message, long? parentCommentId = null)
{
Commentable = commentable;
CommentableId = commentableId;
Message = message;
ParentCommentId = parentCommentId;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.Method = HttpMethod.Post;
req.AddParameter(@"comment[commentable_type]", Commentable.ToString().ToLowerInvariant());
req.AddParameter(@"comment[commentable_id]", $"{CommentableId}");
req.AddParameter(@"comment[message]", Message);
if (ParentCommentId.HasValue)
req.AddParameter(@"comment[parent_id]", $"{ParentCommentId}");
return req;
}
protected override string Target => "comments";
}
}

View File

@ -25,6 +25,7 @@ namespace osu.Game.Online.Chat
/// </summary>
public partial class StandAloneChatDisplay : CompositeDrawable
{
[Cached]
public readonly Bindable<Channel> Channel = new Bindable<Channel>();
protected readonly ChatTextBox TextBox;

View File

@ -0,0 +1,19 @@
// 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.
namespace osu.Game.Online
{
public class ExperimentalEndpointConfiguration : EndpointConfiguration
{
public ExperimentalEndpointConfiguration()
{
WebsiteRootUrl = @"https://osu.ppy.sh";
APIEndpointUrl = @"https://lazer.ppy.sh";
APIClientSecret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk";
APIClientID = "5";
SpectatorEndpointUrl = "https://spectator.ppy.sh/spectator";
MultiplayerEndpointUrl = "https://spectator.ppy.sh/multiplayer";
MetadataEndpointUrl = "https://spectator.ppy.sh/metadata";
}
}
}

View File

@ -1,16 +1,13 @@
// 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.
#nullable disable
namespace osu.Game.Online
{
public class ProductionEndpointConfiguration : EndpointConfiguration
{
public ProductionEndpointConfiguration()
{
WebsiteRootUrl = @"https://osu.ppy.sh";
APIEndpointUrl = @"https://lazer.ppy.sh";
WebsiteRootUrl = APIEndpointUrl = @"https://osu.ppy.sh";
APIClientSecret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk";
APIClientID = "5";
SpectatorEndpointUrl = "https://spectator.ppy.sh/spectator";

View File

@ -98,8 +98,8 @@ namespace osu.Game
public virtual bool UseDevelopmentServer => DebugUtils.IsDebugBuild;
internal EndpointConfiguration CreateEndpoints() =>
UseDevelopmentServer ? new DevelopmentEndpointConfiguration() : new ProductionEndpointConfiguration();
public virtual EndpointConfiguration CreateEndpoints() =>
UseDevelopmentServer ? new DevelopmentEndpointConfiguration() : new ExperimentalEndpointConfiguration();
public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version();

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -24,6 +25,7 @@ using osu.Game.Online.Chat;
using osu.Game.Resources.Localisation.Web;
using osuTK;
using osuTK.Graphics;
using ChatStrings = osu.Game.Localisation.ChatStrings;
namespace osu.Game.Overlays.Chat
{
@ -65,6 +67,9 @@ namespace osu.Game.Overlays.Chat
[Resolved(canBeNull: true)]
private UserProfileOverlay? profileOverlay { get; set; }
[Resolved]
private Bindable<Channel?>? currentChannel { get; set; }
private readonly APIUser user;
private readonly OsuSpriteText drawableText;
@ -156,6 +161,14 @@ namespace osu.Game.Overlays.Chat
if (!user.Equals(api.LocalUser.Value))
items.Add(new OsuMenuItem(UsersStrings.CardSendMessage, MenuItemType.Standard, openUserChannel));
if (currentChannel?.Value != null)
{
items.Add(new OsuMenuItem(ChatStrings.MentionUser, MenuItemType.Standard, () =>
{
currentChannel.Value.TextBoxMessage.Value += $"@{user.Username} ";
}));
}
return items.ToArray();
}
}

View File

@ -3,6 +3,7 @@
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API;
@ -17,16 +18,22 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Threading;
using System.Collections.Generic;
using JetBrains.Annotations;
using osu.Framework.Localisation;
using osu.Framework.Logging;
using osu.Game.Graphics.Sprites;
using osu.Game.Resources.Localisation.Web;
using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
using osu.Game.Users.Drawables;
using osuTK;
namespace osu.Game.Overlays.Comments
{
[Cached]
public partial class CommentsContainer : CompositeDrawable
{
private readonly Bindable<CommentableType> type = new Bindable<CommentableType>();
private readonly BindableLong id = new BindableLong();
public IBindable<CommentableType> Type => type;
public IBindable<long> Id => id;
public readonly Bindable<CommentsSortCriteria> Sort = new Bindable<CommentsSortCriteria>();
public readonly BindableBool ShowDeleted = new BindableBool();
@ -46,12 +53,14 @@ namespace osu.Game.Overlays.Comments
private DeletedCommentsCounter deletedCommentsCounter;
private CommentsShowMoreButton moreButton;
private TotalCommentsCounter commentCounter;
private UpdateableAvatar avatar;
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddRangeInternal(new Drawable[]
{
new Box
@ -86,6 +95,32 @@ namespace osu.Game.Overlays.Comments
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 50, Vertical = 20 },
Children = new Drawable[]
{
avatar = new UpdateableAvatar(api.LocalUser.Value)
{
Size = new Vector2(50),
CornerExponent = 2,
CornerRadius = 25,
Masking = true,
},
new Container
{
Padding = new MarginPadding { Left = 60 },
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = new NewCommentEditor
{
OnPost = prependPostedComments
}
}
}
},
new CommentsHeader
{
Sort = { BindTarget = Sort },
@ -151,6 +186,7 @@ namespace osu.Game.Overlays.Comments
protected override void LoadComplete()
{
User.BindValueChanged(_ => refetchComments());
User.BindValueChanged(e => avatar.User = e.NewValue);
Sort.BindValueChanged(_ => refetchComments(), true);
base.LoadComplete();
}
@ -245,7 +281,6 @@ namespace osu.Game.Overlays.Comments
{
pinnedContent.AddRange(loaded.Where(d => d.Comment.Pinned));
content.AddRange(loaded.Where(d => !d.Comment.Pinned));
deletedCommentsCounter.Count.Value += topLevelComments.Select(d => d.Comment).Count(c => c.IsDeleted && c.IsTopLevel);
if (bundle.HasMore)
@ -288,6 +323,34 @@ namespace osu.Game.Overlays.Comments
}
}
private void prependPostedComments(CommentBundle bundle)
{
var topLevelComments = new List<DrawableComment>();
foreach (var comment in bundle.Comments)
{
// Exclude possible duplicated comments.
if (CommentDictionary.ContainsKey(comment.Id))
continue;
topLevelComments.Add(getDrawableComment(comment));
}
if (topLevelComments.Any())
{
LoadComponentsAsync(topLevelComments, loaded =>
{
if (content.Count > 0 && content[0] is NoCommentsPlaceholder placeholder)
content.Remove(placeholder, true);
foreach (var comment in loaded)
{
content.Insert((int)-Clock.CurrentTime, comment);
}
}, (loadCancellation = new CancellationTokenSource()).Token);
}
}
private DrawableComment getDrawableComment(Comment comment)
{
if (CommentDictionary.TryGetValue(comment.Id, out var existing))
@ -317,7 +380,7 @@ namespace osu.Game.Overlays.Comments
base.Dispose(isDisposing);
}
private partial class NoCommentsPlaceholder : CompositeDrawable
internal partial class NoCommentsPlaceholder : CompositeDrawable
{
[BackgroundDependencyLoader]
private void load()
@ -336,5 +399,41 @@ namespace osu.Game.Overlays.Comments
});
}
}
private partial class NewCommentEditor : CommentEditor
{
[Resolved]
private CommentsContainer commentsContainer { get; set; }
[Resolved]
private IAPIProvider api { get; set; }
public Action<CommentBundle> OnPost;
//TODO should match web, left empty due to no multiline support
protected override LocalisableString FooterText => default;
protected override LocalisableString CommitButtonText => CommonStrings.ButtonsPost;
protected override LocalisableString TextBoxPlaceholder => CommentsStrings.PlaceholderNew;
protected override void OnCommit(string text)
{
ShowLoadingSpinner = true;
CommentPostRequest req = new CommentPostRequest(commentsContainer.Type.Value, commentsContainer.Id.Value, text);
req.Failure += e => Schedule(() =>
{
ShowLoadingSpinner = false;
Logger.Error(e, "Posting comment failed.");
});
req.Success += cb => Schedule(() =>
{
ShowLoadingSpinner = false;
Current.Value = string.Empty;
OnPost?.Invoke(cb);
});
api.Queue(req);
}
}
}
}

View File

@ -57,6 +57,11 @@ namespace osu.Game.Overlays.Comments
/// </summary>
public bool WasDeleted { get; protected set; }
/// <summary>
/// Tracks this comment's level of nesting. 0 means that this comment has no parents.
/// </summary>
public int Level { get; private set; }
private FillFlowContainer childCommentsVisibilityContainer = null!;
private FillFlowContainer childCommentsContainer = null!;
private LoadRepliesButton loadRepliesButton = null!;
@ -70,7 +75,7 @@ namespace osu.Game.Overlays.Comments
private GridContainer content = null!;
private VotePill votePill = null!;
[Resolved(canBeNull: true)]
[Resolved]
private IDialogOverlay? dialogOverlay { get; set; }
[Resolved]
@ -79,7 +84,7 @@ namespace osu.Game.Overlays.Comments
[Resolved]
private GameHost host { get; set; } = null!;
[Resolved(canBeNull: true)]
[Resolved]
private OnScreenDisplay? onScreenDisplay { get; set; }
public DrawableComment(Comment comment)
@ -88,12 +93,16 @@ namespace osu.Game.Overlays.Comments
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
private void load(OverlayColourProvider colourProvider, DrawableComment? parentComment)
{
LinkFlowContainer username;
FillFlowContainer info;
CommentMarkdownContainer message;
Level = parentComment?.Level + 1 ?? 0;
float childrenPadding = Level < 6 ? 20 : 5;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
@ -248,7 +257,7 @@ namespace osu.Game.Overlays.Comments
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Left = 20 },
Padding = new MarginPadding { Left = childrenPadding },
Children = new Drawable[]
{
childCommentsContainer = new FillFlowContainer

View File

@ -0,0 +1,110 @@
// 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.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Overlays.Profile.Header.Components
{
public partial class ExtendedDetails : CompositeDrawable
{
public Bindable<UserProfileData?> User { get; } = new Bindable<UserProfileData?>();
private SpriteText rankedScore = null!;
private SpriteText hitAccuracy = null!;
private SpriteText playCount = null!;
private SpriteText totalScore = null!;
private SpriteText totalHits = null!;
private SpriteText maximumCombo = null!;
private SpriteText replaysWatched = null!;
[BackgroundDependencyLoader]
private void load()
{
var font = OsuFont.Default.With(size: 12);
const float vertical_spacing = 4;
AutoSizeAxes = Axes.Both;
// this should really be a grid, but trying to avoid one to avoid the performance hit.
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(20, 0),
Children = new[]
{
new FillFlowContainer
{
Name = @"Labels",
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, vertical_spacing),
Children = new Drawable[]
{
new OsuSpriteText { Font = font, Text = UsersStrings.ShowStatsRankedScore },
new OsuSpriteText { Font = font, Text = UsersStrings.ShowStatsHitAccuracy },
new OsuSpriteText { Font = font, Text = UsersStrings.ShowStatsPlayCount },
new OsuSpriteText { Font = font, Text = UsersStrings.ShowStatsTotalScore },
new OsuSpriteText { Font = font, Text = UsersStrings.ShowStatsTotalHits },
new OsuSpriteText { Font = font, Text = UsersStrings.ShowStatsMaximumCombo },
new OsuSpriteText { Font = font, Text = UsersStrings.ShowStatsReplaysWatchedByOthers },
}
},
new FillFlowContainer
{
Name = @"Values",
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, vertical_spacing),
Children = new Drawable[]
{
rankedScore = new OsuSpriteText { Font = font },
hitAccuracy = new OsuSpriteText { Font = font },
playCount = new OsuSpriteText { Font = font },
totalScore = new OsuSpriteText { Font = font },
totalHits = new OsuSpriteText { Font = font },
maximumCombo = new OsuSpriteText { Font = font },
replaysWatched = new OsuSpriteText { Font = font },
}
},
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
User.BindValueChanged(user => updateStatistics(user.NewValue?.User.Statistics), true);
}
private void updateStatistics(UserStatistics? statistics)
{
if (statistics == null)
{
Alpha = 0;
return;
}
Alpha = 1;
rankedScore.Text = statistics.RankedScore.ToLocalisableString(@"N0");
hitAccuracy.Text = statistics.DisplayAccuracy;
playCount.Text = statistics.PlayCount.ToLocalisableString(@"N0");
totalScore.Text = statistics.TotalScore.ToLocalisableString(@"N0");
totalHits.Text = statistics.TotalHits.ToLocalisableString(@"N0");
maximumCombo.Text = statistics.MaxCombo.ToLocalisableString(@"N0");
replaysWatched.Text = statistics.ReplaysWatched.ToLocalisableString(@"N0");
}
}
}

View File

@ -0,0 +1,186 @@
// 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.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Overlays.Profile.Header.Components
{
public partial class MainDetails : CompositeDrawable
{
private readonly Dictionary<ScoreRank, ScoreRankInfo> scoreRankInfos = new Dictionary<ScoreRank, ScoreRankInfo>();
private ProfileValueDisplay medalInfo = null!;
private ProfileValueDisplay ppInfo = null!;
private ProfileValueDisplay detailGlobalRank = null!;
private ProfileValueDisplay detailCountryRank = null!;
private RankGraph rankGraph = null!;
public readonly Bindable<UserProfileData?> User = new Bindable<UserProfileData?>();
[BackgroundDependencyLoader]
private void load()
{
AutoSizeAxes = Axes.Y;
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
AutoSizeDuration = 200,
AutoSizeEasing = Easing.OutQuint,
Masking = true,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 15),
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(20),
Children = new Drawable[]
{
detailGlobalRank = new ProfileValueDisplay(true)
{
Title = UsersStrings.ShowRankGlobalSimple,
},
detailCountryRank = new ProfileValueDisplay(true)
{
Title = UsersStrings.ShowRankCountrySimple,
},
}
},
new Container
{
RelativeSizeAxes = Axes.X,
Height = 60,
Children = new Drawable[]
{
rankGraph = new RankGraph
{
RelativeSizeAxes = Axes.Both,
},
}
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
medalInfo = new ProfileValueDisplay
{
Title = UsersStrings.ShowStatsMedals,
},
ppInfo = new ProfileValueDisplay
{
Title = "pp",
},
new TotalPlayTime
{
User = { BindTarget = User }
},
}
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Children = new[]
{
scoreRankInfos[ScoreRank.XH] = new ScoreRankInfo(ScoreRank.XH),
scoreRankInfos[ScoreRank.X] = new ScoreRankInfo(ScoreRank.X),
scoreRankInfos[ScoreRank.SH] = new ScoreRankInfo(ScoreRank.SH),
scoreRankInfos[ScoreRank.S] = new ScoreRankInfo(ScoreRank.S),
scoreRankInfos[ScoreRank.A] = new ScoreRankInfo(ScoreRank.A),
}
}
}
},
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
User.BindValueChanged(e => updateDisplay(e.NewValue), true);
}
private void updateDisplay(UserProfileData? data)
{
var user = data?.User;
medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0";
ppInfo.Content = user?.Statistics?.PP?.ToLocalisableString("#,##0") ?? (LocalisableString)"0";
foreach (var scoreRankInfo in scoreRankInfos)
scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0;
detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";
detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";
rankGraph.Statistics.Value = user?.Statistics;
}
private partial class ScoreRankInfo : CompositeDrawable
{
private readonly OsuSpriteText rankCount;
public int RankCount
{
set => rankCount.Text = value.ToLocalisableString("#,##0");
}
public ScoreRankInfo(ScoreRank rank)
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
Width = 44,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new DrawableRank(rank)
{
RelativeSizeAxes = Axes.X,
Height = 22,
},
rankCount = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
}
};
}
}
}
}

View File

@ -1,33 +1,17 @@
// 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.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Overlays.Profile.Header
{
public partial class DetailHeaderContainer : CompositeDrawable
{
private readonly Dictionary<ScoreRank, ScoreRankInfo> scoreRankInfos = new Dictionary<ScoreRank, ScoreRankInfo>();
private ProfileValueDisplay medalInfo = null!;
private ProfileValueDisplay ppInfo = null!;
private ProfileValueDisplay detailGlobalRank = null!;
private ProfileValueDisplay detailCountryRank = null!;
private RankGraph rankGraph = null!;
public readonly Bindable<UserProfileData?> User = new Bindable<UserProfileData?>();
[BackgroundDependencyLoader]
@ -35,8 +19,6 @@ namespace osu.Game.Overlays.Profile.Header
{
AutoSizeAxes = Axes.Y;
User.ValueChanged += e => updateDisplay(e.NewValue);
InternalChildren = new Drawable[]
{
new Box
@ -44,149 +26,52 @@ namespace osu.Game.Overlays.Profile.Header
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background5,
},
new FillFlowContainer
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
AutoSizeDuration = 200,
AutoSizeEasing = Easing.OutQuint,
Masking = true,
Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 },
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 15),
Children = new Drawable[]
Child = new GridContainer
{
new FillFlowContainer
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
RowDimensions = new[]
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(20),
Children = new Drawable[]
{
detailGlobalRank = new ProfileValueDisplay(true)
{
Title = UsersStrings.ShowRankGlobalSimple,
},
detailCountryRank = new ProfileValueDisplay(true)
{
Title = UsersStrings.ShowRankCountrySimple,
},
}
new Dimension(GridSizeMode.AutoSize),
},
new Container
ColumnDimensions = new[]
{
RelativeSizeAxes = Axes.X,
Height = 60,
Children = new Drawable[]
{
rankGraph = new RankGraph
{
RelativeSizeAxes = Axes.Both,
},
}
new Dimension(),
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.AutoSize),
},
new Container
Content = new[]
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
new Drawable[]
{
new FillFlowContainer
new MainDetails
{
RelativeSizeAxes = Axes.X,
User = { BindTarget = User }
},
new Box
{
RelativeSizeAxes = Axes.Y,
Width = 2,
Colour = colourProvider.Background6,
Margin = new MarginPadding { Horizontal = 15 }
},
new ExtendedDetails
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
medalInfo = new ProfileValueDisplay
{
Title = UsersStrings.ShowStatsMedals,
},
ppInfo = new ProfileValueDisplay
{
Title = "pp",
},
new TotalPlayTime
{
User = { BindTarget = User }
},
}
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Children = new[]
{
scoreRankInfos[ScoreRank.XH] = new ScoreRankInfo(ScoreRank.XH),
scoreRankInfos[ScoreRank.X] = new ScoreRankInfo(ScoreRank.X),
scoreRankInfos[ScoreRank.SH] = new ScoreRankInfo(ScoreRank.SH),
scoreRankInfos[ScoreRank.S] = new ScoreRankInfo(ScoreRank.S),
scoreRankInfos[ScoreRank.A] = new ScoreRankInfo(ScoreRank.A),
}
User = { BindTarget = User }
}
}
},
}
},
};
}
private void updateDisplay(UserProfileData? data)
{
var user = data?.User;
medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0";
ppInfo.Content = user?.Statistics?.PP?.ToLocalisableString("#,##0") ?? (LocalisableString)"0";
foreach (var scoreRankInfo in scoreRankInfos)
scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0;
detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";
detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";
rankGraph.Statistics.Value = user?.Statistics;
}
private partial class ScoreRankInfo : CompositeDrawable
{
private readonly OsuSpriteText rankCount;
public int RankCount
{
set => rankCount.Text = value.ToLocalisableString("#,##0");
}
public ScoreRankInfo(ScoreRank rank)
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
Width = 44,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new DrawableRank(rank)
{
RelativeSizeAxes = Axes.X,
Height = 22,
},
rankCount = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
}
};
}
}
};
}
}
}

View File

@ -5,18 +5,15 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Users.Drawables;
using osuTK;
@ -38,7 +35,6 @@ namespace osu.Game.Overlays.Profile.Header
private OsuSpriteText titleText = null!;
private UpdateableFlag userFlag = null!;
private OsuSpriteText userCountryText = null!;
private FillFlowContainer userStats = null!;
private GroupBadgeFlow groupBadgeFlow = null!;
[BackgroundDependencyLoader]
@ -164,16 +160,6 @@ namespace osu.Game.Overlays.Profile.Header
}
}
},
userStats = new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Y,
Width = 300,
Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN },
Padding = new MarginPadding { Vertical = 15 },
Spacing = new Vector2(0, 2)
}
};
User.BindValueChanged(user => updateUser(user.NewValue));
@ -192,43 +178,6 @@ namespace osu.Game.Overlays.Profile.Header
titleText.Text = user?.Title ?? string.Empty;
titleText.Colour = Color4Extensions.FromHex(user?.Colour ?? "fff");
groupBadgeFlow.User.Value = user;
userStats.Clear();
if (user?.Statistics != null)
{
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsRankedScore, user.Statistics.RankedScore.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsHitAccuracy, user.Statistics.DisplayAccuracy));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsPlayCount, user.Statistics.PlayCount.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalScore, user.Statistics.TotalScore.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalHits, user.Statistics.TotalHits.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsMaximumCombo, user.Statistics.MaxCombo.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsReplaysWatchedByOthers, user.Statistics.ReplaysWatched.ToLocalisableString("#,##0")));
}
}
private partial class UserStatsLine : Container
{
public UserStatsLine(LocalisableString left, LocalisableString right)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 15),
Text = left,
},
new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold),
Text = right,
},
};
}
}
}
}

View File

@ -596,6 +596,9 @@ namespace osu.Game.Screens.Select
public void FlushPendingFilterOperations()
{
if (!IsLoaded)
return;
if (PendingFilter?.Completed == false)
{
applyActiveCriteria(false);

View File

@ -863,6 +863,7 @@ namespace osu.Game.Screens.Select
// if we have a pending filter operation, we want to run it now.
// it could change selection (ie. if the ruleset has been changed).
Carousel.FlushPendingFilterOperations();
return true;
}